diff options
| -rw-r--r-- | crates/macros/Cargo.toml | 5 | ||||
| -rw-r--r-- | crates/macros/src/lib.rs | 159 | ||||
| -rw-r--r-- | src-tauri/Cargo.toml | 60 | ||||
| -rw-r--r-- | src-tauri/src/main.rs | 1 |
4 files changed, 115 insertions, 110 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 } } }; diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index cf92627..28d600a 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,47 +1,53 @@ [package] name = "dropout" version = "0.2.0-alpha.1" -edition = "2021" +edition = "2024" authors = ["HsiangNianian"] description = "The DropOut Minecraft Game Launcher" -license = "MIT" repository = "https://github.com/HydroRoll-Team/DropOut" +license = "MIT" publish = false +[package.metadata.deb] +depends = "libgtk-3-0" +section = "games" +assets = [ + ["target/release/dropout", "usr/bin/", "755"], +] + [dependencies] -dropout-macros = { version = "0.1.0", path = "../crates/macros" } -serde = { version = "1.0", features = ["derive"] } -toml = "0.5" -log = "0.4" +bytes = "1.11.0" +chrono = "0.4" +dirs = "5.0" +dropout-core = { path = "../crates/core", version = "0.1.0" } +dropout-macros = { path = "../crates/macros", version = "0.1.0" } env_logger = "0.9" -tokio = { version = "1.49.0", features = ["full"] } -reqwest = { version = "0.11", features = ["json", "blocking", "stream", "multipart"] } -serde_json = "1.0.149" -tauri = { version = "2.9", features = [] } -tauri-plugin-shell = "2.3" -uuid = { version = "1.10.0", features = ["v3", "v4", "serde"] } +flate2 = "1.0" futures = "0.3" +hex = "0.4" +log = "0.4" +regex = "1.12.2" +reqwest = { version = "0.11", features = [ + "blocking", + "json", + "multipart", + "stream" +] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.149" +serde_urlencoded = "0.7.1" sha1 = "0.10" sha2 = "0.10" -hex = "0.4" -zip = "2.2.2" -flate2 = "1.0" tar = "0.4" -dirs = "5.0" -serde_urlencoded = "0.7.1" +tauri = { version = "2.9", features = [] } tauri-plugin-dialog = "2.6.0" tauri-plugin-fs = "2.4.5" -bytes = "1.11.0" -chrono = "0.4" -regex = "1.12.2" +tauri-plugin-shell = "2.3" +tokio = { version = "1.49.0", features = ["full"] } +toml = "0.5" ts-rs = { version = "11.1.0", features = ["serde-compat"] } +uuid = { version = "1.10.0", features = ["serde", "v3", "v4"] } +zip = "2.2.2" [build-dependencies] tauri-build = { version = "2.0", features = [] } - -[package.metadata.deb] -depends = "libgtk-3-0" -section = "games" -assets = [ - ["target/release/dropout", "usr/bin/", "755"], -] diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index b74c746..f652012 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -896,6 +896,7 @@ fn parse_jvm_arguments( } #[tauri::command] +#[dropout_macros::api] async fn get_versions( _window: Window, instance_state: State<'_, core::instance::InstanceState>, |