dtz_identifier/
apikey_id.rs

1static PREFIX: &str = "apikey-";
2
3#[derive(Debug, Clone, PartialEq, Copy)]
4pub struct ApiKeyId {
5    pub id: uuid::Uuid,
6}
7
8impl Default for ApiKeyId {
9    fn default() -> Self {
10        Self {
11            id: uuid::Uuid::new_v4(),
12        }
13    }
14}
15
16impl std::fmt::Display for ApiKeyId {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        f.write_str(&format!("{PREFIX}{}", self.id))
19    }
20}
21
22impl<'de> serde::Deserialize<'de> for ApiKeyId {
23    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
24    where
25        D: serde::Deserializer<'de>,
26    {
27        struct ApiKeyIdVisitor;
28
29        impl serde::de::Visitor<'_> for ApiKeyIdVisitor {
30            type Value = ApiKeyId;
31
32            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
33                formatter.write_str("a string starting with 'apikey-' followed by a UUID")
34            }
35
36            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
37            where
38                E: serde::de::Error,
39            {
40                if let Some(uuid_str) = value.strip_prefix(PREFIX) {
41                    let uuid = uuid::Uuid::parse_str(uuid_str).map_err(E::custom)?;
42                    Ok(ApiKeyId { id: uuid })
43                } else {
44                    Err(E::custom("invalid format"))
45                }
46            }
47        }
48
49        deserializer.deserialize_str(ApiKeyIdVisitor)
50    }
51}
52
53impl serde::Serialize for ApiKeyId {
54    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
55    where
56        S: serde::Serializer,
57    {
58        serializer.serialize_str(&self.to_string())
59    }
60}
61
62impl From<uuid::Uuid> for ApiKeyId {
63    fn from(id: uuid::Uuid) -> Self {
64        ApiKeyId { id }
65    }
66}
67
68impl TryFrom<&str> for ApiKeyId {
69    type Error = String;
70
71    fn try_from(value: &str) -> Result<Self, Self::Error> {
72        if let Some(uuid_str) = value.strip_prefix(PREFIX) {
73            let uuid =
74                uuid::Uuid::parse_str(uuid_str).map_err(|_e| "invalid format".to_string())?;
75            Ok(ApiKeyId { id: uuid })
76        } else {
77            Err("invalid format".to_string())
78        }
79    }
80}
81
82#[test]
83fn key_invalid_1() {
84    let k = "abc-dsfdg";
85    let apikey: Result<ApiKeyId, String> = ApiKeyId::try_from(k);
86    assert!(apikey.is_err())
87}
88
89#[test]
90fn key_invalid_2() {
91    let k = "apikey-dsfdg";
92    let apikey: Result<ApiKeyId, String> = ApiKeyId::try_from(k);
93    assert!(apikey.is_err())
94}
95
96#[test]
97fn key_valid_1() {
98    let k = "apikey-0190c589-eb70-7980-97cf-af67b3a84116";
99    let apikey: Result<ApiKeyId, String> = ApiKeyId::try_from(k);
100    assert!(apikey.is_ok())
101}
102
103#[test]
104fn key_invalid_3() {
105    let k = "abc-0190c589-eb70-7980-97cf-af67b3a84116";
106    let apikey: Result<ApiKeyId, String> = ApiKeyId::try_from(k);
107    assert!(apikey.is_err())
108}