use proc_macro::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::{
Item::{Fn, Mod, Struct},
__private::Span,
parse_macro_input,
token::Brace,
};
mod build;
mod build_command;
mod build_nested;
mod checks;
mod return_token;
macro_rules! callback {
($inp:expr) => {{
let mut cmd = $inp.to_string();
cmd = cmd
.replace(";", "")
.replace(" ", "")
.replace("\n", "")
.replace("(", "")
.replace(")", "")
.replace("::", " ")
.replace(",", " ");
std::thread::spawn(move || {
let output = std::process::Command::new(cmd)
.output()
.expect("Failed to execute command");
eprintln!("{}", std::string::String::from_utf8_lossy(&output.stdout));
});
}};
}
macro_rules! parse {
($inp:expr) => {
let mut path = $inp.split("::").collect();
let mut module = path.last();
module = module[0].uppercase() + module[1..].to_string();
path.push(&module);
let path = path.join("::");
path!($path).delegate();
};
}
#[proc_macro_attribute]
pub fn crpc(attr: TokenStream, item: TokenStream) -> TokenStream {
let ts_item = item.clone();
let item = syn::parse_macro_input!(item as syn::Item);
match item {
Fn(item) if attr.to_string() == "fn" => {
quote! {
#[crpc_fn]
#item
}
.into()
}
Mod(item) if attr.to_string() == "mod" => {
quote! {
#[crpc_mod]
#item
}
.into()
}
Struct(item) if attr.to_string() == "struct" => {
quote! {
#[crpc_param]
#item
}
.into()
}
_ => {
eprint!(
"Error in {:?}: crpc can only be used on fn, mod and struct",
ts_item.to_string()
);
ts_item
}
}
}
#[proc_macro_attribute]
pub fn crpc_main(_attr: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as syn::Item);
if let Mod(mut item) = item {
if let syn::Visibility::Public(_) = item.vis {
let (name, sc_name) = build_nested::names(item.ident.to_string());
let subcommands = build_nested::subcommands_with_help(item.clone());
let sc_enum = build_nested::subcommand_enum(
&name.to_string()[..name.to_string().len()].to_string(),
subcommands.clone(),
sc_name.clone(),
);
let sc_match = build_nested::delegate_match_expr(subcommands.clone(), &sc_name);
let (_, mut _body) = item.content.clone().unwrap();
_body.insert(0, build::item_use());
item.content = Some((Brace(Span::call_site()), _body));
return_token::main_token(name, sc_name, sc_enum, sc_match, item.clone())
} else {
eprintln!(
"An item marked with #[crpc_main] must be public and accessible to the binary."
);
return item.to_token_stream().into();
}
} else {
eprintln!("An item marked with #[crpc_main] must be a module.");
return item.to_token_stream().into();
}
}
#[proc_macro_attribute]
pub fn crpc_mod(_attr: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as syn::Item);
if let Mod(mut item) = item {
if let syn::Visibility::Public(_) = item.vis {
let (name, sc_name) = build_nested::names(item.ident.to_string());
let subcommands = build_nested::subcommands_with_help(item.clone());
let sc_enum = build_nested::subcommand_enum(
&name.to_string()[..name.to_string().len()].to_string(),
subcommands.clone(),
sc_name.clone(),
);
let sc_match = build_nested::delegate_match_expr(subcommands.clone(), &sc_name);
let (_, mut _body) = item.content.clone().unwrap();
_body.insert(0, build::item_use());
item.content = Some((Brace(Span::call_site()), _body));
return_token::mod_token(name, sc_name, sc_enum, sc_match, item.clone())
} else {
eprintln!(
"An item marked with #[crpc_main] must be public and accessible to the binary."
);
return item.to_token_stream().into();
}
} else {
eprintln!("An item marked with #[crpc_main] must be a module.");
return item.to_token_stream().into();
}
}
#[proc_macro_attribute]
pub fn crpc_fn(_attr: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as syn::Item);
if let Fn(item) = &item {
if let syn::Visibility::Public(_) = item.vis {
checks::output_check(&item.sig.output);
let name_struct = format_ident!("{}", build::bigger(&item.sig.ident.to_string()));
let new_function = build_command::to_impl_item(item.clone());
let fields = build_command::fields(item.clone());
let struct_item = build_command::to_struct(name_struct.clone(), fields);
return_token::fn_token(name_struct, struct_item, new_function)
} else {
eprintln!("An item marked with #[crpc_fn] must be public.");
return item.to_token_stream().into();
}
} else {
eprintln!("An item marked with #[crpc_fn] must be a function.");
return item.to_token_stream().into();
}
}