1
use std::{
2
    borrow::Cow,
3
    fmt::{Debug, Display, Write},
4
    ops::Deref,
5
    sync::Arc,
6
};
7

            
8
use serde::{Deserialize, Serialize};
9

            
10
/// A schema name. Cloning is inexpensive.
11
245196929
#[derive(Hash, PartialEq, Eq, Deserialize, Serialize, Debug, Clone, Ord, PartialOrd)]
12
#[serde(try_from = "String")]
13
#[serde(into = "String")]
14
pub struct Name {
15
    name: Arc<Cow<'static, str>>,
16
    needs_escaping: bool,
17
}
18

            
19
/// A name was unable to e parsed.
20
#[derive(thiserror::Error, Debug, Serialize, Deserialize, Clone)]
21
#[error("invalid name: {0}")]
22
pub struct InvalidNameError(pub String);
23

            
24
impl Name {
25
    /// Creates a new name.
26
20430467
    pub fn new<T: Into<Self>>(contents: T) -> Self {
27
20430467
        contents.into()
28
20430467
    }
29

            
30
    /// Parses a name that was previously encoded via [`Self::encoded()`].
31
    ///
32
    /// # Errors
33
    ///
34
    /// Returns [`InvalidNameError`] if the name contains invalid escape
35
    /// sequences.
36
443
    pub fn parse_encoded(encoded: &str) -> Result<Self, InvalidNameError> {
37
443
        let mut bytes = encoded.bytes();
38
443
        let mut decoded = Vec::with_capacity(encoded.len());
39
4683
        while let Some(byte) = bytes.next() {
40
4244
            if byte == b'_' {
41
158
                if let (Some(high), Some(low)) = (bytes.next(), bytes.next()) {
42
155
                    if let Some(byte) = hex_chars_to_byte(high, low) {
43
154
                        decoded.push(byte);
44
154
                        continue;
45
1
                    }
46
3
                }
47
4
                return Err(InvalidNameError(encoded.to_string()));
48
4086
            }
49
4086

            
50
4086
            decoded.push(byte);
51
        }
52

            
53
439
        String::from_utf8(decoded)
54
439
            .map(Self::from)
55
439
            .map_err(|_| InvalidNameError(encoded.to_string()))
56
443
    }
57

            
58
    /// Returns an encoded version of this name that contains only alphanumeric
59
    /// ASCII, underscore, and hyphen.
60
    #[must_use]
61
1
    pub fn encoded(&self) -> String {
62
1
        format!("{:#}", self)
63
1
    }
64
}
65

            
66
impl From<Cow<'static, str>> for Name {
67
137479558
    fn from(value: Cow<'static, str>) -> Self {
68
137479558
        let needs_escaping = !value
69
137479558
            .bytes()
70
1197566805
            .all(|b| b.is_ascii_alphanumeric() || b == b'-');
71
137479558
        Self {
72
137479558
            name: Arc::new(value),
73
137479558
            needs_escaping,
74
137479558
        }
75
137479558
    }
76
}
77

            
78
impl From<&'static str> for Name {
79
71746208
    fn from(value: &'static str) -> Self {
80
71746208
        Self::from(Cow::Borrowed(value))
81
71746208
    }
82
}
83

            
84
impl From<String> for Name {
85
14463210
    fn from(value: String) -> Self {
86
14463210
        Self::from(Cow::Owned(value))
87
14463210
    }
88
}
89

            
90
#[allow(clippy::from_over_into)] // the auto into impl doesn't work with serde(into)
91
impl Into<String> for Name {
92
23447377
    fn into(self) -> String {
93
23447377
        self.name.to_string()
94
23447377
    }
95
}
96

            
97
impl Display for Name {
98
26246564
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99
26246564
        if f.alternate() && self.needs_escaping {
100
68378088
            for byte in self.name.bytes() {
101
68378088
                if byte.is_ascii_alphanumeric() || byte == b'-' {
102
60345679
                    f.write_char(byte as char)?;
103
                } else {
104
                    // Encode the byte as _FF
105
8056341
                    f.write_char('_')?;
106
8056341
                    f.write_char(nibble_to_hex_char(byte >> 4))?;
107
8056341
                    f.write_char(nibble_to_hex_char(byte & 0xF))?;
108
                }
109
            }
110
8056564
            Ok(())
111
        } else {
112
18195270
            Display::fmt(&self.name, f)
113
        }
114
26251834
    }
