diff options
Diffstat (limited to 'src-tauri/src/utils/api.rs')
| -rw-r--r-- | src-tauri/src/utils/api.rs | 90 |
1 files changed, 90 insertions, 0 deletions
diff --git a/src-tauri/src/utils/api.rs b/src-tauri/src/utils/api.rs new file mode 100644 index 0000000..0d5a925 --- /dev/null +++ b/src-tauri/src/utils/api.rs @@ -0,0 +1,90 @@ +use std::collections::BTreeSet; + +#[derive(Debug)] +pub struct ApiInfo { + pub fn_name: &'static str, + pub ts_fn_name: &'static str, + pub param_names: &'static [&'static str], + pub param_defs: &'static [&'static str], + pub return_ts_promise: &'static str, + pub import_types: &'static [&'static str], + pub import_from: Option<&'static str>, +} + +inventory::collect!(ApiInfo); + +pub fn export_api_bindings(import_from: &str, export_to: &str) { + use std::collections::BTreeMap; + + let api_infos = inventory::iter::<ApiInfo>.into_iter().collect::<Vec<_>>(); + if api_infos.is_empty() { + return; + } + + let mut ts_lines = Vec::new(); + ts_lines.push(r#"import { invoke } from "@tauri-apps/api/core""#.to_string()); + + let mut import_types: BTreeMap<&str, BTreeSet<&str>> = BTreeMap::new(); + let mut ts_funcs = Vec::new(); + for api_info in api_infos { + let api_types = api_info.import_types.iter().cloned().collect::<Vec<_>>(); + import_types + .entry(api_info.import_from.unwrap_or(import_from)) + .or_insert_with(BTreeSet::new) + .extend(api_types.clone()); + if api_types.contains(&"Vec") { + eprintln!("???? from {}", api_info.fn_name) + } + + // Determine return generic for invoke: need the raw type (not Promise<...>) + let invoke_generic = if api_info.return_ts_promise.starts_with("Promise<") + && api_info.return_ts_promise.ends_with('>') + { + &api_info.return_ts_promise["Promise<".len()..api_info.return_ts_promise.len() - 1] + } else { + "unknown" + }; + let invoke_line = if api_info.param_names.is_empty() { + format!("invoke<{}>(\"{}\")", invoke_generic, api_info.fn_name) + } else { + format!( + "invoke<{}>(\"{}\", {{\n {}\n }})", + invoke_generic, + api_info.fn_name, + api_info.param_names.join(", ") + ) + }; + + ts_funcs.push(format!( + "export function {}({}): {} {{\n \ + return {}\n\ + }}\n", + api_info.ts_fn_name, + api_info.param_defs.join(", "), + api_info.return_ts_promise, + invoke_line + )) + } + + for (import_from, import_types) in import_types { + ts_lines.push(format!( + "import type {{ {} }} from \"{}\"", + import_types.iter().cloned().collect::<Vec<_>>().join(", "), + import_from + )) + } + ts_lines.push("".to_string()); + ts_lines.extend(ts_funcs); + + let ts_content = ts_lines.join("\n"); + let export_to = std::path::Path::new(export_to); + if let Some(parent) = export_to.parent() { + std::fs::create_dir_all(parent).expect("Failed to create parent directory"); + } + std::fs::write(export_to, ts_content).unwrap(); +} + +#[ctor::dtor] +fn __dropout_export_api_bindings() { + export_api_bindings("@/types", "../packages/ui-new/src/client.ts"); +} |