use std::fmt;
use std::str::FromStr;
use crate::errors::ParamParseError;
#[derive(Debug, PartialEq)]
pub struct Type {
pub name: String,
pub bare: bool,
pub generic_arg: Option<Box<Type>>,
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)?;
if let Some(generic_arg) = &self.generic_arg {
write!(f, "<{generic_arg}>")?;
}
Ok(())
}
}
impl FromStr for Type {
type Err = ParamParseError;
fn from_str(ty: &str) -> Result<Self, Self::Err> {
let (ty, generic_arg) = if let Some(pos) = ty.find('<') {
if !ty.ends_with('>') {
return Err(ParamParseError::InvalidGeneric);
}
(
&ty[..pos],
Some(Box::new(Type::from_str(&ty[pos + 1..ty.len() - 1])?)),
)
} else {
(ty, None)
};
if ty.is_empty() {
return Err(ParamParseError::Empty);
}
let bare = ty.chars().next().unwrap().is_ascii_lowercase();
Ok(Self {
name: ty.into(),
bare,
generic_arg,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_empty_simple() {
assert_eq!(Type::from_str(""), Err(ParamParseError::Empty));
}
#[test]
fn check_simple() {
assert_eq!(
Type::from_str("foo"),
Ok(Type {
name: "foo".into(),
bare: true,
generic_arg: None,
})
);
}
#[test]
fn check_empty() {
assert_eq!(Type::from_str(""), Err(ParamParseError::Empty));
}
#[test]
fn check_bare() {
assert!(matches!(Type::from_str("foo"), Ok(Type { bare: true, .. })));
assert!(matches!(
Type::from_str("Foo"),
Ok(Type { bare: false, .. })
));
}
#[test]
fn check_generic_arg() {
assert!(matches!(
Type::from_str("foo"),
Ok(Type {
generic_arg: None,
..
})
));
assert!(match Type::from_str("foo<bar>") {
Ok(Type {
generic_arg: Some(x),
..
}) => *x == "bar".parse().unwrap(),
_ => false,
});
assert!(match Type::from_str("foo<bar>") {
Ok(Type {
generic_arg: Some(x),
..
}) => *x == "bar".parse().unwrap(),
_ => false,
});
assert!(match Type::from_str("foo<bar<baz>>") {
Ok(Type {
generic_arg: Some(x),
..
}) => *x == "bar<baz>".parse().unwrap(),
_ => false,
});
}
}