1use std::{
2 borrow::{Borrow, Cow},
3 fmt::{self, Debug, Display, Formatter},
4 iter::repeat_with,
5 ops::Deref,
6 str::FromStr,
7 time::{SystemTime, UNIX_EPOCH},
8};
9
10use serde::{de, Deserialize, Serialize};
11use static_assertions::assert_impl_all;
12use zvariant::{Str, Type};
13
14#[derive(Clone, Debug, PartialEq, Eq, Hash, Type, Serialize)]
23pub struct Guid<'g>(Str<'g>);
24
25assert_impl_all!(Guid<'_>: Send, Sync, Unpin);
26
27impl Guid<'_> {
28 pub fn generate() -> Guid<'static> {
31 let r: Vec<u32> = repeat_with(rand::random::<u32>).take(3).collect();
32 let r3 = match SystemTime::now().duration_since(UNIX_EPOCH) {
33 Ok(n) => n.as_secs() as u32,
34 Err(_) => rand::random::<u32>(),
35 };
36
37 let s = format!("{:08x}{:08x}{:08x}{:08x}", r[0], r[1], r[2], r3);
38 Guid(s.into())
39 }
40
41 pub fn as_str(&self) -> &str {
43 self.0.as_str()
44 }
45
46 pub fn from_static_str(guid: &'static str) -> crate::Result<Self> {
48 validate_guid(guid)?;
49
50 Ok(Self(Str::from_static(guid)))
51 }
52
53 pub fn to_owned(&self) -> Guid<'static> {
55 Guid(self.0.to_owned())
56 }
57}
58
59impl fmt::Display for Guid<'_> {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(f, "{}", self.as_str())
62 }
63}
64
65impl<'g> TryFrom<&'g str> for Guid<'g> {
66 type Error = crate::Error;
67
68 fn try_from(value: &'g str) -> std::result::Result<Self, Self::Error> {
74 validate_guid(value)?;
75
76 Ok(Self(Str::from(value)))
77 }
78}
79
80impl<'g> TryFrom<Str<'g>> for Guid<'g> {
81 type Error = crate::Error;
82
83 fn try_from(value: Str<'g>) -> std::result::Result<Self, Self::Error> {
89 validate_guid(&value)?;
90
91 Ok(Guid(value))
92 }
93}
94
95impl TryFrom<String> for Guid<'_> {
96 type Error = crate::Error;
97
98 fn try_from(value: String) -> std::result::Result<Self, Self::Error> {
99 validate_guid(&value)?;
100
101 Ok(Guid(value.into()))
102 }
103}
104
105impl<'g> TryFrom<Cow<'g, str>> for Guid<'g> {
106 type Error = crate::Error;
107
108 fn try_from(value: Cow<'g, str>) -> std::result::Result<Self, Self::Error> {
109 validate_guid(&value)?;
110
111 Ok(Guid(value.into()))
112 }
113}
114
115impl FromStr for Guid<'static> {
116 type Err = crate::Error;
117
118 fn from_str(s: &str) -> Result<Self, Self::Err> {
119 s.try_into().map(|guid: Guid<'_>| guid.to_owned())
120 }
121}
122
123impl<'de> Deserialize<'de> for Guid<'de> {
124 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
125 where
126 D: serde::Deserializer<'de>,
127 {
128 <Cow<'de, str>>::deserialize(deserializer)
129 .and_then(|s| s.try_into().map_err(serde::de::Error::custom))
130 }
131}
132
133fn validate_guid(value: &str) -> crate::Result<()> {
134 if value.as_bytes().len() != 32 || value.chars().any(|c| !char::is_ascii_hexdigit(&c)) {
135 return Err(crate::Error::InvalidGUID);
136 }
137
138 Ok(())
139}
140
141impl From<Guid<'_>> for String {
142 fn from(guid: Guid<'_>) -> Self {
143 guid.0.into()
144 }
145}
146
147impl Deref for Guid<'_> {
148 type Target = str;
149
150 fn deref(&self) -> &Self::Target {
151 self.as_str()
152 }
153}
154
155impl AsRef<str> for Guid<'_> {
156 fn as_ref(&self) -> &str {
157 self.as_str()
158 }
159}
160
161impl Borrow<str> for Guid<'_> {
162 fn borrow(&self) -> &str {
163 self.as_str()
164 }
165}
166
167#[derive(Clone, Debug, PartialEq, Eq, Hash, Type, Serialize)]
169pub struct OwnedGuid(#[serde(borrow)] Guid<'static>);
170
171assert_impl_all!(OwnedGuid: Send, Sync, Unpin);
172
173impl OwnedGuid {
174 pub fn inner(&self) -> &Guid<'static> {
176 &self.0
177 }
178}
179
180impl Deref for OwnedGuid {
181 type Target = Guid<'static>;
182
183 fn deref(&self) -> &Self::Target {
184 &self.0
185 }
186}
187
188impl Borrow<str> for OwnedGuid {
189 fn borrow(&self) -> &str {
190 self.0.as_str()
191 }
192}
193
194impl From<OwnedGuid> for Guid<'_> {
195 fn from(o: OwnedGuid) -> Self {
196 o.0
197 }
198}
199
200impl<'unowned, 'owned: 'unowned> From<&'owned OwnedGuid> for Guid<'unowned> {
201 fn from(guid: &'owned OwnedGuid) -> Self {
202 guid.0.clone()
203 }
204}
205
206impl From<Guid<'_>> for OwnedGuid {
207 fn from(guid: Guid<'_>) -> Self {
208 OwnedGuid(guid.to_owned())
209 }
210}
211
212impl From<OwnedGuid> for Str<'_> {
213 fn from(value: OwnedGuid) -> Self {
214 value.0 .0
215 }
216}
217
218impl<'de> Deserialize<'de> for OwnedGuid {
219 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
220 where
221 D: de::Deserializer<'de>,
222 {
223 String::deserialize(deserializer)
224 .and_then(|n| Guid::try_from(n).map_err(|e| de::Error::custom(e.to_string())))
225 .map(Self)
226 }
227}
228
229impl PartialEq<&str> for OwnedGuid {
230 fn eq(&self, other: &&str) -> bool {
231 self.as_str() == *other
232 }
233}
234
235impl PartialEq<Guid<'_>> for OwnedGuid {
236 fn eq(&self, other: &Guid<'_>) -> bool {
237 self.0 == *other
238 }
239}
240
241impl Display for OwnedGuid {
242 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
243 Display::fmt(&Guid::from(self), f)
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use crate::Guid;
250 use test_log::test;
251
252 #[test]
253 fn generate() {
254 let u1 = Guid::generate();
255 let u2 = Guid::generate();
256 assert_eq!(u1.as_str().len(), 32);
257 assert_eq!(u2.as_str().len(), 32);
258 assert_ne!(u1, u2);
259 assert_ne!(u1.as_str(), u2.as_str());
260 }
261}