aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src-tauri/src/utils/api.rs
diff options
context:
space:
mode:
author苏向夜 <46275354+fu050409@users.noreply.github.com>2026-02-25 02:06:07 +0800
committerGitHub <noreply@github.com>2026-02-25 02:06:07 +0800
commit78ac61904d78d558d092eff08c9f261cbdb187e8 (patch)
tree96f68d1f1554ee3a0532793afaaa52b0c73dcbec /src-tauri/src/utils/api.rs
parent8ff3af6cb908fd824b512379dd21ed4f595ab6bb (diff)
parent329734b23957b84cde2af459fa61c7385fb5b5f1 (diff)
downloadDropOut-78ac61904d78d558d092eff08c9f261cbdb187e8.tar.gz
DropOut-78ac61904d78d558d092eff08c9f261cbdb187e8.zip
feat(ui): partial react rewrite (#77)
## Summary by Sourcery Export backend data structures to TypeScript for the new React-based UI and update CI to build additional targets. New Features: - Generate TypeScript definitions for core backend structs and enums used by the UI. - Now use our own Azure app(_DropOut_) to finish the authorize process. Enhancements: - Annotate existing Rust models with ts-rs metadata to control exported TypeScript shapes, including tagged enums and opaque JSON fields. Build: - Add ts-rs as a dependency for generating TypeScript bindings from Rust types. CI: - Extend the Semifold CI workflow to run on the dev branch and build additional Linux musl and Windows GNU targets using cross where needed.
Diffstat (limited to 'src-tauri/src/utils/api.rs')
-rw-r--r--src-tauri/src/utils/api.rs95
1 files changed, 95 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..92e8ab9
--- /dev/null
+++ b/src-tauri/src/utils/api.rs
@@ -0,0 +1,95 @@
+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);
+
+fn sort_api_infos(api_infos: &mut [&ApiInfo]) {
+ api_infos.sort_by(|a, b| a.fn_name.cmp(b.fn_name));
+}
+
+pub fn export_api_bindings(import_from: &str, export_to: &str) {
+ use std::collections::BTreeMap;
+
+ let mut api_infos = inventory::iter::<ApiInfo>.into_iter().collect::<Vec<_>>();
+ if api_infos.is_empty() {
+ return;
+ }
+ sort_api_infos(&mut api_infos);
+
+ 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");
+}