1use crate::{
2 generate::Import,
3 stub_type::{ImportRef, ModuleRef},
4 type_info::{ParameterDefault as ParameterDefaultInfo, ParameterInfo, ParameterKind},
5 TypeInfo,
6};
7use std::{collections::HashSet, fmt};
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum ParameterDefault {
12 None,
14 Expr {
16 value: String,
18 source_module: Option<ModuleRef>,
20 },
21}
22
23#[derive(Debug, Clone, PartialEq)]
27pub struct Parameter {
28 pub name: &'static str,
30 pub kind: ParameterKind,
32 pub type_info: TypeInfo,
34 pub default: ParameterDefault,
36}
37
38impl Import for Parameter {
39 fn import(&self) -> HashSet<ImportRef> {
40 self.type_info.import.clone()
41 }
42}
43
44impl From<&ParameterInfo> for Parameter {
45 fn from(info: &ParameterInfo) -> Self {
46 Self {
47 name: info.name,
48 kind: info.kind,
49 type_info: (info.type_info)(),
50 default: match &info.default {
51 ParameterDefaultInfo::None => ParameterDefault::None,
52 ParameterDefaultInfo::Expr {
53 value,
54 source_module,
55 } => ParameterDefault::Expr {
56 value: value(),
57 source_module: source_module.and_then(|f| f()),
58 },
59 },
60 }
61 }
62}
63
64impl fmt::Display for Parameter {
65 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66 match self.kind {
67 ParameterKind::VarPositional => {
68 write!(f, "*{}: {}", self.name, self.type_info)
69 }
70 ParameterKind::VarKeyword => {
71 write!(f, "**{}: {}", self.name, self.type_info)
72 }
73 _ => {
74 write!(f, "{}: {}", self.name, self.type_info)?;
75 match &self.default {
76 ParameterDefault::None => Ok(()),
77 ParameterDefault::Expr { value, .. } => write!(f, " = {}", value),
78 }
79 }
80 }
81 }
82}
83
84#[derive(Debug, Clone, PartialEq)]
89pub struct Parameters {
90 pub positional_only: Vec<Parameter>,
92 pub positional_or_keyword: Vec<Parameter>,
94 pub keyword_only: Vec<Parameter>,
96 pub varargs: Option<Parameter>,
98 pub varkw: Option<Parameter>,
100}
101
102impl Parameters {
103 pub fn new() -> Self {
105 Self {
106 positional_only: Vec::new(),
107 positional_or_keyword: Vec::new(),
108 keyword_only: Vec::new(),
109 varargs: None,
110 varkw: None,
111 }
112 }
113
114 pub fn from_infos(infos: &[ParameterInfo]) -> Self {
116 let mut params = Self::new();
117
118 for info in infos {
119 let param = Parameter::from(info);
120 match param.kind {
121 ParameterKind::PositionalOnly => params.positional_only.push(param),
122 ParameterKind::PositionalOrKeyword => params.positional_or_keyword.push(param),
123 ParameterKind::KeywordOnly => params.keyword_only.push(param),
124 ParameterKind::VarPositional => params.varargs = Some(param),
125 ParameterKind::VarKeyword => params.varkw = Some(param),
126 }
127 }
128
129 params
130 }
131
132 pub fn iter_entries(&self) -> impl Iterator<Item = &Parameter> {
134 self.positional_only
135 .iter()
136 .chain(self.positional_or_keyword.iter())
137 .chain(self.varargs.iter())
138 .chain(self.keyword_only.iter())
139 .chain(self.varkw.iter())
140 }
141
142 pub fn is_empty(&self) -> bool {
144 self.positional_only.is_empty()
145 && self.positional_or_keyword.is_empty()
146 && self.keyword_only.is_empty()
147 && self.varargs.is_none()
148 && self.varkw.is_none()
149 }
150}
151
152impl Default for Parameters {
153 fn default() -> Self {
154 Self::new()
155 }
156}
157
158impl Import for Parameters {
159 fn import(&self) -> HashSet<ImportRef> {
160 self.iter_entries().flat_map(|p| p.import()).collect()
161 }
162}
163
164impl fmt::Display for Parameters {
165 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
166 let mut parts = Vec::new();
167
168 for param in &self.positional_only {
170 parts.push(param.to_string());
171 }
172
173 if !self.positional_only.is_empty() {
175 parts.push("/".to_string());
176 }
177
178 for param in &self.positional_or_keyword {
180 parts.push(param.to_string());
181 }
182
183 if let Some(varargs) = &self.varargs {
185 parts.push(varargs.to_string());
186 } else if !self.keyword_only.is_empty() {
187 parts.push("*".to_string());
189 }
190
191 for param in &self.keyword_only {
193 parts.push(param.to_string());
194 }
195
196 if let Some(varkw) = &self.varkw {
198 parts.push(varkw.to_string());
199 }
200
201 write!(f, "{}", parts.join(", "))
202 }
203}
204
205impl Parameters {
206 pub fn fmt_for_module(&self, target_module: &str) -> String {
211 let mut parts = Vec::new();
212
213 for param in &self.positional_only {
215 parts.push(Self::format_param(param, target_module));
216 }
217
218 if !self.positional_only.is_empty() {
220 parts.push("/".to_string());
221 }
222
223 for param in &self.positional_or_keyword {
225 parts.push(Self::format_param(param, target_module));
226 }
227
228 if let Some(varargs) = &self.varargs {
230 parts.push(Self::format_param(varargs, target_module));
231 } else if !self.keyword_only.is_empty() {
232 parts.push("*".to_string());
234 }
235
236 for param in &self.keyword_only {
238 parts.push(Self::format_param(param, target_module));
239 }
240
241 if let Some(varkw) = &self.varkw {
243 parts.push(Self::format_param(varkw, target_module));
244 }
245
246 parts.join(", ")
247 }
248
249 fn format_param(param: &Parameter, target_module: &str) -> String {
251 let qualified_type = param.type_info.qualified_for_module(target_module);
252 match param.kind {
253 ParameterKind::VarPositional => format!("*{}: {}", param.name, qualified_type),
254 ParameterKind::VarKeyword => format!("**{}: {}", param.name, qualified_type),
255 _ => {
256 let base = format!("{}: {}", param.name, qualified_type);
257 match ¶m.default {
258 ParameterDefault::None => base,
259 ParameterDefault::Expr {
260 value,
261 source_module,
262 } => {
263 let qualified_expr =
264 qualify_default_value(value, source_module.as_ref(), target_module);
265 format!("{} = {}", base, qualified_expr)
266 }
267 }
268 }
269 }
270 }
271}
272
273fn qualify_default_value(
278 value: &str,
279 source_module: Option<&ModuleRef>,
280 target_module: &str,
281) -> String {
282 let Some(module_ref) = source_module else {
283 return value.to_string();
284 };
285 let Some(module_name) = module_ref.get() else {
286 return value.to_string();
287 };
288
289 if module_name == target_module {
292 value.to_string()
293 } else {
294 let module_component = module_name.rsplit('.').next().unwrap_or(module_name);
297 format!("{}.{}", module_component, value)
298 }
299}
300
301#[cfg(test)]
302mod tests {
303 use super::*;
304
305 #[test]
306 fn test_positional_only() {
307 let params = Parameters {
308 positional_only: vec![
309 Parameter {
310 name: "x",
311 kind: ParameterKind::PositionalOnly,
312 type_info: TypeInfo::builtin("int"),
313 default: ParameterDefault::None,
314 },
315 Parameter {
316 name: "y",
317 kind: ParameterKind::PositionalOnly,
318 type_info: TypeInfo::builtin("int"),
319 default: ParameterDefault::None,
320 },
321 ],
322 ..Default::default()
323 };
324
325 assert_eq!(params.to_string(), "x: builtins.int, y: builtins.int, /");
326 }
327
328 #[test]
329 fn test_keyword_only() {
330 let params = Parameters {
331 keyword_only: vec![Parameter {
332 name: "kw",
333 kind: ParameterKind::KeywordOnly,
334 type_info: TypeInfo::builtin("str"),
335 default: ParameterDefault::Expr {
336 value: "None".to_string(),
337 source_module: None,
338 },
339 }],
340 ..Default::default()
341 };
342
343 assert_eq!(params.to_string(), "*, kw: builtins.str = None");
344 }
345
346 #[test]
347 fn test_mixed_with_defaults() {
348 let params = Parameters {
349 positional_only: vec![Parameter {
350 name: "token",
351 kind: ParameterKind::PositionalOnly,
352 type_info: TypeInfo::builtin("str"),
353 default: ParameterDefault::None,
354 }],
355 keyword_only: vec![
356 Parameter {
357 name: "retries",
358 kind: ParameterKind::KeywordOnly,
359 type_info: TypeInfo::builtin("int"),
360 default: ParameterDefault::Expr {
361 value: "3".to_string(),
362 source_module: None,
363 },
364 },
365 Parameter {
366 name: "timeout",
367 kind: ParameterKind::KeywordOnly,
368 type_info: TypeInfo::builtin("float"),
369 default: ParameterDefault::None,
370 },
371 ],
372 ..Default::default()
373 };
374
375 assert_eq!(
376 params.to_string(),
377 "token: builtins.str, /, *, retries: builtins.int = 3, timeout: builtins.float"
378 );
379 }
380
381 #[test]
382 fn test_varargs_kwargs() {
383 let params = Parameters {
384 varargs: Some(Parameter {
385 name: "args",
386 kind: ParameterKind::VarPositional,
387 type_info: TypeInfo::builtin("str"),
388 default: ParameterDefault::None,
389 }),
390 varkw: Some(Parameter {
391 name: "kwargs",
392 kind: ParameterKind::VarKeyword,
393 type_info: TypeInfo::any(),
394 default: ParameterDefault::None,
395 }),
396 ..Default::default()
397 };
398
399 assert_eq!(
400 params.to_string(),
401 "*args: builtins.str, **kwargs: typing.Any"
402 );
403 }
404
405 #[test]
406 fn test_qualify_default_value_same_module() {
407 let module_ref = ModuleRef::Named("pkg._core".to_string());
409 let result = qualify_default_value("C.C1", Some(&module_ref), "pkg._core");
410 assert_eq!(result, "C.C1");
411 }
412
413 #[test]
414 fn test_qualify_default_value_different_module() {
415 let module_ref = ModuleRef::Named("pkg._core".to_string());
417 let result = qualify_default_value("C.C1", Some(&module_ref), "pkg");
418 assert_eq!(result, "_core.C.C1");
419 }
420
421 #[test]
422 fn test_qualify_default_value_different_submodules() {
423 let module_ref = ModuleRef::Named("pkg.a._core".to_string());
426 let result = qualify_default_value("C.C1", Some(&module_ref), "pkg.b._core");
427 assert_eq!(result, "_core.C.C1");
428 }
429
430 #[test]
431 fn test_qualify_default_value_no_source_module() {
432 let result = qualify_default_value("None", None, "pkg._core");
434 assert_eq!(result, "None");
435 }
436}