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 s = uuid::Uuid::new_v4().as_simple().to_string();
30 Guid(s.into())
31 }
32
33 pub fn as_str(&self) -> &str {
35 self.0.as_str()
36 }
37
38 pub fn from_static_str(guid: &'static str) -> crate::Result<Self> {
40 validate_guid(guid)?;
41
42 Ok(Self(Str::from_static(guid)))
43 }
44
45 pub fn to_owned(&self) -> Guid<'static> {
47 Guid(self.0.to_owned())
48 }
49}
50
51impl fmt::Display for Guid<'_> {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 write!(f, "{}", self.as_str())
54 }
55}
56
57impl<'g> TryFrom<&'g str> for Guid<'g> {
58 type Error = crate::Error;
59
60 fn try_from(value: &'g str) -> std::result::Result<Self, Self::Error> {
66 validate_guid(value)?;
67
68 Ok(Self(Str::from(value)))
69 }
70}
71
72impl<'g> TryFrom<Str<'g>> for Guid<'g> {
73 type Error = crate::Error;
74
75 fn try_from(value: Str<'g>) -> std::result::Result<Self, Self::Error> {
81 validate_guid(&value)?;
82
83 Ok(Guid(value))
84 }
85}
86
87impl TryFrom<String> for Guid<'_> {
88 type Error = crate::Error;
89
90 fn try_from(value: String) -> std::result::Result<Self, Self::Error> {
91 validate_guid(&value)?;
92
93 Ok(Guid(value.into()))
94 }
95}
96
97impl<'g> TryFrom<Cow<'g, str>> for Guid<'g> {
98 type Error = crate::Error;
99
100 fn try_from(value: Cow<'g, str>) -> std::result::Result<Self, Self::Error> {
101 validate_guid(&value)?;
102
103 Ok(Guid(value.into()))
104 }
105}
106
107impl FromStr for Guid<'static> {
108 type Err = crate::Error;
109
110 fn from_str(s: &str) -> Result<Self, Self::Err> {
111 s.try_into().map(|guid: Guid<'_>| guid.to_owned())
112 }
113}
114
115impl<'de> Deserialize<'de> for Guid<'de> {
116 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
117 where
118 D: serde::Deserializer<'de>,
119 {
120 <Cow<'de, str>>::deserialize(deserializer)
121 .and_then(|s| s.try_into().map_err(serde::de::Error::custom))
122 }
123}
124
125const fn validate_guid(value: &str) -> crate::Result<()> {
126 match uuid::Uuid::try_parse(value) {
127 Ok(_) => Ok(()),
128 Err(_) => Err(crate::Error::InvalidGUID),
129 }
130}
131
132impl From<Guid<'_>> for String {
133 fn from(guid: Guid<'_>) -> Self {
134 guid.0.into()
135 }
136}
137
138impl Deref for Guid<'_> {
139 type Target = str;
140
141 fn deref(&self) -> &Self::Target {
142 self.as_str()
143 }
144}
145
146impl<'a> Borrow<Guid<'a>> for OwnedGuid {
147 fn borrow(&self) -> &Guid<'a> {
148 &self.0
149 }
150}
151
152impl AsRef<str> for Guid<'_> {
153 fn as_ref(&self) -> &str {
154 self.as_str()
155 }
156}
157
158impl Borrow<str> for Guid<'_> {
159 fn borrow(&self) -> &str {
160 self.as_str()
161 }
162}
163
164#[derive(Clone, Debug, PartialEq, Eq, Hash, Type, Serialize)]
166pub struct OwnedGuid(#[serde(borrow)] Guid<'static>);
167
168impl OwnedGuid {
169 pub fn inner(&self) -> &Guid<'static> {
171 &self.0
172 }
173}
174
175impl Deref for OwnedGuid {
176 type Target = Guid<'static>;
177
178 fn deref(&self) -> &Self::Target {
179 &self.0
180 }
181}
182
183impl Borrow<str> for OwnedGuid {
184 fn borrow(&self) -> &str {
185 self.0.as_str()
186 }
187}
188
189impl From<OwnedGuid> for Guid<'_> {
190 fn from(o: OwnedGuid) -> Self {
191 o.0
192 }
193}
194
195impl<'unowned, 'owned: 'unowned> From<&'owned OwnedGuid> for Guid<'unowned> {
196 fn from(guid: &'owned OwnedGuid) -> Self {
197 guid.0.clone()
198 }
199}
200
201impl From<Guid<'_>> for OwnedGuid {
202 fn from(guid: Guid<'_>) -> Self {
203 OwnedGuid(guid.to_owned())
204 }
205}
206
207impl From<OwnedGuid> for Str<'_> {
208 fn from(value: OwnedGuid) -> Self {
209 value.0 .0
210 }
211}
212
213impl<'de> Deserialize<'de> for OwnedGuid {
214 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
215 where
216 D: de::Deserializer<'de>,
217 {
218 String::deserialize(deserializer)
219 .and_then(|n| Guid::try_from(n).map_err(|e| de::Error::custom(e.to_string())))
220 .map(Self)
221 }
222}
223
224impl PartialEq<&str> for OwnedGuid {
225 fn eq(&self, other: &&str) -> bool {
226 self.as_str() == *other
227 }
228}
229
230impl PartialEq<Guid<'_>> for OwnedGuid {
231 fn eq(&self, other: &Guid<'_>) -> bool {
232 self.0 == *other
233 }
234}
235
236impl Display for OwnedGuid {
237 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
238 Display::fmt(&Guid::from(self), f)
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use crate::Guid;
245 use test_log::test;
246
247 #[test]
248 #[cfg(feature = "p2p")]
249 fn generate() {
250 let u1 = Guid::generate();
251 let u2 = Guid::generate();
252 assert_eq!(u1.as_str().len(), 32);
253 assert_eq!(u2.as_str().len(), 32);
254 assert_ne!(u1, u2);
255 assert_ne!(u1.as_str(), u2.as_str());
256 }
257
258 #[test]
259 fn parse() {
260 let valid = "0123456789ABCDEF0123456789ABCDEF";
261 let invalid = "0123456789ABCDEF0123456789ABCD";
263
264 assert!(Guid::try_from(valid).is_ok());
265 assert!(Guid::try_from(invalid).is_err());
266 }
267}