hyperactor/reference/
name.rs1use rand::RngCore as _;
12use serde::Deserialize;
13use serde::Serialize;
14
15use crate::reference::lex::Lexer;
16use crate::reference::lex::ParseError;
17use crate::reference::lex::Token;
18
19pub(crate) const FLICKR_BASE_58: &str =
23 "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
24
25#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
30pub struct Uid(u64);
31
32impl Uid {
33 pub fn generate() -> Self {
35 Self(rand::thread_rng().next_u64())
36 }
37
38 pub(crate) fn zero() -> Self {
39 Self(0)
40 }
41}
42
43impl From<u64> for Uid {
44 fn from(value: u64) -> Self {
45 Self(value)
46 }
47}
48
49impl std::fmt::Display for Uid {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 let mut num = self.0;
52 let base = FLICKR_BASE_58.len() as u64;
53 let mut result = String::with_capacity(12);
54
55 for _pos in 0..12 {
56 let remainder = (num % base) as usize;
57 num /= base;
58 let c = FLICKR_BASE_58.chars().nth(remainder).unwrap();
59 result.push(c);
60 }
61 debug_assert_eq!(num, 0);
62
63 let result = result.chars().rev().collect::<String>();
64 write!(f, "{}", result)
65 }
66}
67
68impl std::str::FromStr for Uid {
69 type Err = ParseError;
70
71 fn from_str(s: &str) -> Result<Self, Self::Err> {
72 let mut lexer = Lexer::new(s);
73 let uid = lexer.next_or_eof().into_uid()?;
74 lexer.expect(Token::Eof)?;
75 Ok(uid)
76 }
77}
78
79#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
82pub struct Ident(String);
83
84impl Ident {
85 pub fn new(value: String) -> Result<Self, String> {
88 let mut chars = value.chars();
89 match chars.next() {
90 None => return Err(value),
91 Some(ch) if !unicode_ident::is_xid_start(ch) && ch != '_' => return Err(value),
92 Some(_) => (),
93 }
94 if chars.all(unicode_ident::is_xid_continue) {
95 Ok(Self(value))
96 } else {
97 Err(value)
98 }
99 }
100}
101
102impl std::fmt::Display for Ident {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 self.0.fmt(f)
105 }
106}
107
108impl std::str::FromStr for Ident {
109 type Err = String;
110
111 fn from_str(s: &str) -> Result<Self, Self::Err> {
112 Ident::new(s.to_string())
113 }
114}
115
116#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
128pub struct Name {
129 namespace: Ident,
130 base: Ident,
131 uid: Uid,
132}
133
134impl Name {
135 pub fn new(namespace: Ident, base: Ident, uid: Uid) -> Self {
138 Self {
139 namespace,
140 base,
141 uid,
142 }
143 }
144
145 pub fn generate(namespace: Ident, base: Ident) -> Self {
147 Self {
148 namespace,
149 base,
150 uid: Uid::generate(),
151 }
152 }
153}
154
155impl std::fmt::Display for Name {
156 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
157 write!(f, "{}:{}-{}", self.namespace, self.base, self.uid)
158 }
159}
160
161impl std::str::FromStr for Name {
162 type Err = ParseError;
163
164 fn from_str(s: &str) -> Result<Self, Self::Err> {
165 let mut lexer = Lexer::new(s);
166
167 let namespace = lexer.next_or_eof().into_ident()?;
168 lexer.expect(Token::Colon)?;
169 let base = lexer.next_or_eof().into_ident()?;
170 let uid = lexer.next_or_eof().into_uid()?;
171 lexer.expect(Token::Eof)?;
172
173 Ok(Name {
174 namespace,
175 base,
176 uid,
177 })
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn test_generate() {
187 assert_ne!(
188 Name::generate("namespace".parse().unwrap(), "base".parse().unwrap(),),
189 Name::generate("namespace".parse().unwrap(), "base".parse().unwrap(),),
190 );
191 }
192
193 #[test]
194 fn test_roundtrip() {
195 let name = Name::new(
196 "namespace".parse().unwrap(),
197 "base".parse().unwrap(),
198 Uid::zero(),
199 );
200 assert_eq!(name.to_string(), "namespace:base-111111111111".to_string());
201 assert_eq!(name.to_string().parse::<Name>().unwrap(), name);
202 }
203
204 #[test]
205 fn test_invalid() {
206 assert_eq!(
207 "namespace:base-lllll".parse::<Name>().unwrap_err(),
208 ParseError::Expected(Token::Uid(Uid::zero()), Token::Error("lllll".to_string()))
209 );
210 }
211}