115
}
116

            
117
16110109
const fn nibble_to_hex_char(nibble: u8) -> char {
118
16112930
    let ch = match nibble {
119
16109923
        0..=9 => b'0' + nibble,
120
8056801
        _ => b'a' + nibble - 10,
121
    };
122
16112930
    ch as char
123
16112930
}
124

            
125
const fn hex_chars_to_byte(high_nibble: u8, low_nibble: u8) -> Option<u8> {
126
    match (
127
155
        hex_char_to_nibble(high_nibble),
128
155
        hex_char_to_nibble(low_nibble),
129
    ) {
130
154
        (Some(high_nibble), Some(low_nibble)) => Some(high_nibble << 4 | low_nibble),
131
1
        _ => None,
132
    }
133
155
}
134

            
135
310
const fn hex_char_to_nibble(nibble: u8) -> Option<u8> {
136
310
    let ch = match nibble {
137
310
        b'0'..=b'9' => nibble - b'0',
138
150
        b'a'..=b'f' => nibble - b'a' + 10,
139
1
        _ => return None,
140
    };
141
309
    Some(ch)
142
310
}
143

            
144
impl AsRef<str> for Name {
145
860566
    fn as_ref(&self) -> &str {
146
860566
        self.name.as_ref()
147
860566
    }
148
}
149

            
150
/// The owner of a schema item. This should represent the company, group, or
151
/// individual that created the item in question. This value is used for
152
/// namespacing. Changing this after values are in use is not supported without
153
/// manual migrations at this time.
154
102589928
#[derive(Hash, PartialEq, Eq, Deserialize, Serialize, Debug, Clone, Ord, PartialOrd)]
155
#[serde(transparent)]
156
pub struct Authority(Name);
157

            
158
impl From<Cow<'static, str>> for Authority {
159
51316826
    fn from(value: Cow<'static, str>) -> Self {
160
51316826
        Self::from(Name::from(value))
161
51316826
    }
162
}
163

            
164
impl From<&'static str> for Authority {
165
51317074
    fn from(value: &'static str) -> Self {
166
51317074
        Self::from(Cow::Borrowed(value))
167
51317074
    }
168
}
169

            
170
impl From<String> for Authority {
171
    fn from(value: String) -> Self {
172
        Self::from(Cow::Owned(value))
173
    }
174
}
175

            
176
impl From<Name> for Authority {
177
51319680
    fn from(value: Name) -> Self {
178
51319680
        Self(value)
179
51319680
    }
180
}
181

            
182
impl Display for Authority {
183
10542887
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184
10542887
        Display::fmt(&self.0, f)
185
10542887
    }
186
}
187

            
188
/// A namespaced name.
189
102590858
#[derive(Hash, PartialEq, Eq, Deserialize, Serialize, Debug, Clone, Ord, PartialOrd)]
190
pub struct QualifiedName {
191
    /// The authority that defines this name.
192
    pub authority: Authority,
193

            
194
    /// The name, unique within `authority`.
195
    pub name: Name,
196
}
197

            
198
impl Display for QualifiedName {
199
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200
10542918
        Display::fmt(&self.authority, f)?;
201
10542918
        f.write_char('.')?;
202
10543011
        Display::fmt(&self.name, f)
203
10543011
    }
