Last active
May 28, 2024 04:42
-
-
Save jgaskins/2ea2657ec9ffacb90cdc09e31c6342da to your computer and use it in GitHub Desktop.
Comparing Crystal and Rust with HTTP, JSON, and Redis
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[package] | |
name = "post-example" | |
version = "0.1.0" | |
edition = "2021" | |
[dependencies] | |
rocket = { version = "*", features = ["json"] } | |
uuid = { version = "0.8", features = ["v4", "serde"] } | |
chrono = "0.4" | |
redis = "*" | |
rocket_db_pools = { version = "*", features = [ "deadpool_redis" ] } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "armature" | |
require "json" | |
require "uuid/json" | |
require "redis" | |
REDIS = Redis::Client.new(URI.parse("redis:///?max_idle_pool_size=100")) | |
class App | |
include Armature::Route | |
include HTTP::Handler | |
def call(context) | |
route context do |r, response| | |
r.post "send-money" do | |
request = if body = r.body | |
SendMoneyRequest.from_json(body.gets_to_end) | |
else | |
response.status = :bad_request | |
return | |
end | |
REDIS.set "transaction:#{request.from.id}:#{request.to.id}", request.amount.to_s | |
Receipt.new( | |
from_account: request.from.name, | |
to_account: request.to.name, | |
amount: request.amount, | |
created_on: Time.utc, | |
to_address: request.to.address.to_s, | |
).to_json response | |
end | |
r.get "hi" do | |
response << "hi" | |
end | |
end | |
end | |
end | |
abstract struct Resource | |
include JSON::Serializable | |
macro field(field) | |
@[JSON::Field(key: {{field.var.id.camelcase(lower: true)}})] | |
getter {{field}} | |
end | |
end | |
struct AccountHolder < Resource | |
field id : UUID | |
field first_name : String | |
field last_name : String | |
field address : Address | |
field email : String | |
def name | |
"#{first_name} #{last_name}" | |
end | |
end | |
struct Address < Resource | |
field street : String | |
field city : String | |
field state : State | |
field zip : String | |
enum State | |
CA | |
NY | |
WA | |
TX | |
FL | |
IL | |
PA | |
OH | |
GA | |
MI | |
NC | |
NJ | |
VA | |
end | |
def to_s(io) : Nil | |
io << street << ", " << city << ", " << state << ", " << zip | |
end | |
end | |
struct SendMoneyRequest < Resource | |
field from : AccountHolder | |
field to : AccountHolder | |
field amount : Float64 | |
field send_on : String | |
end | |
struct Receipt < Resource | |
field from_account : String | |
field to_account : String | |
field amount : Float64 | |
field created_on : Time | |
field to_address : String | |
def initialize(*, @from_account, @to_account, @amount, @created_on, @to_address) | |
end | |
end | |
http = HTTP::Server.new([ | |
App.new, | |
]) | |
port = ENV.fetch("PORT", "8001").to_i | |
log = Log.for("post-example", level: :notice) | |
log.notice { "Listening on #{port}" } | |
http.listen port |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[macro_use] | |
extern crate rocket; | |
use chrono; | |
use rocket_db_pools::{Connection, Database, deadpool_redis}; | |
use rocket::serde::{json::Json, Deserialize, Serialize}; | |
use rocket::{launch, get}; | |
use std::fmt; | |
use uuid::Uuid; | |
#[derive(Deserialize)] | |
#[serde(crate = "rocket::serde")] | |
struct AccountHolder { | |
id: Uuid, | |
firstName: String, | |
lastName: String, | |
address: Address, | |
email: String, | |
} | |
#[derive(Deserialize)] | |
#[serde(crate = "rocket::serde")] | |
struct Address { | |
street: String, | |
city: String, | |
state: State, | |
zip: String, | |
} | |
#[derive(Deserialize)] | |
#[serde(crate = "rocket::serde")] | |
struct SendMoneyRequest { | |
from: AccountHolder, | |
to: AccountHolder, | |
amount: f64, | |
sendOn: String, // DateTime is not directly serializable in serde, use String instead | |
} | |
#[derive(Deserialize, Serialize)] | |
#[serde(crate = "rocket::serde")] | |
struct Receipt { | |
from_account: String, | |
to_account: String, | |
amount: f64, | |
created_on: String, // DateTime is not directly serializable in serde, use String instead | |
to_address: String, | |
} | |
#[derive(Serialize, Deserialize)] | |
#[serde(crate = "rocket::serde")] | |
enum State { | |
CA, | |
NY, | |
WA, | |
TX, | |
FL, | |
IL, | |
PA, | |
OH, | |
GA, | |
MI, | |
NC, | |
NJ, | |
VA, | |
} | |
impl fmt::Display for State { | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
match *self { | |
State::CA => write!(f, "CA"), | |
State::NY => write!(f, "NY"), | |
State::WA => write!(f, "WA"), | |
State::TX => write!(f, "TX"), | |
State::FL => write!(f, "FL"), | |
State::IL => write!(f, "IL"), | |
State::PA => write!(f, "PA"), | |
State::OH => write!(f, "OH"), | |
State::GA => write!(f, "GA"), | |
State::MI => write!(f, "MI"), | |
State::NC => write!(f, "NC"), | |
State::NJ => write!(f, "NJ"), | |
State::VA => write!(f, "VA"), | |
} | |
} | |
} | |
#[derive(Database)] | |
#[database("redis_pool")] | |
pub struct RedisPool(deadpool_redis::Pool); | |
#[get("/hi")] | |
fn index() -> &'static str { | |
"hi" | |
} | |
#[post("/send-money", data = "<request>")] | |
async fn send_money(request: Json<SendMoneyRequest>, mut redis: Connection<RedisPool>) -> Json<Receipt> { | |
let receipt = Receipt { | |
from_account: format!("{} {}", request.from.firstName, request.from.lastName), | |
to_account: format!("{} {}", request.to.firstName, request.to.lastName), | |
amount: request.amount, | |
created_on: chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(), | |
to_address: format!( | |
"{}, {}, {}, {}", | |
request.to.address.street, | |
request.to.address.city, | |
request.to.address.state, | |
request.to.address.zip | |
), | |
}; | |
let _ = redis.send_packed_command( | |
redis::cmd("SET") | |
.arg(format!("transaction:{}:{}", request.from.id, request.to.id)) | |
.arg(request.amount) | |
).await; | |
Json(receipt) | |
} | |
#[launch] | |
fn rocket() -> _ { | |
rocket::build() | |
.mount("/", routes![index, send_money]) | |
.attach(RedisPool::init()) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: post-example | |
version: 0.1.0 | |
dependencies: | |
armature: | |
github: jgaskins/armature | |
crystal: '>= 1.12.1' | |
license: MIT |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment