aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/crates/macros
diff options
context:
space:
mode:
author苏向夜 <fu050409@163.com>2026-02-15 15:34:40 +0800
committer苏向夜 <fu050409@163.com>2026-02-15 15:34:40 +0800
commit2c3de3ac5ab1ab59f7245ab9cbdfda9b4e96dcb0 (patch)
treed7d11f358674ac7c875ac8b2d1dd728a8a2f9451 /crates/macros
parente0dd58c443ed4d13e69b511bb01e17922926076f (diff)
downloadDropOut-2c3de3ac5ab1ab59f7245ab9cbdfda9b4e96dcb0.tar.gz
DropOut-2c3de3ac5ab1ab59f7245ab9cbdfda9b4e96dcb0.zip
merge: dev
Diffstat (limited to 'crates/macros')
-rw-r--r--crates/macros/Cargo.toml5
-rw-r--r--crates/macros/src/lib.rs159
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
}
}
};