1use std::collections::BTreeSet;
2
3use crate::util::*;
4use crate::{ClassDefs, FfiType, TsModule};
5use dodo_bindgen_rust_ir::{Abi, Type, TypeNumeric};
6use proc_macro2::TokenStream;
7use syn::Ident;
8
9#[derive(Clone, Debug, PartialEq)]
14pub enum RustTypeNumeric {
15 U8,
16 U16,
17 U32,
18 I8,
19 I16,
20 I32,
21 F32,
22 F64,
23 U64,
24 I64,
25 Usize,
26 Isize,
27}
28
29#[derive(Clone, Debug, PartialEq)]
30pub enum RustType {
31 Void,
32 Numeric(RustTypeNumeric),
33 Boolean,
34 FnPtr(String), Ptr(Box<RustType>),
36 PtrMut(Box<RustType>),
37 Ref(Box<RustType>),
38 RefMut(Box<RustType>),
39 Box(Box<RustType>),
40 Str,
41 Slice(Box<RustType>),
42 Vec(Box<RustType>),
43 Tuple(Vec<RustType>),
44 UserDefined(Ident),
45 Unsupported, Custom {
47 ts_type: String,
48 to_rust: String,
49 from_rust: String,
50 },
51}
52
53fn numeric(type_numeric: TypeNumeric) -> RustTypeNumeric {
54 match type_numeric {
55 TypeNumeric::U8 => RustTypeNumeric::U8,
56 TypeNumeric::U16 => RustTypeNumeric::U16,
57 TypeNumeric::U32 => RustTypeNumeric::U32,
58 TypeNumeric::I8 => RustTypeNumeric::I8,
59 TypeNumeric::I16 => RustTypeNumeric::I16,
60 TypeNumeric::I32 => RustTypeNumeric::I32,
61 TypeNumeric::F32 => RustTypeNumeric::F32,
62 TypeNumeric::F64 => RustTypeNumeric::F64,
63 TypeNumeric::U64 => RustTypeNumeric::U64,
64 TypeNumeric::I64 => RustTypeNumeric::I64,
65 TypeNumeric::Usize => RustTypeNumeric::Usize,
66 TypeNumeric::Isize => RustTypeNumeric::Isize,
67 }
68}
69
70pub trait TypeExt {
75 fn unwrap(self, module: &mut TsModule) -> RustType;
76}
77
78impl TypeExt for Type {
79 fn unwrap(self, module: &mut TsModule) -> RustType {
80 fn match_str_or_slice(
81 elem: Box<Type>,
82 module: &mut TsModule,
83 fallback: &dyn Fn(Box<Type>, &mut TsModule) -> RustType,
84 ) -> RustType {
85 match *elem {
86 Type::Str => {
87 module.type_defs.insert(RustTypeList::Str);
88 RustType::Str
89 },
90 Type::Slice(elem) => {
91 module.type_defs.insert(RustTypeList::Slice);
92 let elem = Box::new(elem.unwrap(module));
93 RustType::Slice(elem)
94 },
95 _ => fallback(elem, module),
96 }
97 }
98
99 if let Some(class) = module.class_types.get(&self) {
100 return RustType::UserDefined(class.clone());
101 }
102
103 let type_defs = &mut module.type_defs;
104 match self {
105 Type::Void => RustType::Void,
106 Type::Numeric(type_numeric) => RustType::Numeric(numeric(type_numeric)),
107 Type::Bool => RustType::Boolean,
108 fn_ptr @ Type::FnPtr { .. } => {
109 type_defs.insert(RustTypeList::FnPtr);
110 RustType::FnPtr(fn_ptr.to_string())
111 },
112 Type::Ptr(elem) => match_str_or_slice(elem, module, &|elem, module| {
113 module.type_defs.insert(RustTypeList::Ptr);
114 RustType::Ptr(Box::new(elem.unwrap(module)))
115 }),
116 Type::PtrMut(elem) => match_str_or_slice(elem, module, &|elem, module| {
117 module.type_defs.insert(RustTypeList::PtrMut);
118 RustType::PtrMut(Box::new(elem.unwrap(module)))
119 }),
120 Type::Ref(elem) => match_str_or_slice(elem, module, &|elem, module| {
121 module.type_defs.insert(RustTypeList::Ref);
122 RustType::Ref(Box::new(elem.unwrap(module)))
123 }),
124 Type::RefMut(elem) => match_str_or_slice(elem, module, &|elem, module| {
125 module.type_defs.insert(RustTypeList::RefMut);
126 RustType::RefMut(Box::new(elem.unwrap(module)))
127 }),
128 Type::Box(elem) => match_str_or_slice(elem, module, &|elem, module| {
129 module.type_defs.insert(RustTypeList::Box);
130 RustType::Box(Box::new(elem.unwrap(module)))
131 }),
132 Type::Str => {
133 type_defs.insert(RustTypeList::Str);
134 RustType::Str
135 },
136 Type::Slice(elem) => {
137 type_defs.insert(RustTypeList::Slice);
138 RustType::Slice(Box::new(elem.unwrap(module)))
139 },
140 Type::Vec(elem) => {
141 type_defs.insert(RustTypeList::Vec);
142 RustType::Vec(Box::new(elem.unwrap(module)))
143 },
144 Type::Tuple(elems) => {
145 type_defs.insert(RustTypeList::Tuple);
146 RustType::Tuple(elems.into_iter().map(|elem| elem.unwrap(module)).collect())
147 },
148 Type::Object { name, .. } => {
149 let ident = format_ident!("{}", name);
150 module.user_defs.insert(ident.clone());
151 RustType::UserDefined(ident)
152 },
153 Type::Custom {
154 ts_type,
155 to_rust,
156 from_rust,
157 helper,
158 } => {
159 if !helper.is_empty() {
160 module.used_helpers.insert(helper);
161 }
162 RustType::Custom {
163 ts_type,
164 to_rust,
165 from_rust,
166 }
167 },
168 Type::String | Type::Array { .. } | Type::Opaque => {
169 type_defs.insert(RustTypeList::Unsupported);
170 RustType::Unsupported
171 },
172 }
173 }
174}
175
176impl From<Abi> for FfiType {
177 fn from(abi: Abi) -> Self {
178 match abi {
179 Abi::Void => FfiType::Void,
180 Abi::U8 => FfiType::U8,
181 Abi::U16 => FfiType::U16,
182 Abi::U32 => FfiType::U32,
183 Abi::U64 => FfiType::U64,
184 Abi::Usize => FfiType::Usize,
185 Abi::I8 => FfiType::I8,
186 Abi::I16 => FfiType::I16,
187 Abi::I32 => FfiType::I32,
188 Abi::I64 => FfiType::I64,
189 Abi::Isize => FfiType::Isize,
190 Abi::F32 => FfiType::F32,
191 Abi::F64 => FfiType::F64,
192 Abi::Bool => FfiType::Bool,
193 Abi::Pointer => FfiType::Pointer,
194 Abi::Buffer => FfiType::Buffer,
195 Abi::Function => FfiType::FnPointer,
196 }
197 }
198}
199
200#[cfg(test)]
205mod transform_tests {
206 use super::*;
207
208 #[test]
209 fn test_ts_type() {
210 let mut module = TsModule::default();
211 assert_eq!(Type::Void.unwrap(&mut module), RustType::Void);
212 assert_eq!(
213 Type::Ref(Box::new(Type::Str)).unwrap(&mut module),
214 RustType::Str
215 );
216 assert_eq!(
217 Type::FnPtr {
218 inputs: vec![Type::Numeric(TypeNumeric::U8)],
219 output: Box::new(Type::Numeric(TypeNumeric::U8)),
220 }
221 .unwrap(&mut module),
222 RustType::FnPtr("extern \"C\" fn(u8) -> u8".to_string())
223 );
224 }
225
226 #[test]
227 fn test_custom_records_helper() {
228 let mut module = TsModule::default();
229 let ty = Type::Custom {
230 ts_type: "string".to_string(),
231 to_rust: "encode({})".to_string(),
232 from_rust: "decode({})".to_string(),
233 helper: "codec".to_string(),
234 };
235 assert!(matches!(ty.unwrap(&mut module), RustType::Custom { .. }));
236 assert!(module.used_helpers.contains("codec"));
237 }
238}
239
240impl ToTokens for RustTypeNumeric {
245 fn to_tokens(&self, tokens: &mut TokenStream) {
246 tokens.extend(match self {
247 RustTypeNumeric::U64
248 | RustTypeNumeric::I64
249 | RustTypeNumeric::Usize
250 | RustTypeNumeric::Isize => quote! { bigint },
251 _ => quote! { number },
252 });
253 }
254}
255
256#[rustfmt::skip]
257impl ToTokens for RustType {
258 fn to_tokens(&self, tokens: &mut TokenStream) {
259 fn get_str_or_slice(elem: &Box<RustType>, fallback: TokenStream) -> TokenStream {
260 match &**elem {
261 RustType::Str => quote! { RustStr },
262 RustType::Slice(elem) => quote! { RustSlice<#elem> },
263 _ => fallback,
264 }
265 }
266
267 tokens.extend(match self {
268 RustType::Void => quote! { void },
269 RustType::Numeric(ts_type_numeric) => ts_type_numeric.to_token_stream(),
270 RustType::Boolean => quote! { boolean },
271 RustType::FnPtr(sig) => quote! { RustFnPtr<#sig> },
272 RustType::Ptr(elem) =>
273 get_str_or_slice(elem, quote! { RustPtr<#elem> }),
274 RustType::PtrMut(elem) =>
275 get_str_or_slice(elem, quote! { RustPtrMut<#elem> }),
276 RustType::Ref(elem) =>
277 get_str_or_slice(elem, quote! { RustRef<#elem> }),
278 RustType::RefMut(elem) =>
279 get_str_or_slice(elem, quote! { RustRefMut<#elem> }),
280 RustType::Box(elem) =>
281 get_str_or_slice(elem, quote! { RustBox<#elem> }),
282 RustType::Str => quote! { RustStr },
283 RustType::Slice(elem) => quote! { RustSlice<#elem> },
284 RustType::Vec(elem) => quote! { RustVec<#elem> },
285 RustType::Tuple(elem) => quote! { RustTuple<[#(#elem),*]> },
286 RustType::UserDefined(ident) => quote! { #ident },
287 RustType::Unsupported => quote! { RustUnsupportedType },
288 RustType::Custom { ts_type, .. } => raw(ts_type.as_str()).to_token_stream(),
289 });
290 }
291}
292
293#[derive(Clone, Debug, Default)]
300pub struct UserDefinedDefs {
301 store: BTreeSet<Ident>,
302}
303
304impl UserDefinedDefs {
305 fn insert(&mut self, ident: Ident) {
306 self.store.insert(ident);
307 }
308
309 pub fn dedup(&mut self, class_defs: &ClassDefs) {
312 class_defs.store.keys().for_each(|class_name| {
313 self.store
314 .extract_if(.., |user_defined| *user_defined == *class_name)
315 .for_each(drop);
316 });
317 }
318}
319
320impl ToTokens for UserDefinedDefs {
321 fn to_tokens(&self, tokens: &mut TokenStream) {
322 tokens.extend(
323 self.store
324 .iter()
325 .map(|ident| {
326 quote! {
327
328 export class #ident extends RustPrototype<#ident> {}
329
330 }
331 })
332 .collect::<TokenStream>(),
333 );
334 }
335}
336
337#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
340enum RustTypeList {
341 FnPtr,
342 Ptr,
343 PtrMut,
344 Ref,
345 RefMut,
346 Box,
347 Str,
348 Slice,
349 Vec,
350 Tuple,
351 Unsupported,
352 }
357
358impl ToTokens for RustTypeList {
359 fn to_tokens(&self, tokens: &mut TokenStream) {
360 tokens.extend(match self {
361 RustTypeList::FnPtr => quote! { RustFnPtr },
362 RustTypeList::Ptr => quote! { RustPtr },
363 RustTypeList::PtrMut => quote! { RustPtrMut },
364 RustTypeList::Ref => quote! { RustRef },
365 RustTypeList::RefMut => quote! { RustRefMut },
366 RustTypeList::Box => quote! { RustBox },
367 RustTypeList::Str => quote! { RustStr },
368 RustTypeList::Slice => quote! { RustSlice },
369 RustTypeList::Vec => quote! { RustVec },
370 RustTypeList::Tuple => quote! { RustTuple },
371 RustTypeList::Unsupported => quote! { RustUnsupportedType },
372 });
373 }
374}
375
376#[derive(Clone, Debug, Default)]
384pub struct RustTypeDefs {
385 store: BTreeSet<RustTypeList>,
386}
387
388impl RustTypeDefs {
389 fn insert(&mut self, ty: RustTypeList) {
390 self.store.insert(ty);
391 }
392}
393
394impl RustTypeDefs {
395 pub fn print_separate(
397 &self,
398 mut type_defs: String, type_defs_name: &str,
400 ) -> (String, String) {
401 let mut type_imports = Vec::new();
402 let mut class_imports = vec![
403 quote! { RustPrototype },
404 quote! { __dodo_borrow },
405 quote! { __dodo_borrow_mut },
406 quote! { __dodo_borrow_async },
407 quote! { __dodo_borrow_mut_async },
408 quote! { __dodo_take_async },
409 quote! { __dodo_dispose },
410 ];
411
412 for ty in &self.store {
413 match ty {
414 RustTypeList::Box
415 | RustTypeList::Str
416 | RustTypeList::Slice
417 | RustTypeList::Vec
418 | RustTypeList::Tuple => class_imports.push(ty.to_token_stream()),
419 rest => type_imports.push(rest.to_token_stream()),
420 };
421 }
422
423 type_defs.push('\n');
424 type_defs.push_str(
425 "e! {
426 export type { #(#type_imports),* };
427 export { #(#class_imports),* };
428 }
429 .to_string(),
430 );
431
432 let type_defs_name = format!("./{type_defs_name}");
433 let type_imports = if type_imports.is_empty() {
434 TokenStream::new()
435 } else {
436 quote! { import type { #(#type_imports),* } from #type_defs_name; }
437 };
438 let imports = quote! {
439 #type_imports
440 import { #(#class_imports),* } from #type_defs_name;
441 }
442 .to_string();
443
444 (type_defs, imports)
445 }
446}