Skip to main content

telltale_runtime/
identifiers.rs

1//! Typed identifiers used across topology and runtime APIs.
2
3use std::fmt;
4use std::sync::Arc;
5
6use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub struct IdentifierError {
10    pub kind: &'static str,
11    pub value: String,
12}
13
14impl fmt::Display for IdentifierError {
15    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16        write!(f, "invalid {} identifier: {}", self.kind, self.value)
17    }
18}
19
20impl std::error::Error for IdentifierError {}
21
22fn is_ident_start(ch: char) -> bool {
23    ch.is_ascii_alphabetic() || ch == '_'
24}
25
26fn is_ident_continue(ch: char) -> bool {
27    ch.is_ascii_alphanumeric() || ch == '_'
28}
29
30fn validate_ident(kind: &'static str, value: &str) -> Result<(), IdentifierError> {
31    let mut chars = value.chars();
32    let Some(first) = chars.next() else {
33        return Err(IdentifierError {
34            kind,
35            value: value.to_string(),
36        });
37    };
38    if !is_ident_start(first) {
39        return Err(IdentifierError {
40            kind,
41            value: value.to_string(),
42        });
43    }
44    if !chars.all(is_ident_continue) {
45        return Err(IdentifierError {
46            kind,
47            value: value.to_string(),
48        });
49    }
50    Ok(())
51}
52
53#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
54pub struct RoleName(Arc<str>);
55
56impl RoleName {
57    pub fn new(value: impl Into<String>) -> Result<Self, IdentifierError> {
58        let value = value.into();
59        validate_ident("role", &value)?;
60        Ok(Self(Arc::from(value)))
61    }
62
63    pub fn from_static(value: &'static str) -> Self {
64        debug_assert!(validate_ident("role", value).is_ok());
65        Self(Arc::from(value))
66    }
67
68    pub fn as_str(&self) -> &str {
69        &self.0
70    }
71}
72
73impl fmt::Debug for RoleName {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        write!(f, "RoleName({})", self.0)
76    }
77}
78
79impl fmt::Display for RoleName {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        f.write_str(&self.0)
82    }
83}
84
85impl Serialize for RoleName {
86    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
87    where
88        S: Serializer,
89    {
90        serializer.serialize_str(self.as_str())
91    }
92}
93
94impl<'de> Deserialize<'de> for RoleName {
95    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
96    where
97        D: Deserializer<'de>,
98    {
99        let value = String::deserialize(deserializer)?;
100        RoleName::new(value).map_err(de::Error::custom)
101    }
102}
103
104#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
105pub struct Namespace(Arc<str>);
106
107impl Namespace {
108    pub fn new(value: impl Into<String>) -> Result<Self, IdentifierError> {
109        let value = value.into();
110        validate_ident("namespace", &value)?;
111        Ok(Self(Arc::from(value)))
112    }
113
114    pub fn as_str(&self) -> &str {
115        &self.0
116    }
117}
118
119impl fmt::Debug for Namespace {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        write!(f, "Namespace({})", self.0)
122    }
123}
124
125impl fmt::Display for Namespace {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        f.write_str(&self.0)
128    }
129}
130
131impl Serialize for Namespace {
132    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
133    where
134        S: Serializer,
135    {
136        serializer.serialize_str(self.as_str())
137    }
138}
139
140impl<'de> Deserialize<'de> for Namespace {
141    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
142    where
143        D: Deserializer<'de>,
144    {
145        let value = String::deserialize(deserializer)?;
146        Namespace::new(value).map_err(de::Error::custom)
147    }
148}
149
150#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
151pub struct Datacenter(Arc<str>);
152
153impl Datacenter {
154    pub fn new(value: impl Into<String>) -> Result<Self, IdentifierError> {
155        let value = value.into();
156        validate_ident("datacenter", &value)?;
157        Ok(Self(Arc::from(value)))
158    }
159
160    pub fn as_str(&self) -> &str {
161        &self.0
162    }
163}
164
165impl fmt::Debug for Datacenter {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        write!(f, "Datacenter({})", self.0)
168    }
169}
170
171impl fmt::Display for Datacenter {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        f.write_str(&self.0)
174    }
175}
176
177impl Serialize for Datacenter {
178    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
179    where
180        S: Serializer,
181    {
182        serializer.serialize_str(self.as_str())
183    }
184}
185
186impl<'de> Deserialize<'de> for Datacenter {
187    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
188    where
189        D: Deserializer<'de>,
190    {
191        let value = String::deserialize(deserializer)?;
192        Datacenter::new(value).map_err(de::Error::custom)
193    }
194}
195
196#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
197pub struct Region(Arc<str>);
198
199impl Region {
200    pub fn new(value: impl Into<String>) -> Result<Self, IdentifierError> {
201        let value = value.into();
202        validate_ident("region", &value)?;
203        Ok(Self(Arc::from(value)))
204    }
205
206    pub fn as_str(&self) -> &str {
207        &self.0
208    }
209}
210
211impl fmt::Debug for Region {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        write!(f, "Region({})", self.0)
214    }
215}
216
217impl fmt::Display for Region {
218    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219        f.write_str(&self.0)
220    }
221}
222
223impl Serialize for Region {
224    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
225    where
226        S: Serializer,
227    {
228        serializer.serialize_str(self.as_str())
229    }
230}
231
232impl<'de> Deserialize<'de> for Region {
233    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
234    where
235        D: Deserializer<'de>,
236    {
237        let value = String::deserialize(deserializer)?;
238        Region::new(value).map_err(de::Error::custom)
239    }
240}
241
242#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
243pub struct Endpoint(Arc<str>);
244
245impl Endpoint {
246    pub fn new(value: impl Into<String>) -> Result<Self, IdentifierError> {
247        let value = value.into();
248        if value.trim().is_empty() {
249            return Err(IdentifierError {
250                kind: "endpoint",
251                value,
252            });
253        }
254        Ok(Self(Arc::from(value)))
255    }
256
257    pub fn as_str(&self) -> &str {
258        &self.0
259    }
260}
261
262impl fmt::Debug for Endpoint {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        write!(f, "Endpoint({})", self.0)
265    }
266}
267
268impl fmt::Display for Endpoint {
269    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270        f.write_str(&self.0)
271    }
272}
273
274impl Serialize for Endpoint {
275    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
276    where
277        S: Serializer,
278    {
279        serializer.serialize_str(self.as_str())
280    }
281}
282
283impl<'de> Deserialize<'de> for Endpoint {
284    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
285    where
286        D: Deserializer<'de>,
287    {
288        let value = String::deserialize(deserializer)?;
289        Endpoint::new(value).map_err(de::Error::custom)
290    }
291}