Skip to content

Commit

Permalink
Merge pull request #103 from kana-rus/add_derive_from_request
Browse files Browse the repository at this point in the history
Add derive from request
  • Loading branch information
kanarus authored Mar 5, 2024
2 parents e2d0987 + c63e4c8 commit 195a228
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 4 deletions.
3 changes: 2 additions & 1 deletion examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ members = [
"realworld",
"quick_start",
"json_response",
"derive_from_request",
]

[workspace.dependencies]
# To assure "DEBUG" feature be off even if DEBUGing `../ohkami`,
# explicitly set `default-features = false`
ohkami = { version = "0.15.0", path = "../ohkami", default-features = false, features = ["rt_tokio", "utils", "testing"] }
ohkami = { version = "0.15.1", path = "../ohkami", default-features = false, features = ["rt_tokio", "utils", "testing"] }
tokio = { version = "1", features = ["full"] }
sqlx = { version = "0.7.3", features = ["runtime-tokio-native-tls", "postgres", "macros", "chrono", "uuid"] }
tracing = "0.1"
Expand Down
8 changes: 8 additions & 0 deletions examples/derive_from_request/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "derive_from_request"
version = "0.1.0"
edition = "2021"

[dependencies]
ohkami = { workspace = true }
tokio = { workspace = true }
54 changes: 54 additions & 0 deletions examples/derive_from_request/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#![allow(unused)]
fn main() {}

use ohkami::{FromRequest, Method};


struct RequestMethod(Method);
impl<'req> FromRequest<'req> for RequestMethod {
type Error = std::convert::Infallible;
fn from_request(req: &'req ohkami::prelude::Request) -> Result<Self, Self::Error> {
Ok(Self(req.method()))
}
}

struct RequestPath<'req>(std::borrow::Cow<'req, str>);
impl<'req> FromRequest<'req> for RequestPath<'req> {
type Error = std::convert::Infallible;
fn from_request(req: &'req ohkami::prelude::Request) -> Result<Self, Self::Error> {
Ok(Self(req.path()))
}
}

struct RequestPathOwned(String);
impl<'req> FromRequest<'req> for RequestPathOwned {
type Error = std::convert::Infallible;
fn from_request(req: &'req ohkami::prelude::Request) -> Result<Self, Self::Error> {
Ok(Self(req.path().into()))
}
}


#[derive(FromRequest)]
struct MethodAndPathA {
method: RequestMethod,
path: RequestPathOwned,
}

#[derive(FromRequest)]
struct MethodAndPathB<'req> {
method: RequestMethod,
path: RequestPath<'req>,
}

#[derive(FromRequest)]
struct MethodAndPathC(
RequestMethod,
RequestPathOwned,
);

#[derive(FromRequest)]
struct MethodAndPathD<'req>(
RequestMethod,
RequestPath<'req>,
);
4 changes: 2 additions & 2 deletions ohkami/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ohkami"
version = "0.15.0"
version = "0.15.1"
edition = "2021"
authors = ["kanarus <[email protected]>"]
description = "Build web app in intuitive and declarative code"
Expand All @@ -17,7 +17,7 @@ features = ["rt_tokio", "custom-header"]

