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 };
20
21 pub fn globals() -> String {
23 formatdoc! {
24 r#"
25 // Late bind to avoid 'window not defined' in SSR
26 const invoke = () => window.__TAURI_INVOKE__;"#
27 }
28 }
29
30 pub fn render_functions(
32 function_types: Vec<FunctionDataType>,
33 cfg: &specta::ts::ExportConfiguration,
34 ) -> Result<String, TsExportError> {
35 function_types
36 .into_iter()
37 .map(|function| {
38 let name = &function.name;
39 let name_camel = function.name.to_lower_camel_case();
40
41 let arg_list = function
42 .args
43 .iter()
44 .map(|(name, _)| name.to_lower_camel_case())
45 .collect::<Vec<_>>();
46
47 let arg_defs = arg_list.join(", ");
48
49 let arg_usages = arg_list
50 .is_empty()
51 .then(Default::default)
52 .unwrap_or_else(|| format!(", {{ {} }}", arg_list.join(", ")));
53
54 let ret_type = ts::datatype(cfg, &function.result)?;
55
56 let jsdoc = {
57 let vec = []
58 .into_iter()
59 .chain(
60 function
61 .docs
62 .into_iter()
63 .map(str::to_owned)
64 .collect::<Vec<_>>(),
65 )
66 .chain(function.args.iter().flat_map(|(name, typ)| {
67 ts::datatype(cfg, typ).map(|typ| {
68 let name = name.to_lower_camel_case();
69
70 format!("@param {{ {typ} }} {name}")
71 })
72 }))
73 .chain([format!("@returns {{ Promise<{ret_type}> }}")])
74 .collect::<Vec<_>>();
75
76 specta::ts::js_doc(&vec.iter().map(|s| s.as_str()).collect::<Vec<_>>())
77 };
78
79 Ok(formatdoc! {
80 r#"
81 {jsdoc} export function {name_camel}({arg_defs}) {{
82 return invoke()("{name}"{arg_usages})
83 }}"#
84 })
85 })
86 .collect::<Result<Vec<_>, _>>()
87 .map(|v| v.join("\n\n"))
88 }
89
90 pub fn render(
92 function_types: Vec<FunctionDataType>,
93 cfg: &specta::ts::ExportConfiguration,
94 ) -> Result<String, TsExportError> {
95 let globals = globals();
96
97 let functions = render_functions(function_types, cfg)?;
98
99 Ok(formatdoc! {
100 r#"
101 {DO_NOT_EDIT}
102
103 {globals}
104
105 {functions}
106 "#
107 })
108 }
109}
110
111pub struct Language;
113
114pub type Exporter = crate::Exporter<Language>;
116
117impl ExportLanguage for Language {
118 fn globals() -> String {
119 internal::globals()
120 }
121
122 fn render_functions(
123 function_types: Vec<FunctionDataType>,
124 cfg: &specta::ts::ExportConfiguration,
125 ) -> Result<String, TsExportError> {
126 internal::render_functions(function_types, cfg)
127 }
128
129 fn render(
130 function_types: Vec<FunctionDataType>,
131 _: TypeDefs,
132 cfg: &specta::ts::ExportConfiguration,
133 ) -> Result<String, TsExportError> {
134 internal::render(function_types, cfg)
135 }
136}
137
138pub fn export_with_cfg(
141 result: (Vec<FunctionDataType>, TypeDefs),
142 export_path: impl AsRef<Path>,
143 cfg: specta::ts::ExportConfiguration,
144) -> Result<(), TsExportError> {
145 Exporter::new(Ok(result), export_path)
146 .with_cfg(cfg)
147 .export()
148}
149
150pub fn export(
152 macro_data: Result<(Vec<FunctionDataType>, TypeDefs), ExportError>,
153 export_path: impl AsRef<Path>,
154) -> Result<(), TsExportError> {
155 Exporter::new(macro_data, export_path).export()
156}