1use convert_case::{Case, Casing};
2use quote::ToTokens;
3use std::fmt::{Display, Formatter};
4use syn::{Pat, PathArguments, PathSegment};
5
6use super::{ty_to_ts_type, ToTypeDef, TypeDef};
7use crate::{js_doc_from_comments, CallbackArg, FnKind, NapiFn};
8
9pub(crate) struct FnArg {
10 pub(crate) arg: String,
11 pub(crate) ts_type: String,
12 pub(crate) is_optional: bool,
13}
14
15pub(crate) struct FnArgList {
16 this: Option<FnArg>,
17 args: Vec<FnArg>,
18 last_required: Option<usize>,
19}
20
21impl Display for FnArgList {
22 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
23 if let Some(this: &FnArg) = &self.this {
24 write!(f, "this: {}", this.ts_type)?;
25 }
26 for (i: usize, arg: &FnArg) in self.args.iter().enumerate() {
27 if i != 0 || self.this.is_some() {
28 write!(f, ", ")?;
29 }
30 let is_optional: bool = arg.is_optional
31 && self
32 .last_required
33 .map_or(default:true, |last_required: usize| i > last_required);
34 if is_optional {
35 write!(f, "{}?: {}", arg.arg, arg.ts_type)?;
36 } else {
37 write!(f, "{}: {}", arg.arg, arg.ts_type)?;
38 }
39 }
40 Ok(())
41 }
42}
43
44impl FromIterator<FnArg> for FnArgList {
45 fn from_iter<T: IntoIterator<Item = FnArg>>(iter: T) -> Self {
46 let mut args: Vec = Vec::new();
47 let mut this: Option = None;
48 for arg: FnArg in iter.into_iter() {
49 if arg.arg != "this" {
50 args.push(arg);
51 } else {
52 this = Some(arg);
53 }
54 }
55 let last_required: Option = argsOption<(usize, &FnArg)>
56 .iter()
57 .enumerate()
58 .rfind(|(_, arg: &&FnArg)| !arg.is_optional)
59 .map(|(i: usize, _)| i);
60 FnArgList {
61 this,
62 args,
63 last_required,
64 }
65 }
66}
67
68impl ToTypeDef for NapiFn {
69 fn to_type_def(&self) -> Option<TypeDef> {
70 if self.skip_typescript {
71 return None;
72 }
73
74 let def = format!(
75 r#"{prefix} {name}{generic}({args}){ret}"#,
76 prefix = self.gen_ts_func_prefix(),
77 name = &self.js_name,
78 generic = &self
79 .ts_generic_types
80 .as_ref()
81 .map(|g| format!("<{}>", g))
82 .unwrap_or_default(),
83 args = self
84 .ts_args_type
85 .clone()
86 .unwrap_or_else(|| self.gen_ts_func_args()),
87 ret = self
88 .ts_return_type
89 .clone()
90 .map(|t| format!(": {}", t))
91 .unwrap_or_else(|| self.gen_ts_func_ret()),
92 );
93
94 Some(TypeDef {
95 kind: "fn".to_owned(),
96 name: self.js_name.clone(),
97 original_name: None,
98 def,
99 js_mod: self.js_mod.to_owned(),
100 js_doc: js_doc_from_comments(&self.comments),
101 })
102 }
103}
104
105fn gen_callback_type(callback: &CallbackArg) -> String {
106 format!(
107 "({args}) => {ret}",
108 args = &callback
109 .args
110 .iter()
111 .enumerate()
112 .map(|(i, arg)| {
113 let (ts_type, is_optional) = ty_to_ts_type(arg, false, false, false);
114 FnArg {
115 arg: format!("arg{}", i),
116 ts_type,
117 is_optional,
118 }
119 })
120 .collect::<FnArgList>(),
121 ret = match &callback.ret {
122 Some(ty) => ty_to_ts_type(ty, true, false, false).0,
123 None => "void".to_owned(),
124 }
125 )
126}
127
128impl NapiFn {
129 fn gen_ts_func_args(&self) -> String {
130 format!(
131 "{}",
132 self
133 .args
134 .iter()
135 .filter_map(|arg| match &arg.kind {
136 crate::NapiFnArgKind::PatType(path) => {
137 let ty_string = path.ty.to_token_stream().to_string();
138 if ty_string == "Env" {
139 return None;
140 }
141 if let syn::Type::Path(path) = path.ty.as_ref() {
142 if let Some(PathSegment { ident, arguments }) = path.path.segments.last() {
143 if ident == "Reference" || ident == "WeakReference" {
144 if let Some(parent) = &self.parent {
145 if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
146 args: angle_bracketed_args,
147 ..
148 }) = arguments
149 {
150 if let Some(syn::GenericArgument::Type(syn::Type::Path(syn::TypePath {
151 path,
152 ..
153 }))) = angle_bracketed_args.first()
154 {
155 if let Some(segment) = path.segments.first() {
156 if *parent == segment.ident {
157 // If we have a Reference<A> in an impl A block, it shouldn't be an arg
158 return None;
159 }
160 }
161 }
162 }
163 }
164 }
165 if ident == "This" || ident == "this" {
166 if self.kind != FnKind::Normal {
167 return None;
168 }
169 if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
170 args: angle_bracketed_args,
171 ..
172 }) = arguments
173 {
174 if let Some(syn::GenericArgument::Type(ty)) = angle_bracketed_args.first() {
175 let (ts_type, _) = ty_to_ts_type(ty, false, false, false);
176 return Some(FnArg {
177 arg: "this".to_owned(),
178 ts_type,
179 is_optional: false,
180 });
181 }
182 } else {
183 return Some(FnArg {
184 arg: "this".to_owned(),
185 ts_type: "this".to_owned(),
186 is_optional: false,
187 });
188 }
189 return None;
190 }
191 }
192 }
193
194 let mut path = path.clone();
195 // remove mutability from PatIdent
196 if let Pat::Ident(i) = path.pat.as_mut() {
197 i.mutability = None;
198 }
199
200 let (ts_type, is_optional) = ty_to_ts_type(&path.ty, false, false, false);
201 let ts_type = arg.use_overridden_type_or(|| ts_type);
202 let arg = path.pat.to_token_stream().to_string().to_case(Case::Camel);
203
204 Some(FnArg {
205 arg,
206 ts_type,
207 is_optional,
208 })
209 }
210 crate::NapiFnArgKind::Callback(cb) => {
211 let ts_type = arg.use_overridden_type_or(|| gen_callback_type(cb));
212 let arg = cb.pat.to_token_stream().to_string().to_case(Case::Camel);
213
214 Some(FnArg {
215 arg,
216 ts_type,
217 is_optional: false,
218 })
219 }
220 })
221 .collect::<FnArgList>()
222 )
223 }
224
225 fn gen_ts_func_prefix(&self) -> &'static str {
226 if self.parent.is_some() {
227 match self.kind {
228 crate::FnKind::Normal => match self.fn_self {
229 Some(_) => "",
230 None => "static",
231 },
232 crate::FnKind::Factory => "static",
233 crate::FnKind::Constructor => "",
234 crate::FnKind::Getter => "get",
235 crate::FnKind::Setter => "set",
236 }
237 } else {
238 "export function"
239 }
240 }
241
242 fn gen_ts_func_ret(&self) -> String {
243 match self.kind {
244 FnKind::Constructor | FnKind::Setter => "".to_owned(),
245 FnKind::Factory => self
246 .parent
247 .clone()
248 .map(|i| {
249 let parent = i.to_string().to_case(Case::Pascal);
250 if self.is_async {
251 format!(": Promise<{}>", parent)
252 } else {
253 format!(": {}", parent)
254 }
255 })
256 .unwrap_or_else(|| "".to_owned()),
257 _ => {
258 let ret = if let Some(ret) = &self.ret {
259 let (ts_type, _) = ty_to_ts_type(ret, true, false, false);
260 if ts_type == "undefined" {
261 "void".to_owned()
262 } else if ts_type == "Self" {
263 "this".to_owned()
264 } else {
265 ts_type
266 }
267 } else {
268 "void".to_owned()
269 };
270
271 if self.is_async {
272 format!(": Promise<{}>", ret)
273 } else {
274 format!(": {}", ret)
275 }
276 }
277 }
278 }
279}
280