use proc::{Procedures, ProceduresGenerator, RpcMethod};
use proc_macro::{self, TokenStream};
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote, ToTokens};
use syn::{
ext::IdentExt, parse_macro_input, parse_quote, parse_quote_spanned, spanned::Spanned, Ident,
ImplItem, ImplItemFn, ImplItemType, ItemImpl, ItemStruct, Pat, PatType, ReturnType, Type,
};
mod attrs;
mod proc;
use once_cell::sync::Lazy;
use std::sync::Mutex;
use crate::attrs::ProceduresAttrs;
macro_rules! extend_errors {
($errors: ident, $e: expr) => {
match $errors {
Ok(_) => $errors = Err($e),
Err(ref mut errors) => errors.extend($e),
}
};
}
pub(crate) use extend_errors;
static STRUCT_NAMES: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(vec![]));
#[proc_macro_attribute]
pub fn rpc_struct(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemStruct);
STRUCT_NAMES.lock().unwrap().push(input.ident.to_string());
quote! {
#[derive(taurpc::Serialize, taurpc::Deserialize, taurpc::TS, Clone)]
#input
}
.into()
}
#[proc_macro_attribute]
pub fn procedures(attrs: TokenStream, item: TokenStream) -> TokenStream {
let procedures_attrs = parse_macro_input!(attrs as ProceduresAttrs);
let Procedures {
ref ident,
ref methods,
ref vis,
ref generics,
ref attrs,
} = parse_macro_input!(item as Procedures);
let struct_idents = STRUCT_NAMES.lock().unwrap();
let unit_type: &Type = &parse_quote!(());
ProceduresGenerator {
trait_ident: ident,
handler_ident: &format_ident!("TauRpc{}Handler", ident),
event_trigger_ident: &procedures_attrs
.event_trigger_ident
.unwrap_or(format_ident!("TauRpc{}EventTrigger", ident)),
inputs_ident: &format_ident!("TauRpc{}Inputs", ident),
outputs_ident: &format_ident!("TauRpc{}Outputs", ident),
output_types_ident: &format_ident!("TauRpc{}OutputTypes", ident),
outputs_futures_ident: &format_ident!("TauRpc{}OutputFutures", ident),
methods,
method_output_types: &methods
.iter()
.map(|RpcMethod { output, .. }| match output {
ReturnType::Type(_, ref ty) => ty,
ReturnType::Default => unit_type,
})
.collect::<Vec<_>>(),
method_names: &methods
.iter()
.map(|RpcMethod { ident, .. }| format_method_name(ident))
.collect::<Vec<_>>(),
struct_idents: &struct_idents
.iter()
.map(|name| format_ident!("{}", name))
.collect::<Vec<_>>(),
vis,
generics,
attrs,
}
.into_token_stream()
.into()
}
#[proc_macro_attribute]
pub fn resolvers(_attr: TokenStream, item: TokenStream) -> TokenStream {
let mut item = syn::parse_macro_input!(item as ItemImpl);
let mut types: Vec<ImplItemType> = Vec::new();
for inner in &mut item.items {
match inner {
ImplItem::Fn(method) => {
if method.sig.asyncness.is_some() {
types.push(transform_method(method));
}
}
_ => {}
}
}
for t in types.into_iter() {
item.items.push(syn::ImplItem::Type(t));
}
quote!(#item).into()
}
fn transform_method(method: &mut ImplItemFn) -> ImplItemType {
method.sig.asyncness = None;
let ret = match &method.sig.output {
ReturnType::Default => quote!(()),
ReturnType::Type(_, ret) => quote!(#ret),
};
let fut_ident = method_fut_ident(&method.sig.ident);
method.sig.output = parse_quote! {
-> ::core::pin::Pin<Box<
dyn ::core::future::Future<Output = #ret> + ::core::marker::Send
>>
};
let block = method.block.clone();
method.block = parse_quote_spanned! {method.span()=>{
Box::pin(async move #block)
}};
let t = parse_quote! {
type #fut_ident = ::core::pin::Pin<Box<dyn ::core::future::Future<Output = #ret> + ::core::marker::Send>>;
};
t
}
fn format_method_name(method: &Ident) -> Ident {
format_ident!("TauRPC__{}", method)
}
fn method_fut_ident(ident: &Ident) -> Ident {
format_ident!("{}Fut", ident)
}
pub(crate) fn parse_args(args: &Vec<PatType>, message: &Ident) -> syn::Result<Vec<TokenStream2>> {
args.iter().map(|arg| parse_arg(arg, message)).collect()
}
fn parse_arg(arg: &PatType, message: &Ident) -> syn::Result<TokenStream2> {
let key = parse_arg_key(arg)?;
if key == "self" {
return Err(syn::Error::new(
key.span(),
"unable to use self as a command function parameter",
));
}
Ok(quote!(::tauri::command::CommandArg::from_command(
::tauri::command::CommandItem {
name: "placeholder",
key: #key,
message: &#message
}
)))
}
pub(crate) fn parse_arg_key(arg: &PatType) -> Result<String, syn::Error> {
match &mut arg.pat.as_ref().clone() {
Pat::Ident(arg) => Ok(arg.ident.unraw().to_string()),
Pat::Wild(_) => Ok("".into()), Pat::Struct(s) => Ok(s.path.segments.last_mut().unwrap().ident.to_string()),
Pat::TupleStruct(s) => Ok(s.path.segments.last_mut().unwrap().ident.to_string()),
err => {
return Err(syn::Error::new(
err.span(),
"only named, wildcard, struct, and tuple struct arguments allowed",
))
}
}
}