aranya_policy_text/
ident.rs1use 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)]
13pub struct Identifier(Text);
15
16#[macro_export]
20macro_rules! ident {
21 ($($e:tt)+) => {
22 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 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 } 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 #[doc(hidden)]
55 pub const unsafe fn __from_literal(lit: &'static str) -> Self {
56 unsafe { Self(Text::__from_literal(lit)) }
58 }
59
60 pub const fn const_eq(&self, other: &Self) -> bool {
64 self.0.const_eq(&other.0)
65 }
66
67 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
85impl 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}