Skip to main content

zlink_names/
type_name.rs

1//! Varlink type names.
2
3use zcheapstr::CheapStr;
4
5use crate::parse;
6
7/// A Varlink type name, e.g. `FileNotFound`.
8///
9/// Custom types, methods and errors are named this way: an uppercase ASCII letter followed by
10/// ASCII letters and digits.
11///
12/// # Examples
13///
14/// ```
15/// use zlink_names::TypeName;
16///
17/// let name = TypeName::try_from("FileNotFound")?;
18/// assert_eq!(name, "FileNotFound");
19/// assert!(TypeName::try_from("fileNotFound").is_err());
20/// # Ok::<(), zlink_names::Error>(())
21/// ```
22#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct TypeName<'a>(pub(crate) CheapStr<'a>);
24
25/// An owned [`TypeName`], independent of the data it was created from.
26#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub struct OwnedTypeName(TypeName<'static>);
28
29impl_name!(
30    TypeName,
31    OwnedTypeName,
32    is_valid_type_name,
33    crate::Error::InvalidTypeName,
34    "a Varlink type name"
35);
36
37/// Whether `name` is a valid Varlink type name.
38///
39/// Method and error names follow this rule too.
40///
41/// # Examples
42///
43/// ```
44/// use zlink_names::is_valid_type_name;
45///
46/// assert!(is_valid_type_name("FileNotFound"));
47/// assert!(!is_valid_type_name("fileNotFound"));
48/// ```
49pub fn is_valid_type_name(name: &str) -> bool {
50    parse::parses_fully(parse::type_name, name)
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use crate::{Error, is_valid_field_name};
57
58    #[test]
59    fn type_names() {
60        let valid = ["A", "Foo", "FileNotFound", "Foo0", "X"];
61        for name in valid {
62            assert!(is_valid_type_name(name), "`{name}` must be valid");
63        }
64
65        let invalid = [
66            "", "foo", "_Foo", "0Foo", "Foo_bar", "Foo-bar", "Foo bar", "Foo.Bar", "Foo!", "Über",
67        ];
68        for name in invalid {
69            assert!(!is_valid_type_name(name), "`{name}` must be invalid");
70        }
71    }
72
73    /// Type names are the stricter rule: every one of them is also a valid field name.
74    #[test]
75    fn type_names_are_field_names() {
76        for name in ["A", "Foo", "FileNotFound", "Foo0"] {
77            assert!(is_valid_field_name(name), "`{name}` must be valid");
78        }
79    }
80
81    #[test]
82    fn try_from() {
83        let name = TypeName::try_from("FileNotFound").unwrap();
84        assert_eq!(name.as_str(), "FileNotFound");
85        let name = TypeName::try_from(String::from("FileNotFound")).unwrap();
86        assert_eq!(name.as_str(), "FileNotFound");
87
88        assert_eq!(
89            TypeName::try_from("fileNotFound"),
90            Err(Error::InvalidTypeName)
91        );
92        assert_eq!(
93            TypeName::try_from(String::from("File_not_found")),
94            Err(Error::InvalidTypeName)
95        );
96    }
97
98    #[test]
99    fn owned_try_from() {
100        let name = OwnedTypeName::try_from("FileNotFound").unwrap();
101        assert_eq!(name.as_str(), "FileNotFound");
102        let name = OwnedTypeName::try_from(String::from("FileNotFound")).unwrap();
103        assert_eq!(name.as_str(), "FileNotFound");
104
105        assert_eq!(
106            OwnedTypeName::try_from("fileNotFound"),
107            Err(Error::InvalidTypeName)
108        );
109        assert_eq!(
110            OwnedTypeName::try_from(String::new()),
111            Err(Error::InvalidTypeName)
112        );
113    }
114}