204
}
205

            
206
/// Functions for creating qualified names
207
pub trait Qualified: Display + Sized {
208
    /// Creates a name that is not meant to be shared with other developers or
209
    /// projects.
210
    #[must_use]
211
1334454
    fn private<N: Into<Name>>(name: N) -> Self {
212
1334454
        Self::new(Authority::from("private"), name)
213
1334454
    }
214

            
215
    /// Creates a new qualified name.
216
    #[must_use]
217
    fn new<A: Into<Authority>, N: Into<Name>>(authority: A, name: N) -> Self;
218

            
219
    /// Parses a schema name that was previously encoded via
220
    /// [`Self::encoded()`].
221
    ///
222
    /// # Errors
223
    ///
224
    /// Returns [`InvalidNameError`] if the name contains invalid escape
225
    /// sequences or contains more than two periods.
226
96
    fn parse_encoded(schema_name: &str) -> Result<Self, InvalidNameError> {
227
96
        let mut parts = schema_name.split('.');
228
96
        if let (Some(authority), Some(name), None) = (parts.next(), parts.next(), parts.next()) {
229
96
            let authority = Name::parse_encoded(authority)?;
230
96
            let name = Name::parse_encoded(name)?;
231

            
232
96
            Ok(Self::new(authority, name))
233
        } else {
234
            Err(InvalidNameError(schema_name.to_string()))
235
        }
236
96
    }
237

            
238
    /// Encodes this schema name such that the authority and name can be
239
    /// safely parsed using [`Self::parse_encoded`].
240
    #[must_use]
241
1061
    fn encoded(&self) -> String {
242
1061
        format!("{:#}", self)
243
1061
    }
244
}
245

            
246
impl Qualified for QualifiedName {
247
51317852
    fn new<A: Into<Authority>, N: Into<Name>>(authority: A, name: N) -> Self {
248
51317852
        Self {
249
51317852
            authority: authority.into(),
250
51317852
            name: name.into(),
251
51317852
        }
252
51317852
    }
253
}
254

            
255
/// The name of a [`Schema`](super::Schema).
256
2156194
#[derive(Hash, PartialEq, Eq, Deserialize, Serialize, Debug, Clone, Ord, PartialOrd)]
257
#[serde(transparent)]
258
pub struct SchemaName(pub(crate) QualifiedName);
259

            
260
impl Qualified for SchemaName {
261
2350918
    fn new<A: Into<Authority>, N: Into<Name>>(authority: A, name: N) -> Self {
262
2350918
        Self(QualifiedName::new(authority, name))
263
2350918
    }
264
}
265

            
266
impl Display for SchemaName {
267
1459
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
268
1459
        Display::fmt(&self.0, f)
269
1459
    }
270
}
271

            
272
impl Deref for SchemaName {
273
    type Target = QualifiedName;
274

            
275
    fn deref(&self) -> &Self::Target {
276
        &self.0
277
    }
278
}
279

            
280
/// The namespaced name of a [`Collection`](super::Collection).
281
97939773
#[derive(Hash, PartialEq, Eq, Deserialize, Serialize, Debug, Clone, Ord, PartialOrd)]
282
#[serde(transparent)]
283
pub struct CollectionName(pub(crate) QualifiedName);
284

            
285
impl Qualified for CollectionName {
286
46609208
    fn new<A: Into<Authority>, N: Into<Name>>(authority: A, name: N) -> Self {
287
46609208
        Self(QualifiedName::new(authority, name))
288
46609208
    }
289
}
290

            
291
impl Display for CollectionName {
292
10541614
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293
10541614
        Display::fmt(&self.0, f)
294
10541614
    }
