diff options
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/macros/Cargo.toml | 5 | ||||
| -rw-r--r-- | crates/macros/src/lib.rs | 159 |
2 files changed, 81 insertions, 83 deletions
diff --git a/crates/macros/Cargo.toml b/crates/macros/Cargo.toml index e5b9ad7..729b557 100644 --- a/crates/macros/Cargo.toml +++ b/crates/macros/Cargo.toml @@ -10,8 +10,9 @@ publish = false proc-macro = true [dependencies] +darling = { version = "0.23.0", features = ["serde"] } +heck = "0.4" +inventory = "0.3" proc-macro2 = "1.0" quote = "1.0" syn = { version = "2.0", features = ["full"] } -heck = "0.4" -darling = { version = "0.23.0", features = ["serde"] } diff --git a/crates/macros/src/lib.rs b/crates/macros/src/lib.rs index 9e310ca..16db1a9 100644 --- a/crates/macros/src/lib.rs +++ b/crates/macros/src/lib.rs @@ -1,3 +1,4 @@ +use heck::ToLowerCamelCase; use proc_macro::TokenStream; use quote::quote; use std::collections::BTreeSet; @@ -24,7 +25,7 @@ fn get_lit_str_value(nv: &MetaNameValue) -> Option<String> { } } -fn is_state_or_window(ty: &Type) -> bool { +fn is_tauri_native(ty: &Type) -> bool { // Unwrap reference let mut t = ty; if let Type::Reference(r) = t { @@ -112,7 +113,7 @@ fn rust_type_to_ts(ty: &Type) -> (String, bool) { let (inner_ts, inner_struct) = rust_type_to_ts(inner); return (format!("{}[]", inner_ts), inner_struct); } - ("any[]".to_string(), false) + ("unknown[]".to_string(), false) } other => { // treat as struct/complex type @@ -121,7 +122,7 @@ fn rust_type_to_ts(ty: &Type) -> (String, bool) { }; } } - ("any".to_string(), false) + ("unknown".to_string(), false) } fn get_return_ts(ty: &ReturnType) -> (String, BTreeSet<String>) { @@ -158,30 +159,11 @@ fn get_return_ts(ty: &ReturnType) -> (String, BTreeSet<String>) { } } // fallback - ("Promise<any>".to_string(), imports) + ("Promise<unknown>".to_string(), imports) } } } -fn snake_to_camel(s: &str) -> String { - let mut parts = s.split('_'); - let mut out = String::new(); - if let Some(first) = parts.next() { - out.push_str(&first.to_ascii_lowercase()); - } - for p in parts { - if p.is_empty() { - continue; - } - let mut chs = p.chars(); - if let Some(c) = chs.next() { - out.push_str(&c.to_ascii_uppercase().to_string()); - out.push_str(&chs.as_str().to_ascii_lowercase()); - } - } - out -} - #[proc_macro_attribute] pub fn api(attr: TokenStream, item: TokenStream) -> TokenStream { // Parse attribute args via `darling` crate @@ -202,7 +184,7 @@ pub fn api(attr: TokenStream, item: TokenStream) -> TokenStream { // Analyze function let fn_name_ident: Ident = input_fn.sig.ident.clone(); let fn_name = fn_name_ident.to_string(); - let ts_fn_name = snake_to_camel(&fn_name); + let ts_fn_name = fn_name.to_lower_camel_case(); // Collect parameters (ignore State/Window) let mut param_names: Vec<String> = Vec::new(); @@ -225,7 +207,7 @@ pub fn api(attr: TokenStream, item: TokenStream) -> TokenStream { }; // Check if type should be ignored (State, Window) - if is_state_or_window(&*pt.ty) { + if is_tauri_native(&*pt.ty) { continue; } @@ -240,7 +222,7 @@ pub fn api(attr: TokenStream, item: TokenStream) -> TokenStream { if let Some(pn) = param_ident { // Convert param name to camelCase - keep existing but ensure camelCase for TS // We'll convert snake_case param names to camelCase - let ts_param_name = snake_to_camel(&pn); + let ts_param_name = pn.to_lower_camel_case(); param_names.push(ts_param_name.clone()); param_defs.push(format!("{}: {}", ts_param_name, ts_type)); } @@ -255,21 +237,21 @@ pub fn api(attr: TokenStream, item: TokenStream) -> TokenStream { } // Build TypeScript code string - let mut ts_lines: Vec<String> = Vec::new(); - - ts_lines.push(r#"import { invoke } from "@tauri-apps/api/core""#.to_string()); - - if !import_types.is_empty() { - if let Some(import_from_str) = import_from.clone() { - let types_joined = import_types.iter().cloned().collect::<Vec<_>>().join(", "); - ts_lines.push(format!( - "import {{ {} }} from \"{}\"", - types_joined, import_from_str - )); - } else { - // If no import_from provided, still import types from local path? We'll skip if not provided. - } - } + // let mut ts_lines: Vec<String> = Vec::new(); + + // ts_lines.push(r#"import { invoke } from "@tauri-apps/api/core""#.to_string()); + + // if !import_types.is_empty() { + // if let Some(import_from_str) = import_from.clone() { + // let types_joined = import_types.iter().cloned().collect::<Vec<_>>().join(", "); + // ts_lines.push(format!( + // "import {{ {} }} from \"{}\"", + // types_joined, import_from_str + // )); + // } else { + // // If no import_from provided, still import types from local path? We'll skip if not provided. + // } + // } // function signature let params_sig = param_defs.join(", "); @@ -285,7 +267,7 @@ pub fn api(attr: TokenStream, item: TokenStream) -> TokenStream { if return_ts_promise.starts_with("Promise<") && return_ts_promise.ends_with('>') { &return_ts_promise["Promise<".len()..return_ts_promise.len() - 1] } else { - "any" + "unknown" }; let invoke_line = if param_names.is_empty() { @@ -297,14 +279,14 @@ pub fn api(attr: TokenStream, item: TokenStream) -> TokenStream { ) }; - ts_lines.push(format!( - "export async function {}({}): {} {{", - ts_fn_name, params_sig, return_ts_promise - )); - ts_lines.push(invoke_line); - ts_lines.push("}".to_string()); + // ts_lines.push(format!( + // "export async function {}({}): {} {{", + // ts_fn_name, params_sig, return_ts_promise + // )); + // ts_lines.push(invoke_line); + // ts_lines.push("}".to_string()); - let ts_contents = ts_lines.join("\n") + "\n"; + // let ts_contents = ts_lines.join("\n") + "\n"; // Prepare test function name let test_fn_name = Ident::new( @@ -320,38 +302,52 @@ pub fn api(attr: TokenStream, item: TokenStream) -> TokenStream { // Build tokens let original_fn = &input_fn; - let ts_string_literal = ts_contents.clone(); - - let write_stmt = if export_to_literal.is_empty() { - // No-op: don't write - // quote! { - // // No export_to provided; skipping file write. - // } - panic!("No export_to provided") - } else { - // We'll append to the file to avoid overwriting existing bindings from other macros. - // Use create(true).append(true) - let path = export_to_literal.clone(); - let ts_lit = syn::LitStr::new(&ts_string_literal, proc_macro2::Span::call_site()); - quote! { - { - // Ensure parent directories exist if possible (best-effort) - let path = std::path::Path::new(#path); - if let Some(parent) = path.parent() { - let _ = std::fs::create_dir_all(parent); - } - // Append generated bindings to file - match OpenOptions::new().create(true).append(true).open(path) { - Ok(mut f) => { - let _ = f.write_all(#ts_lit.as_bytes()); - println!("Successfully wrote to {}", path.display()); - } - Err(e) => { - eprintln!("dropout::api binding write failed: {}", e); - } - } - } + // let ts_string_literal = ts_contents.clone(); + + // let write_stmt = if export_to_literal.is_empty() { + // // No-op: don't write + // // quote! { + // // // No export_to provided; skipping file write. + // // } + // panic!("No export_to provided") + // } else { + // // We'll append to the file to avoid overwriting existing bindings from other macros. + // // Use create(true).append(true) + // let path = export_to_literal.clone(); + // let ts_lit = syn::LitStr::new(&ts_string_literal, proc_macro2::Span::call_site()); + // quote! { + // { + // // Ensure parent directories exist if possible (best-effort) + // let path = std::path::Path::new(#path); + // if let Some(parent) = path.parent() { + // let _ = std::fs::create_dir_all(parent); + // } + // // Append generated bindings to file + // match OpenOptions::new().create(true).append(true).open(path) { + // Ok(mut f) => { + // let _ = f.write_all(#ts_lit.as_bytes()); + // println!("Successfully wrote to {}", path.display()); + // } + // Err(e) => { + // eprintln!("dropout::api binding write failed: {}", e); + // } + // } + // } + // } + // }; + let register_stmt = quote! { + ::dropout_core::inventory::submit! { + ::dropout_core::ApiInfo { + fn_name: #fn_name, + ts_fn_name: #ts_fn_name, + param_names: vec![#(#param_names),*], + param_defs: vec![#(#param_defs),*], + return_ts_promise: #return_ts_promise, + import_types: BTreeSet::from([#(#import_types),*]), + import_from: #import_from, + export_to: #export_to_literal, } + } }; let gen = quote! { @@ -366,7 +362,8 @@ pub fn api(attr: TokenStream, item: TokenStream) -> TokenStream { #[test] fn #test_fn_name() { // Generated TypeScript bindings for function: #fn_name - #write_stmt + // #write_stmt + #register_stmt } } }; |