1use std::cell::RefCell;
2use std::collections::HashMap;
3
4use super::{add_alias, ToTypeDef, TypeDef};
5use crate::{js_doc_from_comments, ty_to_ts_type, NapiImpl, NapiStruct, NapiStructKind};
6
7thread_local! {
8 pub(crate) static TASK_STRUCTS: RefCell<HashMap<String, String>> = Default::default();
9 pub(crate) static CLASS_STRUCTS: RefCell<HashMap<String, String>> = Default::default();
10}
11
12impl ToTypeDef for NapiStruct {
13 fn to_type_def(&self) -> Option<TypeDef> {
14 CLASS_STRUCTS.with(|c: &RefCell>| {
15 c.borrow_mut()
16 .insert(self.name.to_string(), self.js_name.clone());
17 });
18 add_alias(self.name.to_string(), self.js_name.to_string());
19
20 Some(TypeDef {
21 kind: String::from(if self.kind == NapiStructKind::Object {
22 "interface"
23 } else {
24 "struct"
25 }),
26 name: self.js_name.to_owned(),
27 original_name: Some(self.name.to_string()),
28 def: self.gen_ts_class(),
29 js_mod: self.js_mod.to_owned(),
30 js_doc: js_doc_from_comments(&self.comments),
31 })
32 }
33}
34
35impl ToTypeDef for NapiImpl {
36 fn to_type_def(&self) -> Option<TypeDef> {
37 if let Some(output_type) = &self.task_output_type {
38 TASK_STRUCTS.with(|t| {
39 let (resolved_type, is_optional) = ty_to_ts_type(output_type, false, true, false);
40 t.borrow_mut().insert(
41 self.name.to_string(),
42 if resolved_type == "undefined" {
43 "void".to_owned()
44 } else if is_optional {
45 format!("{} | null", resolved_type)
46 } else {
47 resolved_type
48 },
49 );
50 });
51 }
52
53 if let Some(output_type) = &self.iterator_yield_type {
54 let next_type = if let Some(ref ty) = self.iterator_next_type {
55 ty_to_ts_type(ty, false, false, false).0
56 } else {
57 "void".to_owned()
58 };
59 let return_type = if let Some(ref ty) = self.iterator_return_type {
60 ty_to_ts_type(ty, false, false, false).0
61 } else {
62 "void".to_owned()
63 };
64 Some(TypeDef {
65 kind: "impl".to_owned(),
66 name: self.js_name.to_owned(),
67 original_name: None,
68 def: format!(
69 "[Symbol.iterator](): Iterator<{}, {}, {}>",
70 ty_to_ts_type(output_type, false, true, false).0,
71 return_type,
72 next_type,
73 ),
74 js_mod: self.js_mod.to_owned(),
75 js_doc: "".to_string(),
76 })
77 } else {
78 Some(TypeDef {
79 kind: "impl".to_owned(),
80 name: self.js_name.to_owned(),
81 original_name: None,
82 def: self
83 .items
84 .iter()
85 .filter_map(|f| {
86 if f.skip_typescript {
87 None
88 } else {
89 Some(format!(
90 "{}{}",
91 js_doc_from_comments(&f.comments),
92 f.to_type_def()
93 .map_or(String::default(), |type_def| type_def.def)
94 ))
95 }
96 })
97 .collect::<Vec<_>>()
98 .join("\\n"),
99 js_mod: self.js_mod.to_owned(),
100 js_doc: "".to_string(),
101 })
102 }
103 }
104}
105
106impl NapiStruct {
107 fn gen_ts_class(&self) -> String {
108 let mut ctor_args = vec![];
109 let def = self
110 .fields
111 .iter()
112 .filter(|f| f.getter)
113 .filter_map(|f| {
114 if f.skip_typescript {
115 return None;
116 }
117
118 let mut field_str = String::from("");
119
120 if !f.comments.is_empty() {
121 field_str.push_str(&js_doc_from_comments(&f.comments))
122 }
123
124 if !f.setter {
125 field_str.push_str("readonly ")
126 }
127
128 let (arg, is_optional) = ty_to_ts_type(&f.ty, false, true, false);
129 let arg = f.ts_type.as_ref().map(|ty| ty.to_string()).unwrap_or(arg);
130
131 let arg = match is_optional {
132 false => format!("{}: {}", &f.js_name, arg),
133 true => match self.use_nullable {
134 false => format!("{}?: {}", &f.js_name, arg),
135 true => format!("{}: {} | null", &f.js_name, arg),
136 },
137 };
138 if self.kind == NapiStructKind::Constructor {
139 ctor_args.push(arg.clone());
140 }
141 field_str.push_str(&arg);
142
143 Some(field_str)
144 })
145 .collect::<Vec<_>>()
146 .join("\\n");
147
148 if self.kind == NapiStructKind::Constructor {
149 format!("{}\\nconstructor({})", def, ctor_args.join(", "))
150 } else {
151 def
152 }
153 }
154}
155