1use std::borrow::Borrow;
24use std::cmp::Ordering;
25use std::fmt;
26use std::mem::size_of;
27use std::ops::Deref;
28use std::str::FromStr;
29use std::sync::Arc;
30
31use derive_more::*;
32use get_size::GetSize;
33use regex::Regex;
34use safecast::TryCastFrom;
35
36#[cfg(feature = "stream")]
37mod destream;
38#[cfg(feature = "hash")]
39mod hash;
40#[cfg(feature = "serde")]
41mod serde;
42
43pub const RESERVED_CHARS: [&str; 21] = [
45 "/", "..", "~", "$", "`", "&", "|", "=", "^", "{", "}", "<", ">", "'", "\"", "?", ":", "@",
46 "#", "(", ")",
47];
48
49#[derive(Debug, Display)]
51#[display("{}", msg)]
52pub struct ParseError {
53 msg: Arc<str>,
54}
55
56impl std::error::Error for ParseError {}
57
58impl From<String> for ParseError {
59 fn from(msg: String) -> Self {
60 Self { msg: msg.into() }
61 }
62}
63
64impl From<&str> for ParseError {
65 fn from(msg: &str) -> Self {
66 Self { msg: msg.into() }
67 }
68}
69
70#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
72pub struct Label {
73 id: &'static str,
74}
75
76impl Deref for Label {
77 type Target = str;
78
79 fn deref(&self) -> &Self::Target {
80 self.id
81 }
82}
83
84impl From<Label> for Id {
85 fn from(l: Label) -> Id {
86 Id { inner: l.id.into() }
87 }
88}
89
90impl PartialEq<Id> for Label {
91 fn eq(&self, other: &Id) -> bool {
92 self.id == other.as_str()
93 }
94}
95
96impl fmt::Display for Label {
97 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98 f.write_str(self.id)
99 }
100}
101
102pub const fn label(id: &'static str) -> Label {
104 Label { id }
105}
106
107#[derive(Clone, Eq, Hash, PartialEq, Ord, PartialOrd)]
109pub struct Id {
110 inner: Arc<str>,
111}
112
113impl Id {
114 #[inline]
116 pub fn as_str(&self) -> &str {
117 self.inner.as_ref()
118 }
119
120 pub fn into_inner(self) -> Arc<str> {
122 self.inner
123 }
124
125 pub fn starts_with(&self, prefix: &str) -> bool {
127 self.inner.starts_with(prefix)
128 }
129}
130
131impl GetSize for Id {
132 fn get_size(&self) -> usize {
133 size_of::<Arc<str>>() + self.inner.as_bytes().len()
135 }
136}
137
138#[cfg(feature = "uuid")]
139impl From<uuid::Uuid> for Id {
140 fn from(id: uuid::Uuid) -> Self {
141 Self {
142 inner: id.to_string().into(),
143 }
144 }
145}
146
147impl Borrow<str> for Id {
148 fn borrow(&self) -> &str {
149 &self.inner
150 }
151}
152
153impl PartialEq<String> for Id {
154 fn eq(&self, other: &String) -> bool {
155 self.inner.as_ref() == other.as_str()
156 }
157}
158
159impl PartialEq<str> for Id {
160 fn eq(&self, other: &str) -> bool {
161 self.inner.as_ref() == other
162 }
163}
164
165impl<'a> PartialEq<&'a str> for Id {
166 fn eq(&self, other: &&'a str) -> bool {
167 self.inner.as_ref() == *other
168 }
169}
170
171impl PartialEq<Label> for Id {
172 fn eq(&self, other: &Label) -> bool {
173 self.inner.as_ref() == other.id
174 }
175}
176
177impl PartialEq<Id> for &str {
178 fn eq(&self, other: &Id) -> bool {
179 *self == other.inner.as_ref()
180 }
181}
182
183impl PartialOrd<String> for Id {
184 fn partial_cmp(&self, other: &String) -> Option<Ordering> {
185 self.inner.as_ref().partial_cmp(other.as_str())
186 }
187}
188
189impl PartialOrd<str> for Id {
190 fn partial_cmp(&self, other: &str) -> Option<Ordering> {
191 self.inner.as_ref().partial_cmp(other)
192 }
193}
194
195impl<'a> PartialOrd<&'a str> for Id {
196 fn partial_cmp(&self, other: &&'a str) -> Option<Ordering> {
197 self.inner.as_ref().partial_cmp(*other)
198 }
199}
200
201impl From<usize> for Id {
202 fn from(u: usize) -> Id {
203 u.to_string().parse().expect("usize")
204 }
205}
206
207impl From<u64> for Id {
208 fn from(i: u64) -> Id {
209 i.to_string().parse().expect("64-bit unsigned int")
210 }
211}
212
213impl FromStr for Id {
214 type Err = ParseError;
215
216 fn from_str(id: &str) -> Result<Self, Self::Err> {
217 validate_id(id)?;
218
219 Ok(Id { inner: id.into() })
220 }
221}
222
223impl TryCastFrom<String> for Id {
224 fn can_cast_from(id: &String) -> bool {
225 validate_id(id).is_ok()
226 }
227
228 fn opt_cast_from(id: String) -> Option<Id> {
229 id.parse().ok()
230 }
231}
232
233impl TryCastFrom<Id> for usize {
234 fn can_cast_from(id: &Id) -> bool {
235 id.as_str().parse::<usize>().is_ok()
236 }
237
238 fn opt_cast_from(id: Id) -> Option<usize> {
239 id.as_str().parse::<usize>().ok()
240 }
241}
242
243impl fmt::Debug for Id {
244 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245 f.write_str(&self.inner)
246 }
247}
248
249impl fmt::Display for Id {
250 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
251 f.write_str(&self.inner)
252 }
253}
254
255fn validate_id(id: &str) -> Result<(), ParseError> {
256 if id.is_empty() {
257 return Err("cannot construct an empty Id".into());
258 }
259
260 let mut invalid_chars = id.chars().filter(|c| (*c as u8) < 32u8);
261 if let Some(invalid) = invalid_chars.next() {
262 return Err(format!(
263 "Id {} contains ASCII control characters {}",
264 id, invalid as u8,
265 )
266 .into());
267 }
268
269 for pattern in &RESERVED_CHARS {
270 if id.contains(pattern) {
271 return Err(format!("Id {} contains disallowed pattern {}", id, pattern).into());
272 }
273 }
274
275 if let Some(w) = Regex::new(r"\s").expect("whitespace regex").find(id) {
276 return Err(format!("Id {} is not allowed to contain whitespace {:?}", id, w).into());
277 }
278
279 Ok(())
280}