pub mod core;
pub mod layer;
use crate::toolkit::attr::StrAttrMap;
use hyper::Method;
use miko_core::IntoMethods;
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use syn::parse::{Parse, ParseStream};
pub use layer::LayerAttr;
#[derive(Debug)]
pub struct RouteAttr {
pub path: String,
pub method: Option<Vec<Method>>,
}
impl Parse for RouteAttr {
fn parse(input: ParseStream) -> syn::Result<Self> {
let attr_map = StrAttrMap::from_parse_stream(input);
let path = attr_map.get_or_default("path").unwrap();
let methods = attr_map.get("method");
let methods = match methods {
Some(methods) => methods.into_methods(),
None => {
vec![]
}
};
Ok(RouteAttr {
path,
method: if methods.is_empty() {
None
} else {
Some(methods)
},
})
}
}
pub fn build_register_expr(ra: &RouteAttr, fn_name: &Ident, layers: &[LayerAttr]) -> TokenStream {
let path = ra.path.clone();
let methods = if let Some(method) = ra.method.clone() {
method
} else {
vec![Method::GET]
};
let mut stmts = Vec::new();
if layers.is_empty() {
for method in &methods {
let method_name = format_ident!("{}", method.as_str().to_uppercase());
stmts.push(quote! {
router.route(::miko::hyper::Method::#method_name, #path, #fn_name);
});
}
} else {
let layer_exprs: Vec<_> = layers.iter().map(|l| &l.layer_expr).collect();
for method in &methods {
let _method_name = format_ident!("{}", method.as_str().to_uppercase());
let service_method_name = format_ident!("{}_service", method.as_str().to_lowercase());
stmts.push(quote! {
{
let __handler = #fn_name;
let __svc = ::miko::handler::handler_to_svc(
::std::sync::Arc::new(
::miko::handler::TypedHandler::new(__handler, ::std::sync::Arc::new(()))
)
);
#(
let __svc = {
let layered = ::miko::tower::Layer::layer(&#layer_exprs, __svc);
::miko::tower::ServiceExt::map_response(layered, |resp| {
let (parts, body) = resp.into_parts();
let body = ::miko::http_body_util::BodyExt::map_err(body, |err| {
::miko::miko_core::MikoError(::std::boxed::Box::new(err))
});
let boxed_body = ::miko::http_body_util::BodyExt::boxed_unsync(body);
::miko::hyper::Response::from_parts(parts, boxed_body)
})
};
)*
let __boxed = ::miko::tower::util::BoxCloneService::new(__svc);
router.#service_method_name(#path, __boxed);
}
});
}
}
quote! {
::miko::inventory::submit! {
::miko::auto::RouteFlag {
register: |mut router| {
#(#stmts)*
router
}
}
}
}
}