#![allow(warnings, unused)]
#![feature(proc_macro_span)]
extern crate proc_macro;
use std::{
any::Any,
borrow::BorrowMut,
cell::RefCell,
collections::HashMap,
f64::consts::E,
ops::Add,
path,
str::FromStr,
sync::{Arc, Mutex},
};
use cmeta::CMetaValue;
use nidrs_extern::{datasets::ServiceType, helper::merge_derives};
use once_cell::sync::Lazy;
use proc_macro::{Ident, Span, TokenStream};
use proc_macro2::TokenStream as TokenStream2;
use proc_macro2::{Punct, TokenTree};
use quote::{quote, ToTokens};
use syn::{
meta,
parse::{Parse, ParseStream},
parse_str, Expr, ExprArray, ItemStruct, PatPath, Stmt, Token,
};
use syn::{parse, punctuated::Punctuated};
use syn::{parse_macro_input, spanned::Spanned, ItemFn};
mod args_parse;
use args_parse::*;
use syn_args::{def, SynArgs};
use utils::merge_uses;
mod app_parse;
mod args;
mod cmeta;
mod current_module;
mod impl_expand;
mod import_path;
mod utils;
static ROUTES: Lazy<Mutex<HashMap<String, Vec<String>>>> = Lazy::new(|| Mutex::new(HashMap::new())); static EVENTS: Lazy<Mutex<HashMap<String, Vec<(String, String)>>>> = Lazy::new(|| Mutex::new(HashMap::new())); static DEFAULT_INTERS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(vec![]));
#[proc_macro_attribute]
pub fn get(args: TokenStream, input: TokenStream) -> TokenStream {
return impl_expand::route("get", args, input);
}
#[proc_macro_attribute]
pub fn post(args: TokenStream, input: TokenStream) -> TokenStream {
return impl_expand::route("post", args, input);
}
#[proc_macro_attribute]
pub fn put(args: TokenStream, input: TokenStream) -> TokenStream {
return impl_expand::route("put", args, input);
}
#[proc_macro_attribute]
pub fn delete(args: TokenStream, input: TokenStream) -> TokenStream {
return impl_expand::route("delete", args, input);
}
#[proc_macro_attribute]
pub fn any(args: TokenStream, input: TokenStream) -> TokenStream {
return impl_expand::route("any", args, input);
}
#[proc_macro_attribute]
pub fn head(args: TokenStream, input: TokenStream) -> TokenStream {
return impl_expand::route("head", args, input);
}
#[proc_macro_attribute]
pub fn on(args: TokenStream, input: TokenStream) -> TokenStream {
return impl_expand::route("on", args, input);
}
#[proc_macro_attribute]
pub fn options(args: TokenStream, input: TokenStream) -> TokenStream {
return impl_expand::route("options", args, input);
}
#[proc_macro_attribute]
pub fn patch(args: TokenStream, input: TokenStream) -> TokenStream {
return impl_expand::route("patch", args, input);
}
#[proc_macro_attribute]
pub fn trace(args: TokenStream, input: TokenStream) -> TokenStream {
return impl_expand::route("trace", args, input);
}
#[proc_macro_attribute]
pub fn __route_derive(args: TokenStream, input: TokenStream) -> TokenStream {
return impl_expand::route_derive(args, input);
}
#[syn_args::derive::declare(def::Option<def::String>)]
#[syn_args::derive::proc_attribute]
pub fn controller(args: Args, input: TokenStream) -> TokenStream {
let path = match args {
Args::F1(def::Option(Some(v))) => v.to_string(),
_ => "".to_string(),
};
current_module::begin_mod();
let func = parse_macro_input!(input as ItemStruct);
let ident = func.ident.clone();
let ident_name = ident.to_string();
import_path::push_path(&func.ident.to_string());
println!("// controller {} {:?}", ident.to_string(), func.attrs);
ROUTES.lock().unwrap().insert(ident.to_string(), Vec::new());
TokenStream::from(quote! {
#[nidrs::meta(nidrs::datasets::ServiceType::from("Controller"))]
#[nidrs::meta(nidrs::datasets::ServiceName::from(#ident_name))]
#[nidrs::meta(nidrs::datasets::ControllerPath::from(#path))]
#[nidrs::macros::__service_derive(Controller)]
#func
})
}
#[proc_macro_attribute]
pub fn injectable(args: TokenStream, input: TokenStream) -> TokenStream {
current_module::begin_mod();
let func = parse_macro_input!(input as ItemStruct);
let func_ident = func.ident.clone();
let func_ident_name = func.ident.to_string();
let call_site = Span::call_site();
let binding = call_site.source_file().path();
let call_site_str = binding.to_string_lossy();
let call_site_line = call_site.start().line();
import_path::push_path(&func_ident_name.clone());
return TokenStream::from(quote! {
#[nidrs::meta(nidrs::datasets::ServiceType::from("Service"))]
#[nidrs::meta(nidrs::datasets::ServiceName::from(#func_ident_name))]
#[nidrs::macros::__service_derive(Service)]
#func
});
}
#[proc_macro_attribute]
pub fn interceptor(args: TokenStream, input: TokenStream) -> TokenStream {
current_module::begin_mod();
let func = parse_macro_input!(input as ItemStruct);
let func_ident = func.ident.clone();
let func_ident_name = func.ident.to_string();
import_path::push_path(&func_ident_name.clone());
return TokenStream::from(quote! {
#[nidrs::meta(nidrs::datasets::ServiceType::from("Interceptor"))]
#[nidrs::meta(nidrs::datasets::ServiceName::from(#func_ident_name))]
#[nidrs::macros::__service_derive(Interceptor)]
#func
});
}
#[syn_args::derive::declare(def::Expr)]
#[syn_args::derive::proc_attribute]
pub fn __service_derive(args: Args, input: TokenStream) -> TokenStream {
let service_type = match args {
Args::F1(v) => match v.to_path_name().unwrap().as_str() {
"Controller" => ServiceType::Controller,
"Service" => ServiceType::Service,
"Interceptor" => ServiceType::Interceptor,
_ => panic!("Invalid service type"),
},
};
impl_expand::__service_derive(service_type, input)
}
#[syn_args::derive::declare(args::ModuleOptions)]
#[syn_args::derive::proc_attribute]
pub fn module(args: Args, input: TokenStream) -> TokenStream {
let module_options: args::ModuleOptions = {
if let Args::F1(options) = args {
options
} else {
panic!("Invalid argument");
}
};
let func = parse_macro_input!(input as ItemStruct);
let (impl_generics, ty_generics, where_clause) = func.generics.split_for_impl();
let ident = func.ident.clone();
let ident_name = ident.to_string();
let controller_register_tokens = impl_expand::expand_controller_register(ident_name.clone(), &module_options.controllers);
let service_register_tokens = impl_expand::expand_service_register(ident_name.clone(), &module_options.services);
let all_interceptors = impl_expand::merge_defaults_interceptors(module_options.interceptors.clone());
let interceptor_register_tokens = impl_expand::expand_interceptor_register(ident_name.clone(), &all_interceptors);
let (import_names_tokens, imports_register_tokens) = impl_expand::expand_imports_register(ident_name.clone(), &module_options.imports, &func);
let exports_names_tokens = impl_expand::expand_exports_append(&module_options.exports);
let services_dep_inject_tokens: TokenStream2 = impl_expand::expand_dep_inject("get_service", ident_name.clone(), &module_options.services);
let controller_dep_inject_tokens = impl_expand::expand_dep_inject("get_controller", ident_name.clone(), &module_options.controllers);
let interceptor_dep_inject_tokens = impl_expand::expand_dep_inject("get_interceptor", ident_name.clone(), &module_options.interceptors);
let trigger_on_module_init_tokens: TokenStream2 = impl_expand::expand_events_trigger(ident_name.clone(), "on_module_init");
let trigger_on_module_destroy_tokens = impl_expand::expand_events_trigger(ident_name.clone(), "on_module_destroy");
let module_meta_tokens = cmeta::CMeta::build_tokens();
let is_global_tokens = if let Some(CMetaValue::Bool(bool)) = cmeta::CMeta::get_stack_data("Global") { bool } else { false };
println!("// module {:?}", ident.to_string());
ROUTES.lock().unwrap().clear();
EVENTS.lock().unwrap().clear();
current_module::end_mod();
let derives_tokens: Vec<TokenStream2> = merge_derives(&func, &["Default"]);
return TokenStream::from(quote! {
#(#derives_tokens)*
#func
impl #impl_generics nidrs::Module for #ident #ty_generics #where_clause {
fn init(self, mut ctx: nidrs::ModuleCtx) -> nidrs::ModuleCtx{
use nidrs::{Service, Controller, Interceptor, InterCtx, InterceptorHandler, ModuleCtx, StateCtx, ImplMeta};
if ctx.modules.contains_key(#ident_name) {
return ctx;
}
nidrs_macro::log!("Registering module {}.", #ident_name);
ctx.modules.insert(#ident_name.to_string(), Box::new(self));
ctx.imports.insert(#ident_name.to_string(), #import_names_tokens);
ctx.append_exports(#ident_name, #exports_names_tokens, #is_global_tokens);
#interceptor_register_tokens
#controller_register_tokens
#service_register_tokens
#imports_register_tokens
#services_dep_inject_tokens
#controller_dep_inject_tokens
#interceptor_dep_inject_tokens
#trigger_on_module_init_tokens
ctx
}
fn destroy(&self, ctx: &nidrs::ModuleCtx){
#trigger_on_module_destroy_tokens
nidrs::log!("Destroying module {}.", #ident_name);
}
}
impl #impl_generics nidrs::ImplMeta for #ident #ty_generics #where_clause{
fn __meta() -> nidrs::InnerMeta {
#module_meta_tokens
}
}
});
}
#[proc_macro_attribute]
pub fn on_module_init(args: TokenStream, input: TokenStream) -> TokenStream {
let func = parse_macro_input!(input as ItemFn);
let ident = func.sig.ident.clone();
let name = ident.to_string();
let current_service_name: String =
cmeta::CMeta::get_stack_data("ServiceName").expect(&format!("[on_module_init] {} ServiceName not found", name));
EVENTS.lock().unwrap().entry("on_module_init".to_string()).or_insert(vec![]).push((current_service_name, name));
return TokenStream::from(quote! {
#func
});
}
#[proc_macro_attribute]
pub fn on_module_destroy(args: TokenStream, input: TokenStream) -> TokenStream {
let func = parse_macro_input!(input as ItemFn);
let ident = func.sig.ident.clone();
let name = ident.to_string();
let current_service_name: String =
cmeta::CMeta::get_stack_data("ServiceName").expect(&format!("[on_module_init] {} ServiceName not found", name));
EVENTS.lock().unwrap().entry("on_module_destroy".to_string()).or_insert(vec![]).push((current_service_name, name));
return TokenStream::from(quote! {
#func
});
}
#[syn_args::derive::declare(def::Expr, def::Extends<def::Expr>)]
#[syn_args::derive::proc_attribute]
pub fn uses(args: Args, input: TokenStream) -> TokenStream {
let args: Vec<def::Expr> = match args {
Args::F1(first, other) => {
let mut args = vec![first];
args.append(&mut other.clone());
args
}
_ => panic!("Invalid argument"),
};
let raw = TokenStream2::from(input.clone());
let func = parse_macro_input!(input as UFnStruct);
let used_ident = &func.ident;
let inter_names = args.iter().map(|arg| arg.to_path_name().unwrap()).collect::<Vec<String>>();
let expand = match &func.typ {
TokenType::Fn(item) => {
quote! {
#[nidrs::meta(method_uses = [#(#inter_names),*])]
}
}
TokenType::Struct(item) => {
quote! {
#[nidrs::meta(service_uses = [#(#inter_names),*])]
}
}
_ => panic!("Invalid argument"),
};
return quote! {
#expand
#raw
}
.into();
}
#[proc_macro_attribute]
pub fn meta(args: TokenStream, input: TokenStream) -> TokenStream {
let raw: TokenStream = input.clone();
let fun = parse_macro_input!(input as UFnStruct);
current_module::check_mod();
let level = cmeta::CMeta::get_level();
if let None = level {
cmeta::init_app_meta();
cmeta::init_module_meta();
}
let level = cmeta::CMeta::get_level();
if let TokenType::Struct(item) = &fun.typ {
let cur_mod = current_module::get();
if let Some(cur_mod) = cur_mod {
let level_mod = cmeta::CMeta::get_stack("module");
if let Some(cmeta::CMetaValue::String(name)) = &level_mod {
if &cur_mod.name != name {
let deep = cmeta::CMeta::get_deep();
let loop_deep = deep - 1;
for _ in 0..loop_deep {
cmeta::CMeta::pop();
}
cmeta::CMeta::push(cmeta::CMetaLevel::Module(cur_mod.name.clone()));
cmeta::CMeta::push(cmeta::CMetaLevel::Service(item.ident.to_string()));
}
}
}
let level = cmeta::CMeta::get_level();
if let Some(cmeta::CMetaLevel::Service(name)) = level {
if item.ident.to_string() != name {
cmeta::CMeta::pop();
cmeta::CMeta::push(cmeta::CMetaLevel::Service(item.ident.to_string()));
}
} else if let Some(cmeta::CMetaLevel::Handler(name)) = level {
cmeta::CMeta::pop();
cmeta::CMeta::pop();
cmeta::CMeta::push(cmeta::CMetaLevel::Service(item.ident.to_string()));
} else {
cmeta::CMeta::push(cmeta::CMetaLevel::Service(item.ident.to_string()));
}
} else if let TokenType::Fn(item) = &fun.typ {
if let Some(cmeta::CMetaLevel::Handler(name)) = level {
if item.sig.ident.to_string() != name {
cmeta::CMeta::pop();
cmeta::CMeta::push(cmeta::CMetaLevel::Handler(item.sig.ident.to_string()));
}
} else {
cmeta::CMeta::push(cmeta::CMetaLevel::Handler(item.sig.ident.to_string()));
}
}
let targs = args.clone();
let cmeta = parse_macro_input!(targs as cmeta::CMeta);
cmeta::CMeta::collect(cmeta);
return raw;
}
#[syn_args::derive::declare(def::String)]
#[syn_args::derive::proc_attribute]
pub fn version(args: Args, input: TokenStream) -> TokenStream {
let version = match args {
Args::F1(v) => v.to_string(),
_ => "".to_string(),
};
let input = TokenStream2::from(input);
return TokenStream::from(quote! {
#[nidrs::macros::meta(version = #version)]
#input
});
}
#[proc_macro_attribute]
pub fn disable_default_prefix(args: TokenStream, input: TokenStream) -> TokenStream {
let raw_input = TokenStream2::from(input.clone());
return TokenStream::from(quote! {
#[nidrs::macros::meta(nidrs::datasets::DisableDefaultPrefix::from(true))]
#raw_input
});
}
#[proc_macro_attribute]
pub fn global(args: TokenStream, input: TokenStream) -> TokenStream {
let raw_input = TokenStream2::from(input.clone());
return TokenStream::from(quote! {
#[nidrs::macros::meta(nidrs::datasets::Global::from(true))]
#raw_input
});
}
#[proc_macro_attribute]
pub fn main(args: TokenStream, input: TokenStream) -> TokenStream {
cmeta::CMeta::pop();
let func = parse_macro_input!(input as ItemFn);
let ident = func.sig.ident.clone();
let import_mod_tokens = import_path::gen_import_mod_tokens();
let main_tokens = TokenStream2::from(quote! {
#func
#import_mod_tokens
});
return main_tokens.into();
}
#[proc_macro]
pub fn throw(input: TokenStream) -> TokenStream {
let input = TokenStream2::from(input);
let call_site = Span::call_site();
let binding = call_site.source_file().path();
let call_site_str = binding.to_string_lossy();
let call_site_line = call_site.start().line();
let expanded = quote! {
nidrs::__throw(#input, &format!("from {} line {}", #call_site_str, #call_site_line))?;
};
expanded.into()
}
#[proc_macro]
pub fn log(input: TokenStream) -> TokenStream {
let input = TokenStream2::from(input);
let input_tokens = input.into_iter().collect::<Vec<_>>();
return TokenStream::from(quote::quote! {
print!("{} ", nidrs_extern::colored::Colorize::green("[nidrs]"));
println!(#(#input_tokens)*);
});
}
#[proc_macro]
pub fn elog(input: TokenStream) -> TokenStream {
let input = TokenStream2::from(input);
let input_tokens = input.into_iter().collect::<Vec<_>>();
return TokenStream::from(quote::quote! {
eprint!("{} ", nidrs_extern::colored::Colorize::red("[nidrs]"));
eprintln!(#(#input_tokens)*);
});
}