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