1 | use 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 | |
10 | use serde::{de, Deserialize, Serialize}; |
11 | use static_assertions::assert_impl_all; |
12 | use zvariant::{Str, Type}; |
13 | |
14 | /// A D-Bus server GUID. |
15 | /// |
16 | /// See the D-Bus specification [UUIDs chapter] for details. |
17 | /// |
18 | /// You can create a `Guid` from an existing string with [`Guid::try_from::<&str>`][TryFrom]. |
19 | /// |
20 | /// [UUIDs chapter]: https://dbus.freedesktop.org/doc/dbus-specification.html#uuids |
21 | /// [TryFrom]: #impl-TryFrom%3C%26%27_%20str%3E |
22 | #[derive (Clone, Debug, PartialEq, Eq, Hash, Type, Serialize)] |
23 | pub struct Guid<'g>(Str<'g>); |
24 | |
25 | assert_impl_all!(Guid<'_>: Send, Sync, Unpin); |
26 | |
27 | impl Guid<'_> { |
28 | /// Generate a D-Bus GUID that can be used with e.g. |
29 | /// [`connection::Builder::server`](crate::connection::Builder::server). |
30 | 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 | /// Returns a string slice for the GUID. |
42 | pub fn as_str(&self) -> &str { |
43 | self.0.as_str() |
44 | } |
45 | |
46 | /// Same as `try_from`, except it takes a `&'static str`. |
47 | 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 | /// Create an owned copy of the GUID. |
54 | pub fn to_owned(&self) -> Guid<'static> { |
55 | Guid(self.0.to_owned()) |
56 | } |
57 | } |
58 | |
59 | impl fmt::Display for Guid<'_> { |
60 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
61 | write!(f, " {}" , self.as_str()) |
62 | } |
63 | } |
64 | |
65 | impl<'g> TryFrom<&'g str> for Guid<'g> { |
66 | type Error = crate::Error; |
67 | |
68 | /// Creates a GUID from a string with 32 hex digits. |
69 | /// |
70 | /// Returns `Err(`[`Error::InvalidGUID`]`)` if the provided string is not a well-formed GUID. |
71 | /// |
72 | /// [`Error::InvalidGUID`]: enum.Error.html#variant.InvalidGUID |
73 | 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 | |
80 | impl<'g> TryFrom<Str<'g>> for Guid<'g> { |
81 | type Error = crate::Error; |
82 | |
83 | /// Creates a GUID from a string with 32 hex digits. |
84 | /// |
85 | /// Returns `Err(`[`Error::InvalidGUID`]`)` if the provided string is not a well-formed GUID. |
86 | /// |
87 | /// [`Error::InvalidGUID`]: enum.Error.html#variant.InvalidGUID |
88 | fn try_from(value: Str<'g>) -> std::result::Result<Self, Self::Error> { |
89 | validate_guid(&value)?; |
90 | |
91 | Ok(Guid(value)) |
92 | } |
93 | } |
94 | |
95 | impl 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 | |
105 | impl<'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 | |
115 | impl 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 | |
123 | impl<'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: Cow<'de, str>| s.try_into().map_err(op:serde::de::Error::custom)) |
130 | } |
131 | } |
132 | |
133 | fn validate_guid(value: &str) -> crate::Result<()> { |
134 | if value.as_bytes().len() != 32 || value.chars().any(|c: char| !char::is_ascii_hexdigit(&c)) { |
135 | return Err(crate::Error::InvalidGUID); |
136 | } |
137 | |
138 | Ok(()) |
139 | } |
140 | |
141 | impl From<Guid<'_>> for String { |
142 | fn from(guid: Guid<'_>) -> Self { |
143 | guid.0.into() |
144 | } |
145 | } |
146 | |
147 | impl Deref for Guid<'_> { |
148 | type Target = str; |
149 | |
150 | fn deref(&self) -> &Self::Target { |
151 | self.as_str() |
152 | } |
153 | } |
154 | |
155 | impl AsRef<str> for Guid<'_> { |
156 | fn as_ref(&self) -> &str { |
157 | self.as_str() |
158 | } |
159 | } |
160 | |
161 | impl Borrow<str> for Guid<'_> { |
162 | fn borrow(&self) -> &str { |
163 | self.as_str() |
164 | } |
165 | } |
166 | |
167 | /// Owned version of [`Guid`]. |
168 | #[derive (Clone, Debug, PartialEq, Eq, Hash, Type, Serialize)] |
169 | pub struct OwnedGuid(#[serde(borrow)] Guid<'static>); |
170 | |
171 | assert_impl_all!(OwnedGuid: Send, Sync, Unpin); |
172 | |
173 | impl OwnedGuid { |
174 | /// Get a reference to the inner [`Guid`]. |
175 | pub fn inner(&self) -> &Guid<'static> { |
176 | &self.0 |
177 | } |
178 | } |
179 | |
180 | impl Deref for OwnedGuid { |
181 | type Target = Guid<'static>; |
182 | |
183 | fn deref(&self) -> &Self::Target { |
184 | &self.0 |
185 | } |
186 | } |
187 | |
188 | impl Borrow<str> for OwnedGuid { |
189 | fn borrow(&self) -> &str { |
190 | self.0.as_str() |
191 | } |
192 | } |
193 | |
194 | impl From<OwnedGuid> for Guid<'_> { |
195 | fn from(o: OwnedGuid) -> Self { |
196 | o.0 |
197 | } |
198 | } |
199 | |
200 | impl<'unowned, 'owned: 'unowned> From<&'owned OwnedGuid> for Guid<'unowned> { |
201 | fn from(guid: &'owned OwnedGuid) -> Self { |
202 | guid.0.clone() |
203 | } |
204 | } |
205 | |
206 | impl From<Guid<'_>> for OwnedGuid { |
207 | fn from(guid: Guid<'_>) -> Self { |
208 | OwnedGuid(guid.to_owned()) |
209 | } |
210 | } |
211 | |
212 | impl From<OwnedGuid> for Str<'_> { |
213 | fn from(value: OwnedGuid) -> Self { |
214 | value.0 .0 |
215 | } |
216 | } |
217 | |
218 | impl<'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 | |
229 | impl PartialEq<&str> for OwnedGuid { |
230 | fn eq(&self, other: &&str) -> bool { |
231 | self.as_str() == *other |
232 | } |
233 | } |
234 | |
235 | impl PartialEq<Guid<'_>> for OwnedGuid { |
236 | fn eq(&self, other: &Guid<'_>) -> bool { |
237 | self.0 == *other |
238 | } |
239 | } |
240 | |
241 | impl 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)] |
248 | mod 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 | } |
262 | |