use std::borrow::Cow;
use std::fmt::{Display, Formatter};
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TypeInfo {
Any,
None,
NoReturn,
Callable(Option<Vec<TypeInfo>>, Box<TypeInfo>),
Tuple(Option<Vec<TypeInfo>>),
UnsizedTypedTuple(Box<TypeInfo>),
Class {
module: ModuleName,
name: Cow<'static, str>,
type_vars: Vec<TypeInfo>,
},
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ModuleName {
Builtin,
CurrentModule,
Module(Cow<'static, str>),
}
impl TypeInfo {
pub fn module_name(&self) -> Option<&str> {
match self {
TypeInfo::Any
| TypeInfo::None
| TypeInfo::NoReturn
| TypeInfo::Callable(_, _)
| TypeInfo::Tuple(_)
| TypeInfo::UnsizedTypedTuple(_) => Some("typing"),
TypeInfo::Class { module, .. } => match module {
ModuleName::Builtin => Some("builtins"),
ModuleName::CurrentModule => None,
ModuleName::Module(name) => Some(name),
},
}
}
pub fn name(&self) -> Cow<'_, str> {
Cow::from(match self {
TypeInfo::Any => "Any",
TypeInfo::None => "None",
TypeInfo::NoReturn => "NoReturn",
TypeInfo::Callable(_, _) => "Callable",
TypeInfo::Tuple(_) => "Tuple",
TypeInfo::UnsizedTypedTuple(_) => "Tuple",
TypeInfo::Class { name, .. } => name,
})
}
}
impl TypeInfo {
pub fn optional_of(t: TypeInfo) -> TypeInfo {
TypeInfo::Class {
module: ModuleName::Module(Cow::from("typing")),
name: Cow::from("Optional"),
type_vars: vec![t],
}
}
pub fn union_of(types: &[TypeInfo]) -> TypeInfo {
TypeInfo::Class {
module: ModuleName::Module(Cow::from("typing")),
name: Cow::from("Union"),
type_vars: types.to_vec(),
}
}
pub fn list_of(t: TypeInfo) -> TypeInfo {
TypeInfo::Class {
module: ModuleName::Module(Cow::from("typing")),
name: Cow::from("List"),
type_vars: vec![t],
}
}
pub fn sequence_of(t: TypeInfo) -> TypeInfo {
TypeInfo::Class {
module: ModuleName::Module(Cow::from("typing")),
name: Cow::from("Sequence"),
type_vars: vec![t],
}
}
pub fn set_of(t: TypeInfo) -> TypeInfo {
TypeInfo::Class {
module: ModuleName::Module(Cow::from("typing")),
name: Cow::from("Set"),
type_vars: vec![t],
}
}
pub fn frozen_set_of(t: TypeInfo) -> TypeInfo {
TypeInfo::Class {
module: ModuleName::Module(Cow::from("typing")),
name: Cow::from("FrozenSet"),
type_vars: vec![t],
}
}
pub fn iterable_of(t: TypeInfo) -> TypeInfo {
TypeInfo::Class {
module: ModuleName::Module(Cow::from("typing")),
name: Cow::from("Iterable"),
type_vars: vec![t],
}
}
pub fn iterator_of(t: TypeInfo) -> TypeInfo {
TypeInfo::Class {
module: ModuleName::Module(Cow::from("typing")),
name: Cow::from("Iterator"),
type_vars: vec![t],
}
}
pub fn dict_of(k: TypeInfo, v: TypeInfo) -> TypeInfo {
TypeInfo::Class {
module: ModuleName::Module(Cow::from("typing")),
name: Cow::from("Dict"),
type_vars: vec![k, v],
}
}
pub fn mapping_of(k: TypeInfo, v: TypeInfo) -> TypeInfo {
TypeInfo::Class {
module: ModuleName::Module(Cow::from("typing")),
name: Cow::from("Mapping"),
type_vars: vec![k, v],
}
}
pub fn builtin(name: &'static str) -> TypeInfo {
TypeInfo::Class {
module: ModuleName::Builtin,
name: Cow::from(name),
type_vars: vec![],
}
}
}
impl Display for TypeInfo {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
TypeInfo::Any | TypeInfo::None | TypeInfo::NoReturn => write!(f, "{}", self.name()),
TypeInfo::Callable(input, output) => {
write!(f, "Callable[")?;
if let Some(input) = input {
write!(f, "[")?;
let mut comma = false;
for arg in input {
if comma {
write!(f, ", ")?;
}
write!(f, "{}", arg)?;
comma = true;
}
write!(f, "]")?;
} else {
write!(f, "...")?;
}
write!(f, ", {}]", output)
}
TypeInfo::Tuple(types) => {
write!(f, "Tuple[")?;
if let Some(types) = types {
if types.is_empty() {
write!(f, "()")?;
} else {
let mut comma = false;
for t in types {
if comma {
write!(f, ", ")?;
}
write!(f, "{}", t)?;
comma = true;
}
}
} else {
write!(f, "...")?;
}
write!(f, "]")
}
TypeInfo::UnsizedTypedTuple(t) => write!(f, "Tuple[{}, ...]", t),
TypeInfo::Class {
name, type_vars, ..
} => {
write!(f, "{}", name)?;
if !type_vars.is_empty() {
write!(f, "[")?;
let mut comma = false;
for var in type_vars {
if comma {
write!(f, ", ")?;
}
write!(f, "{}", var)?;
comma = true;
}
write!(f, "]")
} else {
Ok(())
}
}
}
}
}
#[cfg(test)]
mod test {
use std::borrow::Cow;
use crate::inspect::types::{ModuleName, TypeInfo};
pub fn assert_display(t: &TypeInfo, expected: &str) {
assert_eq!(format!("{}", t), expected)
}
#[test]
fn basic() {
assert_display(&TypeInfo::Any, "Any");
assert_display(&TypeInfo::None, "None");
assert_display(&TypeInfo::NoReturn, "NoReturn");
assert_display(&TypeInfo::builtin("int"), "int");
}
#[test]
fn callable() {
let any_to_int = TypeInfo::Callable(None, Box::new(TypeInfo::builtin("int")));
assert_display(&any_to_int, "Callable[..., int]");
let sum = TypeInfo::Callable(
Some(vec![TypeInfo::builtin("int"), TypeInfo::builtin("int")]),
Box::new(TypeInfo::builtin("int")),
);
assert_display(&sum, "Callable[[int, int], int]");
}
#[test]
fn tuple() {
let any = TypeInfo::Tuple(None);
assert_display(&any, "Tuple[...]");
let triple = TypeInfo::Tuple(Some(vec![
TypeInfo::builtin("int"),
TypeInfo::builtin("str"),
TypeInfo::builtin("bool"),
]));
assert_display(&triple, "Tuple[int, str, bool]");
let empty = TypeInfo::Tuple(Some(vec![]));
assert_display(&empty, "Tuple[()]");
let typed = TypeInfo::UnsizedTypedTuple(Box::new(TypeInfo::builtin("bool")));
assert_display(&typed, "Tuple[bool, ...]");
}
#[test]
fn class() {
let class1 = TypeInfo::Class {
module: ModuleName::CurrentModule,
name: Cow::from("MyClass"),
type_vars: vec![],
};
assert_display(&class1, "MyClass");
let class2 = TypeInfo::Class {
module: ModuleName::CurrentModule,
name: Cow::from("MyClass"),
type_vars: vec![TypeInfo::builtin("int"), TypeInfo::builtin("bool")],
};
assert_display(&class2, "MyClass[int, bool]");
}
#[test]
fn collections() {
let int = TypeInfo::builtin("int");
let bool = TypeInfo::builtin("bool");
let str = TypeInfo::builtin("str");
let list = TypeInfo::list_of(int.clone());
assert_display(&list, "List[int]");
let sequence = TypeInfo::sequence_of(bool.clone());
assert_display(&sequence, "Sequence[bool]");
let optional = TypeInfo::optional_of(str.clone());
assert_display(&optional, "Optional[str]");
let iterable = TypeInfo::iterable_of(int.clone());
assert_display(&iterable, "Iterable[int]");
let iterator = TypeInfo::iterator_of(bool);
assert_display(&iterator, "Iterator[bool]");
let dict = TypeInfo::dict_of(int.clone(), str.clone());
assert_display(&dict, "Dict[int, str]");
let mapping = TypeInfo::mapping_of(int, str.clone());
assert_display(&mapping, "Mapping[int, str]");
let set = TypeInfo::set_of(str.clone());
assert_display(&set, "Set[str]");
let frozen_set = TypeInfo::frozen_set_of(str);
assert_display(&frozen_set, "FrozenSet[str]");
}
#[test]
fn complicated() {
let int = TypeInfo::builtin("int");
assert_display(&int, "int");
let bool = TypeInfo::builtin("bool");
assert_display(&bool, "bool");
let str = TypeInfo::builtin("str");
assert_display(&str, "str");
let any = TypeInfo::Any;
assert_display(&any, "Any");
let params = TypeInfo::union_of(&[int.clone(), str]);
assert_display(¶ms, "Union[int, str]");
let func = TypeInfo::Callable(Some(vec![params, any]), Box::new(bool));
assert_display(&func, "Callable[[Union[int, str], Any], bool]");
let dict = TypeInfo::mapping_of(int, func);
assert_display(
&dict,
"Mapping[int, Callable[[Union[int, str], Any], bool]]",
);
}
}
#[cfg(test)]
mod conversion {
use std::collections::{HashMap, HashSet};
use crate::inspect::types::test::assert_display;
use crate::{FromPyObject, IntoPy};
#[test]
fn unsigned_int() {
assert_display(&usize::type_output(), "int");
assert_display(&usize::type_input(), "int");
assert_display(&u8::type_output(), "int");
assert_display(&u8::type_input(), "int");
assert_display(&u16::type_output(), "int");
assert_display(&u16::type_input(), "int");
assert_display(&u32::type_output(), "int");
assert_display(&u32::type_input(), "int");
assert_display(&u64::type_output(), "int");
assert_display(&u64::type_input(), "int");
}
#[test]
fn signed_int() {
assert_display(&isize::type_output(), "int");
assert_display(&isize::type_input(), "int");
assert_display(&i8::type_output(), "int");
assert_display(&i8::type_input(), "int");
assert_display(&i16::type_output(), "int");
assert_display(&i16::type_input(), "int");
assert_display(&i32::type_output(), "int");
assert_display(&i32::type_input(), "int");
assert_display(&i64::type_output(), "int");
assert_display(&i64::type_input(), "int");
}
#[test]
fn float() {
assert_display(&f32::type_output(), "float");
assert_display(&f32::type_input(), "float");
assert_display(&f64::type_output(), "float");
assert_display(&f64::type_input(), "float");
}
#[test]
fn bool() {
assert_display(&bool::type_output(), "bool");
assert_display(&bool::type_input(), "bool");
}
#[test]
fn text() {
assert_display(&String::type_output(), "str");
assert_display(&String::type_input(), "str");
assert_display(&<&[u8]>::type_output(), "bytes");
assert_display(
&<&[u8] as crate::conversion::FromPyObjectBound>::type_input(),
"bytes",
);
}
#[test]
fn collections() {
assert_display(&<Vec<usize>>::type_output(), "List[int]");
assert_display(&<Vec<usize>>::type_input(), "Sequence[int]");
assert_display(&<HashSet<usize>>::type_output(), "Set[int]");
assert_display(&<HashSet<usize>>::type_input(), "Set[int]");
assert_display(&<HashMap<usize, f32>>::type_output(), "Dict[int, float]");
assert_display(&<HashMap<usize, f32>>::type_input(), "Mapping[int, float]");
assert_display(&<(usize, f32)>::type_input(), "Tuple[int, float]");
}
}