aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src-tauri/src/utils/api.rs
blob: 0d5a925cfb2d89ae8ccdf7358179220588dbc8ae (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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");
}