aranya_policy_text/
ident.rs

1use alloc::string::String;
2use core::{borrow::Borrow, fmt, num::NonZeroUsize, str::FromStr};
3
4use serde::de;
5
6use crate::{
7    Text,
8    error::{InvalidIdentifier, InvalidIdentifierRepr},
9    repr::Repr,
10};
11
12#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
13/// A textual identifier which matches `[a-zA-Z][a-zA-Z0-9_]*`.
14pub struct Identifier(Text);
15
16/// Creates an `Identifier` from a string literal.
17///
18/// Fails at compile time for invalid values.
19#[macro_export]
20macro_rules! ident {
21    ($($e:tt)+) => {
22        // SAFETY: `validate_identifier` validates `Identifier`'s requirements.
23        unsafe {
24            $crate::Identifier::__from_literal($crate::__hidden::validate_identifier!($($e)+))
25        }
26    };
27}
28
29impl Identifier {
30    fn validate(s: &str) -> Result<(), InvalidIdentifier> {
31        if s.is_empty() {
32            return Err(InvalidIdentifier(InvalidIdentifierRepr::NotEmpty));
33        }
34        for (i, b) in s.bytes().enumerate() {
35            // Check tail characters
36            if let Some(index) = NonZeroUsize::new(i) {
37                if !(b.is_ascii_alphanumeric() || b == b'_') {
38                    return Err(InvalidIdentifier(InvalidIdentifierRepr::TrailingNotValid {
39                        index,
40                    }));
41                }
42            // Check first character
43            } else if !b.is_ascii_alphabetic() {
44                return Err(InvalidIdentifier(
45                    InvalidIdentifierRepr::InitialNotAlphabetic,
46                ));
47            }
48        }
49        debug_assert!(Text::validate(s).is_ok(), "identifiers are valid text");
50        Ok(())
51    }
52
53    /// SAFETY: The string must meet `Identifier`'s requirements.
54    #[doc(hidden)]
55    pub const unsafe fn __from_literal(lit: &'static str) -> Self {
56        // SAFETY: Valid identifiers are valid text.
57        unsafe { Self(Text::__from_literal(lit)) }
58    }
59
60    /// Compare two identifiers for equality.
61    ///
62    /// Like `Eq` but `const`.
63    pub const fn const_eq(&self, other: &Self) -> bool {
64        self.0.const_eq(&other.0)
65    }
66
67    /// Extracts a string slice containing the entire identifier.
68    pub const fn as_str(&self) -> &str {
69        self.0.as_str()
70    }
71}
72
73impl fmt::Display for Identifier {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        self.0.fmt(f)
76    }
77}
78
79impl fmt::Debug for Identifier {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        self.0.fmt(f)
82    }
83}
84
85// identifiers are more strict than text
86impl From<Identifier> for Text {
87    fn from(value: Identifier) -> Self {
88        value.0
89    }
90}
91
92impl PartialEq<str> for Identifier {
93    fn eq(&self, other: &str) -> bool {
94        self.0.eq(other)
95    }
96}
97impl PartialEq<&str> for Identifier {
98    fn eq(&self, other: &&str) -> bool {
99        self.0.eq(other)
100    }
101}
102
103impl TryFrom<Text> for Identifier {
104    type Error = InvalidIdentifier;
105    fn try_from(value: Text) -> Result<Self, Self::Error> {
106        Self::validate(value.as_str())?;
107        Ok(Self(value))
108    }
109}
110
111impl FromStr for Identifier {
112    type Err = InvalidIdentifier;
113    fn from_str(s: &str) -> Result<Self, Self::Err> {
114        Self::validate(s)?;
115        Ok(Self(Text(Repr::from_str(s))))
116    }
117}
118
119impl TryFrom<String> for Identifier {
120    type Error = InvalidIdentifier;
121    fn try_from(s: String) -> Result<Self, Self::Error> {
122        s.as_str().parse()
123    }
124}
125
126impl AsRef<str> for Identifier {
127    fn as_ref(&self) -> &str {
128        self.as_str()
129    }
130}
131
132impl Borrow<str> for Identifier {
133    fn borrow(&self) -> &str {
134        self.as_str()
135    }
136}
137
138impl serde::Serialize for Identifier {
139    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
140    where
141        S: serde::Serializer,
142    {
143        self.0.serialize(serializer)
144    }
145}
146
147impl<'de> de::Deserialize<'de> for Identifier {
148    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
149    where
150        D: de::Deserializer<'de>,
151    {
152        let r = Repr::deserialize(deserializer)?;
153        Self::validate(r.as_str()).map_err(|_| {
154            de::Error::invalid_value(
155                de::Unexpected::Str(r.as_str()),
156                &"must match `[a-zA-Z][a-zA-Z0-9_]*`",
157            )
158        })?;
159        Ok(Self(Text(r)))
160    }
161}