From d4bea99f11613beccb1addef5bf35eed46b686f6 Mon Sep 17 00:00:00 2001 From: Greg Johnston Date: Wed, 10 Jan 2024 08:03:37 -0500 Subject: [PATCH] allow type paths for input/output, and properly namespace built-in encodings --- examples/todo_app_sqlite/Cargo.toml | 1 - examples/todo_app_sqlite/src/todo.rs | 9 +-- server_fn_macro/src/lib.rs | 102 +++++++++++++++++++-------- 3 files changed, 77 insertions(+), 35 deletions(-) diff --git a/examples/todo_app_sqlite/Cargo.toml b/examples/todo_app_sqlite/Cargo.toml index c5d2ccdf64..f3977ca2c4 100644 --- a/examples/todo_app_sqlite/Cargo.toml +++ b/examples/todo_app_sqlite/Cargo.toml @@ -20,7 +20,6 @@ leptos = { path = "../../leptos", features = ["nightly"] } leptos_actix = { path = "../../integrations/actix", optional = true } leptos_meta = { path = "../../meta", features = ["nightly"] } leptos_router = { path = "../../router", features = ["nightly"] } -server_fn = { path = "../../server_fn", features = ["cbor"] } log = "0.4.17" simple_logger = "4.0.0" gloo = { git = "https://github.com/rustwasm/gloo" } diff --git a/examples/todo_app_sqlite/src/todo.rs b/examples/todo_app_sqlite/src/todo.rs index 1f9d2ca18d..c9b9a90460 100644 --- a/examples/todo_app_sqlite/src/todo.rs +++ b/examples/todo_app_sqlite/src/todo.rs @@ -22,7 +22,9 @@ cfg_if! { } } -#[server(GetTodos, "/api")] +// This is an example of leptos's server functions using an alternative CBOR encoding. Both the function arguments being sent +// to the server and the server response will be encoded with CBOR. Good for binary data that doesn't encode well via the default methods +#[server(encoding = "Cbor")] pub async fn get_todos() -> Result, ServerFnError> { // this is just an example of how to access server context injected in the handlers let req = use_context::(); @@ -43,9 +45,8 @@ pub async fn get_todos() -> Result, ServerFnError> { Ok(todos) } -// This is an example of leptos's server functions using an alternative CBOR encoding. Both the function arguments being sent -// to the server and the server response will be encoded with CBOR. Good for binary data that doesn't encode well via the default methods -#[server(AddTodo, "/api", "Cbor")] + +#[server] pub async fn add_todo(title: String) -> Result<(), ServerFnError> { let mut conn = db().await?; diff --git a/server_fn_macro/src/lib.rs b/server_fn_macro/src/lib.rs index e8c9492fd8..92b574ab48 100644 --- a/server_fn_macro/src/lib.rs +++ b/server_fn_macro/src/lib.rs @@ -13,7 +13,7 @@ use syn::{ parse::{Parse, ParseStream}, punctuated::Punctuated, spanned::Spanned, - *, + Type, *, }; /// The implementation of the `server_fn` macro. @@ -71,23 +71,43 @@ pub fn server_macro_impl( input, output, fn_path, + builtin_encoding, } = args; let prefix = prefix.unwrap_or_else(|| Literal::string(default_path)); let fn_path = fn_path.unwrap_or_else(|| Literal::string("")); - let input_ident = input - .as_ref() - .map(ToString::to_string) - .unwrap_or_else(|| "PostUrl".to_string()); - let input = input.map(|n| n.to_token_stream()).unwrap_or_else(|| { - quote! { - #server_fn_path::codec::PostUrl - } - }); - let output = output.map(|n| n.to_token_stream()).unwrap_or_else(|| { - quote! { - #server_fn_path::codec::Json + let input_ident = match &input { + Some(Type::Path(path)) => { + path.path.segments.last().map(|seg| seg.ident.to_string()) } - }); + None => Some("PostUrl".to_string()), + _ => None, + }; + let input = input + .map(|n| { + if builtin_encoding { + quote! { #server_fn_path::codec::#n } + } else { + n.to_token_stream() + } + }) + .unwrap_or_else(|| { + quote! { + #server_fn_path::codec::PostUrl + } + }); + let output = output + .map(|n| { + if builtin_encoding { + quote! { #server_fn_path::codec::#n } + } else { + n.to_token_stream() + } + }) + .unwrap_or_else(|| { + quote! { + #server_fn_path::codec::Json + } + }); // default to PascalCase version of function name if no struct name given let struct_name = struct_name.unwrap_or_else(|| { let upper_camel_case_name = Converter::new() @@ -316,10 +336,10 @@ pub fn server_macro_impl( } }; - let (is_serde, derives) = match input_ident.as_str() { - "Rkyv" => todo!("implement derives for Rkyv"), - "MultipartFormData" => (false, quote! {}), - "SerdeLite" => ( + let (is_serde, derives) = match input_ident.as_deref() { + Some("Rkyv") => todo!("implement derives for Rkyv"), + Some("MultipartFormData") => (false, quote! {}), + Some("SerdeLite") => ( true, quote! { Clone, #server_fn_path::serde_lite::Serialize, #server_fn_path::serde_lite::Deserialize @@ -470,6 +490,21 @@ pub fn server_macro_impl( }) } +fn type_from_ident(ident: Ident) -> Type { + let mut segments = Punctuated::new(); + segments.push(PathSegment { + ident, + arguments: PathArguments::None, + }); + Type::Path(TypePath { + qself: None, + path: Path { + leading_colon: None, + segments, + }, + }) +} + #[derive(Debug)] struct Middleware { expr: syn::Expr, @@ -555,9 +590,10 @@ fn err_type(return_ty: &Type) -> Result> { struct ServerFnArgs { struct_name: Option, prefix: Option, - input: Option, - output: Option, + input: Option, + output: Option, fn_path: Option, + builtin_encoding: bool, } impl Parse for ServerFnArgs { @@ -569,8 +605,8 @@ impl Parse for ServerFnArgs { let mut fn_path: Option = None; // new arguments: can only be keyed by name - let mut input: Option = None; - let mut output: Option = None; + let mut input: Option = None; + let mut output: Option = None; let mut use_key_and_value = false; let mut arg_pos = 0; @@ -698,23 +734,28 @@ impl Parse for ServerFnArgs { } // parse legacy encoding into input/output + let mut builtin_encoding = false; if let Some(encoding) = encoding { match encoding.to_string().to_lowercase().as_str() { "\"url\"" => { - input = syn::parse_quote!(PostUrl); - output = syn::parse_quote!(Json); + input = Some(type_from_ident(syn::parse_quote!(PostUrl))); + output = Some(type_from_ident(syn::parse_quote!(Json))); + builtin_encoding = true; } "\"cbor\"" => { - input = syn::parse_quote!(Cbor); - output = syn::parse_quote!(Cbor); + input = Some(type_from_ident(syn::parse_quote!(Cbor))); + output = Some(type_from_ident(syn::parse_quote!(Cbor))); + builtin_encoding = true; } "\"getcbor\"" => { - input = syn::parse_quote!(GetUrl); - output = syn::parse_quote!(Cbor); + input = Some(type_from_ident(syn::parse_quote!(GetUrl))); + output = Some(type_from_ident(syn::parse_quote!(Cbor))); + builtin_encoding = true; } "\"getjson\"" => { - input = syn::parse_quote!(GetUrl); - output = syn::parse_quote!(Json); + input = Some(type_from_ident(syn::parse_quote!(GetUrl))); + output = Some(type_from_ident(syn::parse_quote!(Json))); + builtin_encoding = true; } _ => { return Err(syn::Error::new( @@ -731,6 +772,7 @@ impl Parse for ServerFnArgs { input, output, fn_path, + builtin_encoding, }) } }