[dependencies]
ohkami_lib = { version = "=0.1.1", path = "../ohkami_lib" }
ohkami_macros = { version = "=0.6.0", path = "../ohkami_macros" }
ohkami_macros = { version = "=0.6.1", path = "../ohkami_macros" }
tokio = { version = "1", optional = true, features = ["net", "rt", "io-util", "sync", "time"] }
async-std = { version = "1", optional = true }
byte_reader = { version = "2.0.0", features = ["text"] }
Expand Down
2 changes: 2 additions & 0 deletions ohkami/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ mod __rt__ {

mod request;
pub use request::{Request, Method, FromRequestError, FromRequest, FromParam, Memory};
pub use ::ohkami_macros::FromRequest;

mod response;
pub use response::{Response, Status, IntoResponse};
Expand Down Expand Up @@ -166,6 +167,7 @@ pub mod __internal__ {
parse_urlencoded,
};

#[cfg(any(feature="rt_tokio",feature="rt_async-std"))]
pub use crate::fang::Fangs;

/* for benchmarks */
Expand Down
2 changes: 1 addition & 1 deletion ohkami_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ proc-macro = true

[package]
name = "ohkami_macros"
version = "0.6.0"
version = "0.6.1"
edition = "2021"
authors = ["kanarus <[email protected]>"]
description = "Proc macros for ohkami - intuitive and declarative web framework"
Expand Down
53 changes: 53 additions & 0 deletions ohkami_macros/src/from_request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use proc_macro2::{Span, TokenStream};
use syn::{Field, ItemStruct, Result};
use quote::quote;
use crate::components::from_request_lifetime;


pub(super) fn derive_from_request(target: TokenStream) -> Result<TokenStream> {
let s: ItemStruct = syn::parse2(target)?;

let name = &s.ident;

let generics_params_r = &s.generics.params;
let generics_params_l = &mut generics_params_r.clone();
let generics_where = &s.generics.where_clause;

let impl_lifetime = match s.generics.lifetimes().count() {
0 => {
let il = from_request_lifetime();
generics_params_l.push(il.clone());
il
}
1 => s.generics.params.first().unwrap().clone(),
_ => return Err(syn::Error::new(Span::call_site(), "#[derive(FromRequest)] doesn't support multiple lifetime params")),
};

let build = if s.semi_token.is_none() {/* struct S { 〜 } */
let fields = s.fields.into_iter()
.map(|Field { ident, ty, .. }| quote! {
#ident: <#ty as ::ohkami::FromRequest>::from_request(req)
.map_err(::ohkami::IntoResponse::into_response)?
});
quote![ Self { #( #fields ),* } ]

} else {/* struct T(); */
let fields = s.fields.into_iter()
.map(|Field { ty, .. }| quote! {
<#ty as ::ohkami::FromRequest>::from_request(req)
.map_err(::ohkami::IntoResponse::into_response)?
});
quote![ Self(#( #fields ),*) ]
};

Ok(quote! {
impl<#generics_params_l> ::ohkami::FromRequest<#impl_lifetime> for #name<#generics_params_r>
#generics_where
{
type Error = ::ohkami::Response;
fn from_request(req: &#impl_lifetime ::ohkami::Request) -> ::std::result::Result<Self, Self::Error> {
::std::result::Result::Ok(#build)
}
}
})
}
31 changes: 31 additions & 0 deletions ohkami_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod serde;
mod query;
mod payload;
mod response;
mod from_request;


/// The *perfect* reexport of [serde](https://crates.io/crates/serde)'s `Serialize`.
Expand Down Expand Up @@ -253,3 +254,33 @@ pub fn ResponseBody(format: proc_macro::TokenStream, data: proc_macro::TokenStre
.unwrap_or_else(|e| e.into_compile_error())
.into()
}


/// # `#[derive(FromRequest)]`
///
/// Automatically impl `FromRequest` for a struct composed of
/// `FromRequest` types
///
/// <br>
///
/// *example.rs*
/// ```ignore
/// use ohkami::FromRequest;
/// use sqlx::PgPool;
///
/// #[derive(FromRequest)]
/// struct MyItems1<'req> {
/// db: ohkami::Memory<'req, PgPool>,
/// }
///
/// #[derive(FromRequest)]
/// struct MyItems2(
/// MyItems<'req>,
/// );
/// ```
#[proc_macro_derive(FromRequest)]
pub fn derive_from_request(target: proc_macro::TokenStream) -> proc_macro::TokenStream {
from_request::derive_from_request(target.into())
.unwrap_or_else(|e| e.into_compile_error())
.into()
}

0 comments on commit 195a228

Please sign in to comment.