dtz_identifier/
context_id.rs1use 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}