use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{ItemStruct, Path};
pub struct CommandOptions {
pub result_type: Option<Path>,
pub custom_serialize: bool,
}
fn make_args_struct(
input: &ItemStruct,
name: &syn::Ident,
serde_path: &TokenStream,
krate: &syn::Path,
serde_rename_attr: &TokenStream,
) -> ItemStruct {
let mut args = input.clone();
args.ident = name.clone();
args.attrs = vec![
syn::parse_quote!(#[derive(Clone, Debug, #serde_path::Serialize, #serde_path::Deserialize)]),
syn::parse_quote!(#[derive(#krate::TS)]),
syn::parse_quote!(#[ts(crate = "myko::ts_rs")]),
syn::parse_quote!(#serde_rename_attr),
];
args
}
pub fn myko_command_impl(options: CommandOptions, mut input_struct: ItemStruct) -> TokenStream {
let CommandOptions {
result_type,
custom_serialize,
} = options;
let struct_name = &input_struct.ident;
let args_struct_name = format_ident!("{}Args", struct_name);
let ctx = crate::DeriveCtx::new();
let krate = &ctx.krate;
let serde_path = &ctx.serde_path;
let serde_rename_attr = ctx.serde_attr("e!(rename_all = "camelCase"));
let (description_tokens, args_tokens) = crate::operation_metadata_tokens(&input_struct, krate);
crate::gate_ts_attrs(&mut input_struct.attrs);
crate::gate_field_ts_attrs(&mut input_struct.fields);
let ts_cfg_derive = quote!(#[derive(#krate::TS)] #[ts(crate = "myko::ts_rs")]);
let serde_rename_attr_clone = ctx.serde_attr("e!(rename_all = "camelCase"));
let args_struct = make_args_struct(
&input_struct,
&args_struct_name,
serde_path,
krate,
&serde_rename_attr_clone,
);
let result_type_tokens = result_type
.as_ref()
.map_or_else(|| quote! { () }, |path| quote! { #path });
let result_type_str = result_type
.as_ref()
.map_or_else(|| "()".to_string(), |path| quote!(#path).to_string());
let derives = if custom_serialize {
quote! {
#[derive(Clone, Debug, #serde_path::Deserialize)]
#ts_cfg_derive
#serde_rename_attr
}
} else {
quote! {
#[derive(Clone, Debug, #serde_path::Serialize, #serde_path::Deserialize)]
#ts_cfg_derive
#serde_rename_attr
}
};
let pairs = input_struct
.fields
.iter()
.filter_map(|f| {
let f_name = f.ident.as_ref()?;
Some(quote! {#f_name: args.#f_name,})
})
.collect::<Vec<_>>();
let expanded = quote! {
#derives
#input_struct
#args_struct
impl #krate::command::CommandId for &#struct_name {
fn command_id(&self) -> std::sync::Arc<str> {
stringify!(#struct_name).into()
}
}
impl #krate::command::CommandId for #struct_name {
fn command_id(&self) -> std::sync::Arc<str> {
stringify!(#struct_name).into()
}
}
impl #krate::command::CommandIdStatic for #struct_name {
const COMMAND_ID: &'static str = stringify!(#struct_name);
}
impl #krate::command::CommandResultType for #struct_name {
type Result = #result_type_tokens;
}
impl #struct_name {
pub fn new(args: #args_struct_name) -> Self {
Self {
#(#pairs)*
}
}
pub fn handle(
&self,
client: &#krate::prelude::MykoClient,
) -> #krate::hyphae::Cell<Option<Result<#result_type_tokens, String>>, #krate::hyphae::CellImmutable> {
client.send_command::<#struct_name, #result_type_tokens>(self)
}
}
#[cfg(not(target_arch = "wasm32"))]
#krate::submit! {
#krate::command::CommandRegistration {
command_id: stringify!(#struct_name),
result_type: #result_type_str,
result_type_crate: module_path!(),
crate_name: module_path!(),
args: #args_tokens,
description: #description_tokens,
}
}
#[cfg(not(target_arch = "wasm32"))]
#krate::register_command_handler!(#struct_name);
#krate::register_typegen_type!(#struct_name, #args_struct_name);
};
expanded
}