Skip to content

Instantly share code, notes, and snippets.

@sayanarijit
Last active June 16, 2020 15:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sayanarijit/658cae46e8dea26647ffee47f30c7e3f to your computer and use it in GitHub Desktop.
Save sayanarijit/658cae46e8dea26647ffee47f30c7e3f to your computer and use it in GitHub Desktop.
Rocket in a gist
[package]
name = "rocker_app"
version = "0.1.0"
authors = ["Arijit Basu <sayanarijit@gmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rocket = "0.4.5"
serde = { version = "1.0", features = ["derive"] }
postgres = { version = "0.15", features = ["with-chrono", "with-uuid"] }
[dependencies.rocket_contrib]
version = "0.4.5"
default-features = false
features = ["json", "serve", "diesel_sqlite_pool", "diesel_postgres_pool", "redis_pool"]
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use] extern crate rocket;
#[macro_use] extern crate rocket_contrib;
use serde::{Serialize, Deserialize}; // Helps with serializing and deserializing data
use rocket::Request; // The HTTP request
use rocket::uri; // Robust URL formatter
use rocket::http::RawStr; // Represents unvalidated data from URL params / POST data
use rocket::request::{Form, FromFormValue}; // Encodes/Decodes the RawStr to validated data structures
// use rocket::response::content; // Sets the Content-Type header
use rocket::response::status; // Sets the Status header
use rocket::fairing::AdHoc; // Allows attaching ad-hoc fairings
use rocket_contrib::databases::diesel; // ORM
use rocket_contrib::serve::StaticFiles; // Creates routes for serving static files
use rocket_contrib::json::Json; // JSON helper
// See Rocket.toml
#[database("sqlitedb")]
struct SQLiteDB(diesel::SqliteConnection);
// See Rocket.toml
#[database("pgdb")]
struct PGDB(diesel::PgConnection);
// A basic User schema
#[derive(FromForm, Serialize, Deserialize, UriDisplayQuery)]
struct User {
name: String,
account: usize,
}
// A wealthy User schema
#[derive(FromForm)]
struct WealthyUser {
name: String,
account: WealthyAccount,
}
struct WealthyAccount(usize);
impl<'v> FromFormValue<'v> for WealthyAccount {
type Error = &'v RawStr;
fn from_form_value(form_value: &'v RawStr) -> Result<WealthyAccount, &'v RawStr> {
match form_value.parse::<usize>() {
Ok(account) if account >= 1000 => Ok(WealthyAccount(account)),
_ => Err(form_value),
}
}
}
#[get("/")]
fn index(_db: SQLiteDB) -> &'static str {
// curl -si 'http://localhost:8000'
"Hello, world!"
}
#[get("/user?<id>&<user..>", rank = 2)]
fn user_get(_db: SQLiteDB, id: u8, user: Form<User>) -> String {
// Use Option<From<User>> or Result<From<User>> for error handling.
// curl -si 'http://localhost:8000/user?id=1&name=foo&account=1'
format!("Hello, {} ({}) [{}]", user.name, user.account, id)
}
#[get("/user?<id>&<user..>", rank = 1)]
fn user_get_wealthy(_db: SQLiteDB, id: u8, user: Form<WealthyUser>) -> Option<String> {
// curl -si 'http://localhost:8000/user?id=1&name=foo&account=1000'
// Return None to respond with 404 not found
Some(format!("Hello dear, {} ({}) [{}]", user.name, user.account.0, id))
}
#[post("/user", data = "<user>", rank = 1)]
fn user_post(_db: SQLiteDB, user: Form<User>) -> status::Accepted<String> {
// curl -si 'http://localhost:8000/user' -X POST -d 'name=foo&account=122'
status::Accepted(Some(uri!(user_get: id = 1, user = user.0).to_string()))
}
#[post("/user", data = "<user>", format = "json", rank = 2)]
fn user_post_json(
_db: SQLiteDB,
user: Json<User>
) -> Result<status::Accepted<Json<User>>, status::BadRequest<Json<String>>> {
// curl -si 'http://localhost:8000/user' -X POST -d '{"name": "x", "account": 122}' -H 'Content-Type: application/json'
match user.name.len() {
0 => Err(status::BadRequest(Some(Json("Name cannot be empty".to_string())))),
_ => Ok(status::Accepted(Some(user))),
}
}
#[catch(404)]
fn not_found(req: &Request) -> status::NotFound<String> {
// curl -si 'http://localhost:8000/foo'
status::NotFound(format!("Sorry, '{}' is not a valid path.", req.uri()))
}
fn main() {
// TIP: For staging/productioon, run `ROCKET_ENV=<stage/prod> cargo run`
rocket::ignite()
.attach(SQLiteDB::fairing())
// .attach(PGDB::fairing())
.attach(AdHoc::on_launch("Custom Fairing", |_| { println!("Launching App") }))
.mount("/", routes![
index,
user_get,
user_get_wealthy, // when account value >= 1000
user_post,
user_post_json // When Content-Type: application/json
])
.mount("/static", StaticFiles::from("./src/static"))
.register(catchers![not_found])
.launch();
}
#[cfg(test)]
mod test;
# TIP: Use `ROCKET_{VAR}` env vars to override.
# e.g. ROCKET_DATABASES={sqlitedb={url="foo.sqlite"}} cargo run
# [global.limits]
# json = 5242880
# forms = 5242880
[global.databases.sqlitedb]
url = "db.sqlite"
pool_size = 1
[global.databases.pgdb]
url = "postgres://postgres:postgres@localhost:5432/db"
pool_size = 1
[development]
address = "localhost"
port = 8000
workers = 2
keep_alive = 5
log = "normal"
# secret_key = [randomly generated at launch]
[staging]
address = "0.0.0.0"
port = 8000
workers = 4
keep_alive = 5
log = "normal"
# secret_key = [randomly generated at launch]
[production]
address = "0.0.0.0"
port = 8000
workers = 4
keep_alive = 5
log = "critical"
# secret_key = [randomly generated at launch]
use super::*;
use rocket::local::Client;
use rocket::http::Status;
// TIP: Use `ROCKET_CODEGEN_DEBUG=1 cargo build` to expand code
#[test]
fn hello_world() {
let rocket = rocket::ignite().attach(SQLiteDB::fairing()).mount("/", routes![index]);
let client = Client::new(rocket).expect("valid rocket instance");
let mut response = client.get("/").dispatch();
assert_eq!(response.status(), Status::Ok);
assert_eq!(response.body_string(), Some("Hello, world!".into()));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment