cedar_policy_core/ast/
id.rs1use serde::{Deserialize, Deserializer, Serialize};
18use smol_str::SmolStr;
19
20use crate::{parser::err::ParseErrors, FromNormalizedStr};
21
22#[derive(Serialize, Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
29pub struct Id(SmolStr);
30
31impl Id {
32 pub(crate) fn new_unchecked(s: impl Into<SmolStr>) -> Id {
46 Id(s.into())
47 }
48
49 pub fn into_smolstr(self) -> SmolStr {
51 self.0
52 }
53}
54
55impl AsRef<str> for Id {
56 fn as_ref(&self) -> &str {
57 &self.0
58 }
59}
60
61impl std::fmt::Display for Id {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 write!(f, "{}", &self.0)
64 }
65}
66
67impl std::str::FromStr for Id {
69 type Err = ParseErrors;
70
71 fn from_str(s: &str) -> Result<Self, Self::Err> {
72 crate::parser::parse_ident(s)
73 }
74}
75
76impl FromNormalizedStr for Id {
77 fn describe_self() -> &'static str {
78 "Id"
79 }
80}
81
82struct IdVisitor;
83
84impl<'de> serde::de::Visitor<'de> for IdVisitor {
85 type Value = Id;
86
87 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88 formatter.write_str("a valid id")
89 }
90
91 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
92 where
93 E: serde::de::Error,
94 {
95 Id::from_normalized_str(value)
96 .map_err(|err| serde::de::Error::custom(format!("invalid id `{value}`: {err}")))
97 }
98}
99
100impl<'de> Deserialize<'de> for Id {
103 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
104 where
105 D: Deserializer<'de>,
106 {
107 deserializer.deserialize_str(IdVisitor)
108 }
109}
110
111#[cfg(feature = "arbitrary")]
112impl<'a> arbitrary::Arbitrary<'a> for Id {
113 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
114 let construct_list = |s: &str| s.chars().collect::<Vec<char>>();
120 let list_concat = |s1: &[char], s2: &[char]| [s1, s2].concat();
121 let head_letters = construct_list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_");
123 let tail_letters = list_concat(&construct_list("0123456789"), &head_letters);
125 let remaining_length = u.int_in_range(0..=16)?;
127 let mut cs = vec![*u.choose(&head_letters)?];
128 cs.extend(
129 (0..remaining_length)
130 .map(|_| u.choose(&tail_letters))
131 .collect::<Result<Vec<&char>, _>>()?,
132 );
133 let mut s: String = cs.into_iter().collect();
134 if crate::parser::parse_ident(&s).is_err() {
137 s.push('_');
138 }
139 Ok(Self::new_unchecked(s))
140 }
141
142 fn size_hint(depth: usize) -> (usize, Option<usize>) {
143 arbitrary::size_hint::and_all(&[
144 <usize as arbitrary::Arbitrary>::size_hint(depth),
146 <Vec<u8> as arbitrary::Arbitrary>::size_hint(depth),
149 ])
150 }
151}
152
153#[derive(Serialize, Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
158pub struct AnyId(SmolStr);
159
160impl AnyId {
161 pub(crate) fn new_unchecked(s: impl Into<SmolStr>) -> AnyId {
171 AnyId(s.into())
172 }
173
174 pub fn into_smolstr(self) -> SmolStr {
176 self.0
177 }
178}
179
180struct AnyIdVisitor;
181
182impl<'de> serde::de::Visitor<'de> for AnyIdVisitor {
183 type Value = AnyId;
184
185 fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 formatter.write_str("any id")
187 }
188
189 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
190 where
191 E: serde::de::Error,
192 {
193 AnyId::from_normalized_str(value)
194 .map_err(|err| serde::de::Error::custom(format!("invalid id `{value}`: {err}")))
195 }
196}
197
198impl<'de> Deserialize<'de> for AnyId {
201 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
202 where
203 D: Deserializer<'de>,
204 {
205 deserializer.deserialize_str(AnyIdVisitor)
206 }
207}
208
209impl AsRef<str> for AnyId {
210 fn as_ref(&self) -> &str {
211 &self.0
212 }
213}
214
215impl std::fmt::Display for AnyId {
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 write!(f, "{}", &self.0)
218 }
219}
220
221impl std::str::FromStr for AnyId {
223 type Err = ParseErrors;
224
225 fn from_str(s: &str) -> Result<Self, Self::Err> {
226 crate::parser::parse_anyid(s)
227 }
228}
229
230impl FromNormalizedStr for AnyId {
231 fn describe_self() -> &'static str {
232 "AnyId"
233 }
234}
235
236#[cfg(feature = "arbitrary")]
237impl<'a> arbitrary::Arbitrary<'a> for AnyId {
238 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
239 let construct_list = |s: &str| s.chars().collect::<Vec<char>>();
243 let list_concat = |s1: &[char], s2: &[char]| [s1, s2].concat();
244 let head_letters = construct_list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_");
246 let tail_letters = list_concat(&construct_list("0123456789"), &head_letters);
248 let remaining_length = u.int_in_range(0..=16)?;
250 let mut cs = vec![*u.choose(&head_letters)?];
251 cs.extend(
252 (0..remaining_length)
253 .map(|_| u.choose(&tail_letters))
254 .collect::<Result<Vec<&char>, _>>()?,
255 );
256 let s: String = cs.into_iter().collect();
257 debug_assert!(
258 crate::parser::parse_anyid(&s).is_ok(),
259 "all strings constructed this way should be valid AnyIds, but this one is not: {s:?}"
260 );
261 Ok(Self::new_unchecked(s))
262 }
263
264 fn size_hint(depth: usize) -> (usize, Option<usize>) {
265 arbitrary::size_hint::and_all(&[
266 <usize as arbitrary::Arbitrary>::size_hint(depth),
268 <Vec<u8> as arbitrary::Arbitrary>::size_hint(depth),
271 ])
272 }
273}
274
275#[allow(clippy::panic)]
277#[cfg(test)]
278mod test {
279 use super::*;
280
281 #[test]
282 fn normalized_id() {
283 Id::from_normalized_str("foo").expect("should be OK");
284 Id::from_normalized_str("foo::bar").expect_err("shouldn't be OK");
285 Id::from_normalized_str(r#"foo::"bar""#).expect_err("shouldn't be OK");
286 Id::from_normalized_str(" foo").expect_err("shouldn't be OK");
287 Id::from_normalized_str("foo ").expect_err("shouldn't be OK");
288 Id::from_normalized_str("foo\n").expect_err("shouldn't be OK");
289 Id::from_normalized_str("foo//comment").expect_err("shouldn't be OK");
290 }
291}