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