Skip to content

Commit

Permalink
Macro generation for contollers: richerarc#62 (richerarc#65)
Browse files Browse the repository at this point in the history
* saphir_macro: Macros now generate dispatches correctly

* Fixed doc comments

* Moved handler functions to handler file

* Added HandlerAttrs type

* Parse guard meta

* Working guard macro

* Added generation of wrapper fn and auto loading of body for sync fns

* marco v1.0.0-beta & saphir v2.0.0-beta

* Fixed warnings + runned rustfmt

* Applied a couple of clippy fixes

Co-authored-by: Luc Fauvel <[email protected]>
  • Loading branch information
richerarc and LucFauvel authored Feb 28, 2020
1 parent 2cab5d6 commit 8c587ed
Show file tree
Hide file tree
Showing 25 changed files with 1,250 additions and 476 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
run: cargo build --verbose --all-features
- name: Run tests
run: cargo test --verbose
run: cargo test --verbose --all-features
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/target
**/target
**/*.rs.bk
.idea/
*.lock
Expand Down
49 changes: 5 additions & 44 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,44 +1,5 @@
[package]
name = "saphir"
version = "2.0.0-alpha4"
edition = "2018"
authors = ["Richer Archambault <[email protected]>"]
description = "Fully async-await http server framework"
documentation = "https://docs.rs/saphir"
homepage = "https://github.com/richerarc/saphir"
repository = "https://github.com/richerarc/saphir"
readme = "README.md"
keywords = ["hyper", "http", "server", "web", "async"]
license = "MIT"

[features]

default = []
https = ["base64", "rustls", "tokio-rustls"]
json = ["serde", "serde_json"]
form = ["serde", "serde_urlencoded"]

[dependencies]
log = "0.4"
hyper = "0.13"
tokio = { version = "0.2", features = ["full"] }
futures = "0.3"
futures-util = "0.3"
tower-service = "0.3"
cookie = { package = "saphir-cookie", version = "0.13" }
http = "0.2"
http-body = "0.3"
parking_lot = "0.10"
regex = "1.3"
rustls = { version = "0.17", optional = true }
tokio-rustls = { version = "0.13", optional = true }
base64 = { version = "0.11", optional = true }
serde = { version = "1.0", optional = true }
serde_json = { version = "1.0", optional = true }
serde_urlencoded = { version = "0.6", optional = true }

[dev-dependencies]
tokio-timer = "0.2.13"
env_logger = "0.7"
serde = "1.0"
serde_derive = "1.0"
[workspace]
members = [
"saphir",
"saphir_macro",
]
3 changes: 3 additions & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
reorder_imports = true
merge_imports = true
max_width = 160
45 changes: 45 additions & 0 deletions saphir/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
[package]
name = "saphir"
version = "2.0.0-beta"
edition = "2018"
authors = ["Richer Archambault <[email protected]>"]
description = "Fully async-await http server framework"
documentation = "https://docs.rs/saphir"
homepage = "https://github.com/richerarc/saphir"
repository = "https://github.com/richerarc/saphir"
readme = "README.md"
keywords = ["hyper", "http", "server", "web", "async"]
license = "MIT"

[features]

default = []
https = ["base64", "rustls", "tokio-rustls"]
json = ["serde", "serde_json"]
form = ["serde", "serde_urlencoded"]

[dependencies]
log = "0.4"
hyper = "0.13"
tokio = { version = "0.2", features = ["full"] }
futures = "0.3"
futures-util = "0.3"
tower-service = "0.3"
cookie = { package = "saphir-cookie", version = "0.13" }
http = "0.2"
http-body = "0.3"
parking_lot = "0.10"
regex = "1.3"
rustls = { version = "0.17", optional = true }
tokio-rustls = { version = "0.13", optional = true }
base64 = { version = "0.11", optional = true }
serde = { version = "1.0", optional = true }
serde_json = { version = "1.0", optional = true }
serde_urlencoded = { version = "0.6", optional = true }

[dev-dependencies]
tokio-timer = "0.2.13"
env_logger = "0.7"
serde = "1.0"
serde_derive = "1.0"
saphir_macro = { path = "../saphir_macro", version = "1.0.0-beta"}
55 changes: 19 additions & 36 deletions examples/basic.rs → saphir/examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ extern crate log;

use futures::future::Ready;
use saphir::prelude::*;
use serde_derive::{Serialize, Deserialize};
use serde_derive::{Deserialize, Serialize};
use tokio::sync::RwLock;

// == controller == //
Expand All @@ -28,14 +28,14 @@ impl Controller for MagicController {
let b = EndpointsBuilder::new();

#[cfg(feature = "json")]
let b = b.add(Method::POST, "/json", MagicController::user_json);
let b = b.add(Method::POST, "/json", MagicController::user_json);
#[cfg(feature = "json")]
let b = b.add(Method::GET, "/json", MagicController::get_user_json);
let b = b.add(Method::GET, "/json", MagicController::get_user_json);

#[cfg(feature = "form")]
let b = b.add(Method::POST, "/form", MagicController::user_form);
let b = b.add(Method::POST, "/form", MagicController::user_form);
#[cfg(feature = "form")]
let b = b.add(Method::GET, "/form", MagicController::get_user_form);
let b = b.add(Method::GET, "/form", MagicController::get_user_form);

b.add(Method::GET, "/delay/{delay}", MagicController::magic_delay)
.add_with_guards(Method::GET, "/guarded/{delay}", MagicController::magic_delay, |g| {
Expand Down Expand Up @@ -63,7 +63,7 @@ impl MagicController {
(200, body)
}

async fn match_any_route(&self, req: Request) -> (u16, String) {
async fn match_any_route(&self, req: Request) -> (u16, String) {
(200, req.uri().path().to_string())
}

Expand All @@ -79,7 +79,10 @@ impl MagicController {
#[cfg(feature = "form")]
async fn user_form(&self, mut req: Request) -> (u16, String) {
if let Ok(user) = req.body_mut().take_as::<Form<User>>().await {
(200, format!("New user with username: {} and age: {} read from Form data", user.username, user.age))
(
200,
format!("New user with username: {} and age: {} read from Form data", user.username, user.age),
)
} else {
(400, "Bad user format".to_string())
}
Expand All @@ -92,7 +95,7 @@ impl MagicController {
Json(User {
username: "[email protected]".to_string(),
age: 42,
})
}),
)
}

Expand All @@ -103,7 +106,7 @@ impl MagicController {
Form(User {
username: "[email protected]".to_string(),
age: 42,
})
}),
)
}
}
Expand Down Expand Up @@ -138,10 +141,7 @@ impl StatsData {
let mut entered = self.entered.write().await;
let exited = self.exited.read().await;
*entered += 1;
info!(
"entered stats middleware! Current data: entered={} ; exited={}",
*entered, *exited
);
info!("entered stats middleware! Current data: entered={} ; exited={}", *entered, *exited);
}

let res = chain.next(ctx).await?;
Expand All @@ -150,21 +150,14 @@ impl StatsData {
let mut exited = self.exited.write().await;
let entered = self.entered.read().await;
*exited += 1;
info!(
"exited stats middleware! Current data: entered={} ; exited={}",
*entered, *exited
);
info!("exited stats middleware! Current data: entered={} ; exited={}", *entered, *exited);
}

Ok(res)
}
}

async fn log_middleware(
prefix: &String,
ctx: HttpContext<Body>,
chain: &dyn MiddlewareChain,
) -> Result<Response<Body>, SaphirError> {
async fn log_middleware(prefix: &String, ctx: HttpContext<Body>, chain: &dyn MiddlewareChain) -> Result<Response<Body>, SaphirError> {
info!("{} | new request on path: {}", prefix, ctx.request.uri().path());
let res = chain.next(ctx).await?;
info!("{} | new response with status: {}", prefix, res.status());
Expand Down Expand Up @@ -198,25 +191,15 @@ impl ForbidderData {
}

async fn forbidder_guard(data: &ForbidderData, req: Request<Body>) -> Result<Request<Body>, u16> {
if req
.captures()
.get("variable")
.and_then(|v| data.filter_forbidden(v))
.is_some()
{
if req.captures().get("variable").and_then(|v| data.filter_forbidden(v)).is_some() {
Err(403)
} else {
Ok(req)
}
}

async fn numeric_delay_guard(_: &(), req: Request<Body>) -> Result<Request<Body>, &'static str> {
if req
.captures()
.get("delay")
.and_then(|v| v.parse::<u64>().ok())
.is_some()
{
if req.captures().get("delay").and_then(|v| v.parse::<u64>().ok()).is_some() {
Ok(req)
} else {
Err("Guard blocked request: delay is not a valid number.")
Expand All @@ -234,13 +217,13 @@ async fn main() -> Result<(), SaphirError> {
.route("/{variable}/print", Method::GET, test_handler)
.route_with_guards("/{variable}/guarded_print", Method::GET, test_handler, |g| {
g.add(forbidder_guard, ForbidderData { forbidden: "forbidden" })
.add(forbidder_guard, ForbidderData { forbidden: "password" })
.add(forbidder_guard, ForbidderData { forbidden: "password" })
})
.controller(MagicController::new("Just Like Magic!"))
})
.configure_middlewares(|m| {
m.apply(log_middleware, "LOG".to_string(), vec!["/"], None)
.apply(StatsData::stats_middleware, StatsData::new(), vec!["/"], None)
.apply(StatsData::stats_middleware, StatsData::new(), vec!["/"], None)
})
.build();

Expand Down
54 changes: 54 additions & 0 deletions saphir/examples/macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use saphir::prelude::*;
use saphir_macro::controller;
use serde_derive::{Deserialize, Serialize};

fn guard_string(_controller: &UserController) -> String {
UserController::BASE_PATH.to_string()
}

async fn print_string_guard(string: &String, req: Request<Body>) -> Result<Request<Body>, &'static str> {
println!("{}", string);

Ok(req)
}

#[derive(Serialize, Deserialize, Clone)]
struct User {
username: String,
age: i64,
}

struct UserController {}

#[controller(name = "/users", version = 1, prefix = "api")]
impl UserController {
#[get("/<user_id>")]
async fn get_user(&self, _req: Request) -> (u16, String) {
(200, "Yo".to_string())
}

#[post("/sync")]
fn get_user_sync(&self, mut req: Request<Json<User>>) -> (u16, Json<User>) {
let u = req.body_mut();
u.username = "Samuel".to_string();
(200, Json(u.clone()))
}

#[guard(fn = "print_string_guard", data = "guard_string")]
#[get("/")]
async fn list_user(&self, _req: Request<Body<Vec<u8>>>) -> (u16, String) {
(200, "Yo".to_string())
}
}

#[tokio::main]
async fn main() -> Result<(), SaphirError> {
env_logger::init();

let server = Server::builder()
.configure_listener(|l| l.interface("127.0.0.1:3000"))
.configure_router(|r| r.controller(UserController {}))
.build();

server.run().await
}
Loading

0 comments on commit 8c587ed

Please sign in to comment.