1use std::{
2 borrow::{Borrow, Cow},
3 fmt::{self, Debug, Display, Formatter},
4 ops::Deref,
5 str::FromStr,
6};
7
8use serde::{de, Deserialize, Serialize};
9use zvariant::{Str, Type};
10
11#[derive(Clone, Debug, PartialEq, Eq, Hash, Type, Serialize)]
20pub struct Guid<'g>(Str<'g>);
21
22impl Guid<'_> {
23 #[cfg(feature = "p2p")]
28 pub fn generate() -> Guid<'static> {
29 let r: Vec<u32> = std::iter::repeat_with(rand::random::<u32>)
30 .take(3)
31 .collect();
32 let r3 = match std::time::SystemTime::now().duration_since(std::time::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 use winnow::{stream::AsChar, token::take_while, Parser};
135
136 take_while::<_, _, ()>(32, AsChar::is_hex_digit)
137 .map(|_| ())
138 .parse(value.as_bytes())
139 .map_err(|_| crate::Error::InvalidGUID)
140}
141
142impl From<Guid<'_>> for String {
143 fn from(guid: Guid<'_>) -> Self {
144 guid.0.into()
145 }
146}
147
148impl Deref for Guid<'_> {
149 type Target = str;
150
151 fn deref(&self) -> &Self::Target {
152 self.as_str()
153 }
154}
155
156impl<'a> Borrow<Guid<'a>> for OwnedGuid {
157 fn borrow(&self) -> &Guid<'a> {
158 &self.0
159 }
160}
161
162impl AsRef<str> for Guid<'_> {
163 fn as_ref(&self) -> &str {
164 self.as_str()
165 }
166}
167
168impl Borrow<str> for Guid<'_> {
169 fn borrow(&self) -> &str {
170 self.as_str()
171 }
172}
173
174#[derive(Clone, Debug, PartialEq, Eq, Hash, Type, Serialize)]
176pub struct OwnedGuid(#[serde(borrow)] Guid<'static>);
177
178impl OwnedGuid {
179 pub fn inner(&self) -> &Guid<'static> {
181 &self.0
182 }
183}
184
185impl Deref for OwnedGuid {
186 type Target = Guid<'static>;
187
188 fn deref(&self) -> &Self::Target {
189 &self.0
190 }
191}
192
193impl Borrow<str> for OwnedGuid {
194 fn borrow(&self) -> &str {
195 self.0.as_str()
196 }
197}
198
199impl From<OwnedGuid> for Guid<'_> {
200 fn from(o: OwnedGuid) -> Self {
201 o.0
202 }
203}
204
205impl<'unowned, 'owned: 'unowned> From<&'owned OwnedGuid> for Guid<'unowned> {
206 fn from(guid: &'owned OwnedGuid) -> Self {
207 guid.0.clone()
208 }
209}
210
211impl From<Guid<'_>> for OwnedGuid {
212 fn from(guid: Guid<'_>) -> Self {
213 OwnedGuid(guid.to_owned())
214 }
215}
216
217impl From<OwnedGuid> for Str<'_> {
218 fn from(value: OwnedGuid) -> Self {
219 value.0 .0
220 }
221}
222
223impl<'de> Deserialize<'de> for OwnedGuid {
224 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
225 where
226 D: de::Deserializer<'de>,
227 {
228 String::deserialize(deserializer)
229 .and_then(|n| Guid::try_from(n).map_err(|e| de::Error::custom(e.to_string())))
230 .map(Self)
231 }
232}
233
234impl PartialEq<&str> for OwnedGuid {
235 fn eq(&self, other: &&str) -> bool {
236 self.as_str() == *other
237 }
238}
239
240impl PartialEq<Guid<'_>> for OwnedGuid {
241 fn eq(&self, other: &Guid<'_>) -> bool {
242 self.0 == *other
243 }
244}
245
246impl Display for OwnedGuid {
247 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
248 Display::fmt(&Guid::from(self), f)
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use crate::Guid;
255 use test_log::test;
256
257 #[test]
258 #[cfg(feature = "p2p")]
259 fn generate() {
260 let u1 = Guid::generate();
261 let u2 = Guid::generate();
262 assert_eq!(u1.as_str().len(), 32);
263 assert_eq!(u2.as_str().len(), 32);
264 assert_ne!(u1, u2);
265 assert_ne!(u1.as_str(), u2.as_str());
266 }
267
268 #[test]
269 fn parse() {
270 let valid = "0123456789ABCDEF0123456789ABCDEF";
271 let invalid = "0123456789ABCDEF0123456789ABCD";
273
274 assert!(Guid::try_from(valid).is_ok());
275 assert!(Guid::try_from(invalid).is_err());
276 }
277}