295
}
296

            
297
impl Deref for CollectionName {
298
    type Target = QualifiedName;
299

            
300
6
    fn deref(&self) -> &Self::Target {
301
6
        &self.0
302
6
    }
303
}
304

            
305
/// The qualified name of an [`Api`](crate::api::Api).
306
5372195
#[derive(Hash, PartialEq, Eq, Deserialize, Serialize, Debug, Clone, Ord, PartialOrd)]
307
#[serde(transparent)]
308
pub struct ApiName(QualifiedName);
309

            
310
impl Qualified for ApiName {
311
2284406
    fn new<A: Into<Authority>, N: Into<Name>>(authority: A, name: N) -> Self {
312
2284406
        Self(QualifiedName::new(authority, name))
313
2284406
    }
314
}
315

            
316
impl Display for ApiName {
317
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318
        Display::fmt(&self.0, f)
319
    }
320
}
321

            
322
impl Deref for ApiName {
323
    type Target = QualifiedName;
324

            
325
    fn deref(&self) -> &Self::Target {
326
        &self.0
327
    }
328
}
329

            
330
/// The name of a [`View`](super::View).
331
40091318
#[derive(Hash, PartialEq, Eq, Deserialize, Serialize, Debug, Clone)]
332
pub struct ViewName {
333
    /// The name of the collection that contains this view.
334
    pub collection: CollectionName,
335
    /// The name of this view.
336
    pub name: Name,
337
}
338

            
339
impl ViewName {
340
    /// Creates a new view name.
341
    pub fn new<
342
        C: TryInto<CollectionName, Error = InvalidNameError>,
343
        N: TryInto<Name, Error = InvalidNameError>,
344
    >(
345
        collection: C,
346
        name: N,
347
    ) -> Result<Self, InvalidNameError> {
348
        let collection = collection.try_into()?;
349
        let name = name.try_into()?;
350
        Ok(Self { collection, name })
351
    }
352
}
353

            
354
impl Display for ViewName {
355
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
356
5166677
        Display::fmt(&self.collection, f)?;
357
5166677
        f.write_char('.')?;
358
5166863
        Display::fmt(&self.name, f)
359
5166863
    }
360
}
361

            
362
1
#[test]
363
1
fn name_escaping_tests() {
364
1
    const VALID_CHARS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-";
365
1
    const INVALID_CHARS: &str = "._hello\u{1F680}";
366
1
    const ESCAPED_INVALID: &str = "_2e_5fhello_f0_9f_9a_80";
367
1
    assert_eq!(Name::new(VALID_CHARS).to_string(), VALID_CHARS);
368
1
    assert_eq!(Name::new(INVALID_CHARS).to_string(), INVALID_CHARS);
369
1
    assert_eq!(Name::new(INVALID_CHARS).encoded(), ESCAPED_INVALID);
370
1
    assert_eq!(
371
1
        Name::parse_encoded(ESCAPED_INVALID).unwrap(),
372
1
        Name::new(INVALID_CHARS)
373
1
    );
374
1
    Name::parse_encoded("_").unwrap_err();
375
1
    Name::parse_encoded("_0").unwrap_err();
376
1
    Name::parse_encoded("_z").unwrap_err();
377
1
    Name::parse_encoded("_0z").unwrap_err();
378
1
}
379

            
380
1
#[test]
381
1
fn joined_names_tests() {
382
1
    const INVALID_CHARS: &str = "._hello\u{1F680}.._world\u{1F680}";
383
1
    const ESCAPED_INVALID: &str = "_2e_5fhello_f0_9f_9a_80._2e_5fworld_f0_9f_9a_80";
384
1
    let collection = CollectionName::parse_encoded(ESCAPED_INVALID).unwrap();
385
1
    assert_eq!(collection.to_string(), INVALID_CHARS);
386
1
    assert_eq!(collection.encoded(), ESCAPED_INVALID);
387

            
388
1
    let schema_name = SchemaName::parse_encoded(ESCAPED_INVALID).unwrap();
389
1
    assert_eq!(schema_name.to_string(), INVALID_CHARS);
390
1
    assert_eq!(schema_name.encoded(), ESCAPED_INVALID);
391
1
}