capgrok/
namespace.rs

1use crate::error::Error;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Debug, Serialize, Deserialize, Eq, Ord, PartialEq, PartialOrd)]
6/// The namespace that a capability is valid for.
7pub struct Namespace(String);
8
9impl From<Namespace> for String {
10    fn from(from: Namespace) -> String {
11        from.0
12    }
13}
14
15impl AsRef<str> for Namespace {
16    fn as_ref(&self) -> &str {
17        self.0.as_ref()
18    }
19}
20
21impl std::fmt::Display for Namespace {
22    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
23        self.0.fmt(f)
24    }
25}
26
27impl std::str::FromStr for Namespace {
28    type Err = Error;
29
30    fn from_str(s: &str) -> Result<Self, Self::Err> {
31        let mut previous_char_was_alphanum = false;
32        for c in s.chars() {
33            if c.is_ascii_alphanumeric() {
34                previous_char_was_alphanum = true;
35                continue;
36            }
37            if c == '-' && previous_char_was_alphanum {
38                previous_char_was_alphanum = false;
39                continue;
40            }
41            if c == '-' {
42                return Err(Error::InvalidNamespaceHyphens);
43            }
44            return Err(Error::InvalidNamespaceChars);
45        }
46        if !previous_char_was_alphanum {
47            return Err(Error::InvalidNamespaceHyphens);
48        }
49        Ok(Namespace(s.into()))
50    }
51}
52
53#[cfg(test)]
54mod test {
55    use super::*;
56
57    #[test]
58    fn invalid_namespace() {
59        for s in [
60            "https://example.com/",
61            "-my-namespace",
62            "my-namespace-",
63            "my--namespace",
64        ] {
65            s.parse::<Namespace>().unwrap_err();
66        }
67    }
68
69    #[test]
70    fn valid_namespace() {
71        for s in ["my-namespace", "My-nAmespac3-2"] {
72            s.parse::<Namespace>().unwrap();
73        }
74    }
75}