dtz_identifier/
context_id.rs

1use serde::{de::Visitor, Deserialize, Deserializer, Serialize};
2use std::fmt::Display;
3
4static PREFIX: &str = "context-";
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub struct ContextId {
8    pub id: String,
9}
10
11impl ContextId {
12    pub fn nil() -> Self {
13        Self {
14            id: "00000000".to_string(),
15        }
16    }
17}
18
19impl Default for ContextId {
20    fn default() -> Self {
21        Self {
22            id: crate::generate_internal_id(8),
23        }
24    }
25}
26
27impl Display for ContextId {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        f.write_str(&format!("{PREFIX}{}", self.id))
30    }
31}
32
33impl TryFrom<String> for ContextId {
34    type Error = String;
35
36    fn try_from(value: String) -> Result<Self, Self::Error> {
37        if let Some(id_str) = value.strip_prefix(PREFIX) {
38            Ok(ContextId {
39                id: id_str.to_string(),
40            })
41        } else {
42            Err("invalid format".to_string())
43        }
44    }
45}
46
47impl TryFrom<&str> for ContextId {
48    type Error = String;
49
50    fn try_from(value: &str) -> Result<Self, Self::Error> {
51        if let Some(id_str) = value.strip_prefix(PREFIX) {
52            Ok(ContextId {
53                id: id_str.to_string(),
54            })
55        } else {
56            Err("invalid format".to_string())
57        }
58    }
59}
60
61impl<'de> Deserialize<'de> for ContextId {
62    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
63    where
64        D: Deserializer<'de>,
65    {
66        struct ContextIdVisitor;
67
68        impl Visitor<'_> for ContextIdVisitor {
69            type Value = ContextId;
70
71            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
72                formatter.write_str("a string starting with '")?;
73                formatter.write_str(PREFIX)?;
74                formatter.write_str("' followed by a 8-character alphanumeric string")
75            }
76
77            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
78            where
79                E: serde::de::Error,
80            {
81                if let Some(id_str) = value.strip_prefix(PREFIX) {
82                    Ok(ContextId {
83                        id: id_str.to_string(),
84                    })
85                } else {
86                    Err(E::custom("invalid format"))
87                }
88            }
89        }
90
91        deserializer.deserialize_str(ContextIdVisitor)
92    }
93}
94
95impl Serialize for ContextId {
96    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
97    where
98        S: serde::Serializer,
99    {
100        serializer.serialize_str(&self.to_string())
101    }
102}
103
104#[test]
105fn test_from() {
106    let id = crate::generate_internal_id(8);
107    let id_str = format!("{PREFIX}{id}");
108    let ctx = ContextId::try_from(id_str).unwrap();
109    assert_eq!(ctx.id, id);
110}
111
112#[test]
113fn key_invalid_1() {
114    let k = "abc-dsfdg";
115    let apikey: Result<ContextId, String> = ContextId::try_from(k);
116    assert!(apikey.is_err())
117}
118
119#[test]
120fn key_invalid_2() {
121    let k = "context-dsfdg";
122    let apikey: Result<ContextId, String> = ContextId::try_from(k);
123    assert!(apikey.is_ok())
124}
125
126#[test]
127fn key_valid_1() {
128    let k = "context-0190c589-eb70-7980-97cf-af67b3a84116";
129    let apikey: Result<ContextId, String> = ContextId::try_from(k);
130    assert!(apikey.is_ok())
131}
132
133#[test]
134fn key_invalid_3() {
135    let k = "abc-0190c589-eb70-7980-97cf-af67b3a84116";
136    let apikey: Result<ContextId, String> = ContextId::try_from(k);
137    assert!(apikey.is_err())
138}
139
140#[test]
141fn test_nil() {
142    let ctx = ContextId::nil();
143    assert_eq!(ctx.id, "00000000");
144}