extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse::Parse,
Token,
};
const INCORRECT_FORMAT1: &'static str = "PUT THERE SOMETHING";
fn the_crate_name() -> proc_macro2::TokenStream {
quote::quote! {
uni_localservice
}
}
struct ServiceImpl {
struct_name: syn::Type,
service_mod: syn::Expr,
env: Option<syn::Expr>,
method_name: syn::Expr,
generic: Option<syn::Generics>,
}
impl Parse for ServiceImpl {
fn parse(parser: syn::parse::ParseStream<'_>) -> syn::parse::Result<Self> {
let mut struct_name = None;
let mut service_mod = None;
let mut env = None;
let mut method_name = None;
let mut generic = None;
while parser.peek(syn::Ident) {
let ident =
parser.parse::<syn::Ident>().expect(INCORRECT_FORMAT1).to_string();
let _ = parser.parse::<Token![:]>().expect(INCORRECT_FORMAT1);
if ident == "base" {
let error = format!(
"{}\n{}",
"Expected *identifier* for 'base'", INCORRECT_FORMAT1
);
struct_name = Some(parser.parse::<syn::Type>().expect(&error));
} else if ident == "service" {
let error =
format!("{}\n{}", "Expected *type* for 'service'", INCORRECT_FORMAT1);
service_mod = Some(parser.parse::<syn::Expr>().expect(&error));
} else if ident == "method" {
let error =
format!("{}\n{}", "Expected *type* for 'method'", INCORRECT_FORMAT1);
method_name = Some(parser.parse::<syn::Expr>().expect(&error));
} else if ident == "generic" {
let error =
format!("{}\n{}", "Expected *type* for 'generic'", INCORRECT_FORMAT1);
generic = Some(parser.parse::<syn::Generics>().expect(&error));
} else if ident == "env" {
let error =
format!("{}\n{}", "Expected *type* for 'env'", INCORRECT_FORMAT1);
let pre_env = parser.parse::<syn::Expr>().expect(&error);
match &pre_env {
syn::Expr::Tuple(t) if t.elems.len() == 0 => {},
_ => env = Some(pre_env),
}
} else {
panic!("Unexpected parameter:{}.\n{}", ident, INCORRECT_FORMAT1);
}
parser.parse::<Token![,]>().expect(INCORRECT_FORMAT1);
}
return Ok(ServiceImpl {
struct_name: struct_name
.expect(&format!("{} not found.\n{}", "base", INCORRECT_FORMAT1)),
service_mod: service_mod
.expect(&format!("{} not found.\n{}", "service", INCORRECT_FORMAT1)),
env,
method_name: method_name
.expect(&format!("{} not found.\n{}", "method", INCORRECT_FORMAT1)),
generic,
});
}
}
#[proc_macro]
pub fn u_service_impl(input_tokens: TokenStream) -> TokenStream {
let service_impl = syn::parse_macro_input!(input_tokens as ServiceImpl);
let (relay_name, env) = match service_impl.env {
None => (quote! {RelayNoEnv}, quote! {()}),
Some(t) => (quote! {Relay}, quote! {#t}),
};
let generic = match service_impl.generic {
Some(g) => quote!(#g),
None => quote!(),
};
let crate_name = the_crate_name();
let struct_name = service_impl.struct_name;
let service_mod = service_impl.service_mod;
let method_name = service_impl.method_name;
let result = quote! {
#[#crate_name::async_trait::async_trait]
impl #generic #crate_name::LocalService for #struct_name {
type In = <
#service_mod::Service
as
#crate_name::uni_components::service::Service
>::In;
type Out = <
#service_mod::Service
as
#crate_name::uni_components::service::Service
>::Out;
type Env = #env;
type Service = #service_mod::Service;
async fn exec(&mut self, data: Self::In, env: &Self::Env) -> Self::Out {
return self.#method_name(data, env).await;
}
fn service(&self) -> &'static Self::Service {
return #service_mod::REF;
}
}
impl #generic #struct_name {
pub fn register<UNI_T: 'static + Send + Sync + Clone>(
self,
server: &mut #crate_name::tide::Server<UNI_T>,
) {
use #crate_name::LocalService;
use #crate_name::uni_components::service::Service;
let r = #crate_name::#relay_name {
ls: self,
};
let mut zz = server.at(r.ls.service().path());
match r.ls.service().kind() {
#crate_name::uni_components::Kind::Get => zz.get(r),
#crate_name::uni_components::Kind::Post => zz.post(r),
};
}
}
};
if let Ok(_) = std::env::var("UNI_LOCALSERVICE_MACRO_DEBUG") {
println!("u_service_impl:\n{:#}\n", result);
}
return result.into();
}