1use std::path::Path;
2
3use specta::{functions::FunctionDataType, ts::TsExportError, ExportError, TypeDefs};
4
5use crate::ExportLanguage;
6
7pub mod internal {
12 use heck::ToLowerCamelCase;
13 use indoc::formatdoc;
14
15 use crate::DO_NOT_EDIT;
16 use specta::{
17 functions::FunctionDataType,
18 ts::{self, TsExportError},
19 TypeDefs,
20 };
21
22 pub fn globals() -> String {
24 formatdoc! {
25 r#"
26 declare global {{
27 interface Window {{
28 __TAURI_INVOKE__<T>(cmd: string, args?: Record<string, unknown>): Promise<T>;
29 }}
30 }}
31
32 // Function avoids 'window not defined' in SSR
33 const invoke = () => window.__TAURI_INVOKE__;"#
34 }
35 }
36
37 pub fn render_functions(
39 function_types: Vec<FunctionDataType>,
40 cfg: &specta::ts::ExportConfiguration,
41 ) -> Result<String, TsExportError> {
42 function_types
43 .into_iter()
44 .map(|function| {
45 let name = &function.name;
46 let name_camel = function.name.to_lower_camel_case();
47
48 let arg_defs = function
49 .args
50 .iter()
51 .map(|(name, typ)| {
52 ts::datatype(cfg, typ)
53 .map(|ty| format!("{}: {}", name.to_lower_camel_case(), ty))
54 })
55 .collect::<Result<Vec<_>, _>>()?
56 .join(", ");
57
58 let ret_type = ts::datatype(cfg, &function.result)?;
59
60 let arg_usages = function
61 .args
62 .iter()
63 .map(|(name, _)| name.to_lower_camel_case())
64 .collect::<Vec<_>>();
65
66 let arg_usages = arg_usages
67 .is_empty()
68 .then(Default::default)
69 .unwrap_or_else(|| format!(", {{ {} }}", arg_usages.join(",")));
70
71 let docs = specta::ts::js_doc(&function.docs);
72
73 Ok(formatdoc!(
74 r#"
75 {docs}export function {name_camel}({arg_defs}) {{
76 return invoke()<{ret_type}>("{name}"{arg_usages})
77 }}"#
78 ))
79 })
80 .collect::<Result<Vec<_>, _>>()
81 .map(|v| v.join("\n\n"))
82 }
83
84 pub fn render(
86 function_types: Vec<FunctionDataType>,
87 type_map: TypeDefs,
88 cfg: &specta::ts::ExportConfiguration,
89 ) -> Result<String, TsExportError> {
90 let globals = globals();
91
92 let functions = render_functions(function_types, cfg)?;
93
94 let dependant_types = type_map
95 .values()
96 .filter_map(|v| v.as_ref())
97 .map(|v| ts::export_datatype(cfg, v))
98 .collect::<Result<Vec<_>, _>>()
99 .map(|v| v.join("\n"))?;
100
101 Ok(formatdoc! {
102 r#"
103 {DO_NOT_EDIT}
104
105 {globals}
106
107 {functions}
108
109 {dependant_types}
110 "#
111 })
112 }
113}
114
115pub struct Language;
117
118pub type Exporter = crate::Exporter<Language>;
120
121impl ExportLanguage for Language {
122 fn globals() -> String {
123 internal::globals()
124 }
125
126 fn render_functions(
127 function_types: Vec<FunctionDataType>,
128 cfg: &specta::ts::ExportConfiguration,
129 ) -> Result<String, TsExportError> {
130 internal::render_functions(function_types, cfg)
131 }
132
133 fn render(
134 function_types: Vec<FunctionDataType>,
135 type_map: TypeDefs,
136 cfg: &specta::ts::ExportConfiguration,
137 ) -> Result<String, TsExportError> {
138 internal::render(function_types, type_map, cfg)
139 }
140}
141
142pub fn export_with_cfg(
145 result: (Vec<FunctionDataType>, TypeDefs),
146 cfg: specta::ts::ExportConfiguration,
147 export_path: impl AsRef<Path>,
148) -> Result<(), TsExportError> {
149 Exporter::new(Ok(result), export_path)
150 .with_cfg(cfg)
151 .export()
152}
153
154pub fn export(
156 macro_data: Result<(Vec<FunctionDataType>, TypeDefs), ExportError>,
157 export_path: impl AsRef<Path>,
158) -> Result<(), TsExportError> {
159 Exporter::new(macro_data, export_path).export()
160}