1use std::str::FromStr;
2
3use rand::RngCore;
4use serde::de::Error;
5
6use super::INTERNED_ID_REGEX;
7use crate::{format::DataFormat, INTERN};
8
9#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
10pub struct InternedId(lasso::Spur);
11
12impl InternedId {
13 pub fn rand() -> Self {
14 let id = rand::thread_rng().next_u32();
15 Self(INTERN.get_or_intern(format!("unknown-{}", id)))
16 }
17
18 pub fn into_inner(self) -> u32 {
19 self.0.into_inner().get()
20 }
21
22 pub fn is_match(s: &str) -> bool {
23 INTERNED_ID_REGEX.is_match(s)
24 }
25
26 pub fn compute_id() -> Self {
27 Self(INTERN.get_or_intern("compute"))
28 }
29}
30
31impl Default for InternedId {
32 fn default() -> Self {
33 Self(INTERN.get_or_intern("default"))
34 }
35}
36
37impl std::cmp::PartialOrd for InternedId {
38 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
39 Some(std::convert::AsRef::<str>::as_ref(self).cmp(other.as_ref()))
40 }
41}
42
43impl Ord for InternedId {
44 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
45 std::convert::AsRef::<str>::as_ref(self).cmp(other.as_ref())
46 }
47}
48
49pub fn id_or_none<T: FromStr>(s: &str) -> Option<T> {
52 if !INTERN.contains(s) {
53 return None;
54 }
55 T::from_str(s).ok()
56}
57
58impl FromStr for InternedId {
59 type Err = String;
60
61 fn from_str(s: &str) -> Result<Self, Self::Err> {
62 if !InternedId::is_match(s) {
63 return Err(format!(
64 "invalid {} expected pattern [A-Za-z0-9][A-Za-z0-9\\-_.]{{,63}}",
65 stringify!(InternedId)
66 ));
67 }
68
69 Ok(InternedId(INTERN.get_or_intern(s)))
70 }
71}
72
73impl<'de> serde::Deserialize<'de> for InternedId {
74 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
75 where
76 D: serde::Deserializer<'de>,
77 {
78 let s = String::deserialize(deserializer)?;
79 Self::from_str(&s).map_err(D::Error::custom)
80 }
81}
82
83impl std::fmt::Display for InternedId {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 write!(f, "{}", INTERN.resolve(&self.0))
86 }
87}
88
89impl AsRef<str> for InternedId {
90 fn as_ref(&self) -> &str {
91 INTERN.resolve(&self.0)
92 }
93}
94
95impl AsRef<[u8]> for InternedId {
96 fn as_ref(&self) -> &[u8] {
97 INTERN.resolve(&self.0).as_bytes()
98 }
99}
100
101impl serde::Serialize for InternedId {
102 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
103 where
104 S: serde::Serializer,
105 {
106 serializer.serialize_str(self.as_ref())
107 }
108}
109
110impl DataFormat for InternedId {
111 type Header = ();
112 const LATEST_HEADER: Self::Header = ();
113
114 fn write_data<W: std::io::prelude::Write>(
115 &self,
116 writer: &mut W,
117 ) -> Result<usize, crate::format::DataWriteError> {
118 self.0.write_data(writer)
119 }
120
121 fn read_data<R: std::io::prelude::Read>(
122 reader: &mut R,
123 _header: &Self::Header,
124 ) -> Result<Self, crate::format::DataReadError> {
125 Ok(InternedId(lasso::Spur::read_data(reader, &())?))
126 }
127}
128
129#[cfg(test)]
130mod test {
131 use super::*;
132
133 #[test]
134 fn test_interned_id() {
135 let id = InternedId::rand();
136 let s = id.to_string();
137 let id2 = InternedId::from_str(&s).unwrap();
138 assert_eq!(id, id2);
139 }
140
141 #[test]
142 fn test_interned_id_dataformat() {
143 let id = InternedId::rand();
144 let mut buf = Vec::new();
145 id.write_data(&mut buf).unwrap();
146 let id2 = InternedId::read_data(&mut buf.as_slice(), &()).unwrap();
147 assert_eq!(id, id2);
148 }
149}