1use std::borrow::Cow;
4use std::fmt::{Display, Formatter};
5
6#[derive(Debug, Clone, Eq, PartialEq)]
11pub enum TypeInfo {
12 Any,
14 None,
16 NoReturn,
18 Callable(Option<Vec<TypeInfo>>, Box<TypeInfo>),
26 Tuple(Option<Vec<TypeInfo>>),
35 UnsizedTypedTuple(Box<TypeInfo>),
41 Class {
43 module: ModuleName,
45 name: Cow<'static, str>,
47 type_vars: Vec<TypeInfo>,
49 },
50}
51
52#[derive(Debug, Clone, Eq, PartialEq)]
54pub enum ModuleName {
55 Builtin,
57 CurrentModule,
59 Module(Cow<'static, str>),
61}
62
63impl TypeInfo {
64 pub fn module_name(&self) -> Option<&str> {
68 match self {
69 TypeInfo::Any
70 | TypeInfo::None
71 | TypeInfo::NoReturn
72 | TypeInfo::Callable(_, _)
73 | TypeInfo::Tuple(_)
74 | TypeInfo::UnsizedTypedTuple(_) => Some("typing"),
75 TypeInfo::Class { module, .. } => match module {
76 ModuleName::Builtin => Some("builtins"),
77 ModuleName::CurrentModule => None,
78 ModuleName::Module(name) => Some(name),
79 },
80 }
81 }
82
83 pub fn name(&self) -> Cow<'_, str> {
87 Cow::from(match self {
88 TypeInfo::Any => "Any",
89 TypeInfo::None => "None",
90 TypeInfo::NoReturn => "NoReturn",
91 TypeInfo::Callable(_, _) => "Callable",
92 TypeInfo::Tuple(_) => "Tuple",
93 TypeInfo::UnsizedTypedTuple(_) => "Tuple",
94 TypeInfo::Class { name, .. } => name,
95 })
96 }
97}
98
99impl TypeInfo {
101 pub fn optional_of(t: TypeInfo) -> TypeInfo {
103 TypeInfo::Class {
104 module: ModuleName::Module(Cow::from("typing")),
105 name: Cow::from("Optional"),
106 type_vars: vec![t],
107 }
108 }
109
110 pub fn union_of(types: &[TypeInfo]) -> TypeInfo {
112 TypeInfo::Class {
113 module: ModuleName::Module(Cow::from("typing")),
114 name: Cow::from("Union"),
115 type_vars: types.to_vec(),
116 }
117 }
118
119 pub fn list_of(t: TypeInfo) -> TypeInfo {
121 TypeInfo::Class {
122 module: ModuleName::Module(Cow::from("typing")),
123 name: Cow::from("List"),
124 type_vars: vec![t],
125 }
126 }
127
128 pub fn sequence_of(t: TypeInfo) -> TypeInfo {
130 TypeInfo::Class {
131 module: ModuleName::Module(Cow::from("typing")),
132 name: Cow::from("Sequence"),
133 type_vars: vec![t],
134 }
135 }
136
137 pub fn set_of(t: TypeInfo) -> TypeInfo {
139 TypeInfo::Class {
140 module: ModuleName::Module(Cow::from("typing")),
141 name: Cow::from("Set"),
142 type_vars: vec![t],
143 }
144 }
145
146 pub fn frozen_set_of(t: TypeInfo) -> TypeInfo {
148 TypeInfo::Class {
149 module: ModuleName::Module(Cow::from("typing")),
150 name: Cow::from("FrozenSet"),
151 type_vars: vec![t],
152 }
153 }
154
155 pub fn iterable_of(t: TypeInfo) -> TypeInfo {
157 TypeInfo::Class {
158 module: ModuleName::Module(Cow::from("typing")),
159 name: Cow::from("Iterable"),
160 type_vars: vec![t],
161 }
162 }
163
164 pub fn iterator_of(t: TypeInfo) -> TypeInfo {
166 TypeInfo::Class {
167 module: ModuleName::Module(Cow::from("typing")),
168 name: Cow::from("Iterator"),
169 type_vars: vec![t],
170 }
171 }
172
173 pub fn dict_of(k: TypeInfo, v: TypeInfo) -> TypeInfo {
175 TypeInfo::Class {
176 module: ModuleName::Module(Cow::from("typing")),
177 name: Cow::from("Dict"),
178 type_vars: vec![k, v],
179 }
180 }
181
182 pub fn mapping_of(k: TypeInfo, v: TypeInfo) -> TypeInfo {
184 TypeInfo::Class {
185 module: ModuleName::Module(Cow::from("typing")),
186 name: Cow::from("Mapping"),
187 type_vars: vec![k, v],
188 }
189 }
190
191 pub fn builtin(name: &'static str) -> TypeInfo {
193 TypeInfo::Class {
194 module: ModuleName::Builtin,
195 name: Cow::from(name),
196 type_vars: vec![],
197 }
198 }
199}
200
201impl Display for TypeInfo {
202 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
203 match self {
204 TypeInfo::Any | TypeInfo::None | TypeInfo::NoReturn => write!(f, "{}", self.name()),
205 TypeInfo::Callable(input, output) => {
206 write!(f, "Callable[")?;
207
208 if let Some(input) = input {
209 write!(f, "[")?;
210 let mut comma = false;
211 for arg in input {
212 if comma {
213 write!(f, ", ")?;
214 }
215 write!(f, "{}", arg)?;
216 comma = true;
217 }
218 write!(f, "]")?;
219 } else {
220 write!(f, "...")?;
221 }
222
223 write!(f, ", {}]", output)
224 }
225 TypeInfo::Tuple(types) => {
226 write!(f, "Tuple[")?;
227
228 if let Some(types) = types {
229 if types.is_empty() {
230 write!(f, "()")?;
231 } else {
232 let mut comma = false;
233 for t in types {
234 if comma {
235 write!(f, ", ")?;
236 }
237 write!(f, "{}", t)?;
238 comma = true;
239 }
240 }
241 } else {
242 write!(f, "...")?;
243 }
244
245 write!(f, "]")
246 }
247 TypeInfo::UnsizedTypedTuple(t) => write!(f, "Tuple[{}, ...]", t),
248 TypeInfo::Class {
249 name, type_vars, ..
250 } => {
251 write!(f, "{}", name)?;
252
253 if !type_vars.is_empty() {
254 write!(f, "[")?;
255
256 let mut comma = false;
257 for var in type_vars {
258 if comma {
259 write!(f, ", ")?;
260 }
261 write!(f, "{}", var)?;
262 comma = true;
263 }
264
265 write!(f, "]")
266 } else {
267 Ok(())
268 }
269 }
270 }
271 }
272}
273
274#[cfg(test)]
275mod test {
276 use std::borrow::Cow;
277
278 use crate::inspect::types::{ModuleName, TypeInfo};
279
280 #[track_caller]
281 pub fn assert_display(t: &TypeInfo, expected: &str) {
282 assert_eq!(format!("{}", t), expected)
283 }
284
285 #[test]
286 fn basic() {
287 assert_display(&TypeInfo::Any, "Any");
288 assert_display(&TypeInfo::None, "None");
289 assert_display(&TypeInfo::NoReturn, "NoReturn");
290
291 assert_display(&TypeInfo::builtin("int"), "int");
292 }
293
294 #[test]
295 fn callable() {
296 let any_to_int = TypeInfo::Callable(None, Box::new(TypeInfo::builtin("int")));
297 assert_display(&any_to_int, "Callable[..., int]");
298
299 let sum = TypeInfo::Callable(
300 Some(vec![TypeInfo::builtin("int"), TypeInfo::builtin("int")]),
301 Box::new(TypeInfo::builtin("int")),
302 );
303 assert_display(&sum, "Callable[[int, int], int]");
304 }
305
306 #[test]
307 fn tuple() {
308 let any = TypeInfo::Tuple(None);
309 assert_display(&any, "Tuple[...]");
310
311 let triple = TypeInfo::Tuple(Some(vec![
312 TypeInfo::builtin("int"),
313 TypeInfo::builtin("str"),
314 TypeInfo::builtin("bool"),
315 ]));
316 assert_display(&triple, "Tuple[int, str, bool]");
317
318 let empty = TypeInfo::Tuple(Some(vec![]));
319 assert_display(&empty, "Tuple[()]");
320
321 let typed = TypeInfo::UnsizedTypedTuple(Box::new(TypeInfo::builtin("bool")));
322 assert_display(&typed, "Tuple[bool, ...]");
323 }
324
325 #[test]
326 fn class() {
327 let class1 = TypeInfo::Class {
328 module: ModuleName::CurrentModule,
329 name: Cow::from("MyClass"),
330 type_vars: vec![],
331 };
332 assert_display(&class1, "MyClass");
333
334 let class2 = TypeInfo::Class {
335 module: ModuleName::CurrentModule,
336 name: Cow::from("MyClass"),
337 type_vars: vec![TypeInfo::builtin("int"), TypeInfo::builtin("bool")],
338 };
339 assert_display(&class2, "MyClass[int, bool]");
340 }
341
342 #[test]
343 fn collections() {
344 let int = TypeInfo::builtin("int");
345 let bool = TypeInfo::builtin("bool");
346 let str = TypeInfo::builtin("str");
347
348 let list = TypeInfo::list_of(int.clone());
349 assert_display(&list, "List[int]");
350
351 let sequence = TypeInfo::sequence_of(bool.clone());
352 assert_display(&sequence, "Sequence[bool]");
353
354 let optional = TypeInfo::optional_of(str.clone());
355 assert_display(&optional, "Optional[str]");
356
357 let iterable = TypeInfo::iterable_of(int.clone());
358 assert_display(&iterable, "Iterable[int]");
359
360 let iterator = TypeInfo::iterator_of(bool);
361 assert_display(&iterator, "Iterator[bool]");
362
363 let dict = TypeInfo::dict_of(int.clone(), str.clone());
364 assert_display(&dict, "Dict[int, str]");
365
366 let mapping = TypeInfo::mapping_of(int, str.clone());
367 assert_display(&mapping, "Mapping[int, str]");
368
369 let set = TypeInfo::set_of(str.clone());
370 assert_display(&set, "Set[str]");
371
372 let frozen_set = TypeInfo::frozen_set_of(str);
373 assert_display(&frozen_set, "FrozenSet[str]");
374 }
375
376 #[test]
377 fn complicated() {
378 let int = TypeInfo::builtin("int");
379 assert_display(&int, "int");
380
381 let bool = TypeInfo::builtin("bool");
382 assert_display(&bool, "bool");
383
384 let str = TypeInfo::builtin("str");
385 assert_display(&str, "str");
386
387 let any = TypeInfo::Any;
388 assert_display(&any, "Any");
389
390 let params = TypeInfo::union_of(&[int.clone(), str]);
391 assert_display(¶ms, "Union[int, str]");
392
393 let func = TypeInfo::Callable(Some(vec![params, any]), Box::new(bool));
394 assert_display(&func, "Callable[[Union[int, str], Any], bool]");
395
396 let dict = TypeInfo::mapping_of(int, func);
397 assert_display(
398 &dict,
399 "Mapping[int, Callable[[Union[int, str], Any], bool]]",
400 );
401 }
402}
403
404#[cfg(test)]
405mod conversion {
406 use std::collections::{HashMap, HashSet};
407
408 use crate::inspect::types::test::assert_display;
409 use crate::{FromPyObject, IntoPyObject};
410
411 #[test]
412 fn unsigned_int() {
413 assert_display(&usize::type_output(), "int");
414 assert_display(&usize::type_input(), "int");
415
416 assert_display(&u8::type_output(), "int");
417 assert_display(&u8::type_input(), "int");
418
419 assert_display(&u16::type_output(), "int");
420 assert_display(&u16::type_input(), "int");
421
422 assert_display(&u32::type_output(), "int");
423 assert_display(&u32::type_input(), "int");
424
425 assert_display(&u64::type_output(), "int");
426 assert_display(&u64::type_input(), "int");
427 }
428
429 #[test]
430 fn signed_int() {
431 assert_display(&isize::type_output(), "int");
432 assert_display(&isize::type_input(), "int");
433
434 assert_display(&i8::type_output(), "int");
435 assert_display(&i8::type_input(), "int");
436
437 assert_display(&i16::type_output(), "int");
438 assert_display(&i16::type_input(), "int");
439
440 assert_display(&i32::type_output(), "int");
441 assert_display(&i32::type_input(), "int");
442
443 assert_display(&i64::type_output(), "int");
444 assert_display(&i64::type_input(), "int");
445 }
446
447 #[test]
448 fn float() {
449 assert_display(&f32::type_output(), "float");
450 assert_display(&f32::type_input(), "float");
451
452 assert_display(&f64::type_output(), "float");
453 assert_display(&f64::type_input(), "float");
454 }
455
456 #[test]
457 fn bool() {
458 assert_display(&bool::type_output(), "bool");
459 assert_display(&bool::type_input(), "bool");
460 }
461
462 #[test]
463 fn text() {
464 assert_display(&String::type_output(), "str");
465 assert_display(&String::type_input(), "str");
466
467 assert_display(&<&[u8]>::type_output(), "Union[bytes, List[int]]");
468 assert_display(&<&[String]>::type_output(), "Union[bytes, List[str]]");
469 assert_display(
470 &<&[u8] as crate::conversion::FromPyObjectBound>::type_input(),
471 "bytes",
472 );
473 }
474
475 #[test]
476 fn collections() {
477 assert_display(&<Vec<usize>>::type_output(), "List[int]");
478 assert_display(&<Vec<usize>>::type_input(), "Sequence[int]");
479
480 assert_display(&<HashSet<usize>>::type_output(), "Set[int]");
481 assert_display(&<HashSet<usize>>::type_input(), "Set[int]");
482
483 assert_display(&<HashMap<usize, f32>>::type_output(), "Dict[int, float]");
484 assert_display(&<HashMap<usize, f32>>::type_input(), "Mapping[int, float]");
485
486 assert_display(&<(usize, f32)>::type_input(), "Tuple[int, float]");
487 }
488}