1 | use std::{ |
2 | borrow::{Borrow, BorrowMut}, |
3 | convert::{TryFrom, TryInto}, |
4 | fmt, |
5 | iter::repeat_with, |
6 | ops::{Deref, DerefMut}, |
7 | str::FromStr, |
8 | time::{SystemTime, UNIX_EPOCH}, |
9 | }; |
10 | |
11 | use serde::{Deserialize, Serialize}; |
12 | use static_assertions::assert_impl_all; |
13 | use zvariant::Type; |
14 | |
15 | /// A D-Bus server GUID. |
16 | /// |
17 | /// See the D-Bus specification [UUIDs chapter] for details. |
18 | /// |
19 | /// You can create a `Guid` from an existing string with [`Guid::try_from::<&str>`][TryFrom]. |
20 | /// |
21 | /// [UUIDs chapter]: https://dbus.freedesktop.org/doc/dbus-specification.html#uuids |
22 | /// [TryFrom]: #impl-TryFrom%3C%26%27_%20str%3E |
23 | #[derive (Clone, Debug, PartialEq, Eq, Hash, Type, Serialize)] |
24 | pub struct Guid(String); |
25 | |
26 | assert_impl_all!(Guid: Send, Sync, Unpin); |
27 | |
28 | impl Guid { |
29 | /// Generate a D-Bus GUID that can be used with e.g. [`Connection::new_unix_server`]. |
30 | /// |
31 | /// [`Connection::new_unix_server`]: struct.Connection.html#method.new_unix_server |
32 | pub fn generate() -> Self { |
33 | let r: Vec<u32> = repeat_with(repeater:rand::random::<u32>).take(3).collect(); |
34 | let r3: u32 = match SystemTime::now().duration_since(UNIX_EPOCH) { |
35 | Ok(n: Duration) => n.as_secs() as u32, |
36 | Err(_) => rand::random::<u32>(), |
37 | }; |
38 | |
39 | let s: String = format!(" {:08x}{:08x}{:08x}{:08x}" , r[0], r[1], r[2], r3); |
40 | Self(s) |
41 | } |
42 | |
43 | /// Returns a string slice for the GUID. |
44 | pub fn as_str(&self) -> &str { |
45 | self.0.as_str() |
46 | } |
47 | } |
48 | |
49 | impl fmt::Display for Guid { |
50 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
51 | write!(f, " {}" , self.as_str()) |
52 | } |
53 | } |
54 | |
55 | impl TryFrom<&str> for Guid { |
56 | type Error = crate::Error; |
57 | |
58 | /// Creates a GUID from a string with 32 hex digits. |
59 | /// |
60 | /// Returns `Err(`[`Error::InvalidGUID`]`)` if the provided string is not a well-formed GUID. |
61 | /// |
62 | /// [`Error::InvalidGUID`]: enum.Error.html#variant.InvalidGUID |
63 | fn try_from(value: &str) -> std::result::Result<Self, Self::Error> { |
64 | if !valid_guid(value) { |
65 | Err(crate::Error::InvalidGUID) |
66 | } else { |
67 | Ok(Guid(value.to_string())) |
68 | } |
69 | } |
70 | } |
71 | |
72 | impl TryFrom<String> for Guid { |
73 | type Error = crate::Error; |
74 | |
75 | fn try_from(value: String) -> std::result::Result<Self, Self::Error> { |
76 | if !valid_guid(&value) { |
77 | Err(crate::Error::InvalidGUID) |
78 | } else { |
79 | Ok(Guid(value)) |
80 | } |
81 | } |
82 | } |
83 | |
84 | impl FromStr for Guid { |
85 | type Err = crate::Error; |
86 | |
87 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
88 | s.try_into() |
89 | } |
90 | } |
91 | |
92 | impl<'de> Deserialize<'de> for Guid { |
93 | fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error> |
94 | where |
95 | D: serde::Deserializer<'de>, |
96 | { |
97 | String::deserialize(deserializer) |
98 | .and_then(|s: String| s.try_into().map_err(op:serde::de::Error::custom)) |
99 | } |
100 | } |
101 | |
102 | fn valid_guid(value: &str) -> bool { |
103 | value.as_bytes().len() == 32 && value.chars().all(|c: char| char::is_ascii_hexdigit(&c)) |
104 | } |
105 | |
106 | impl From<Guid> for String { |
107 | fn from(guid: Guid) -> Self { |
108 | guid.0 |
109 | } |
110 | } |
111 | |
112 | impl Deref for Guid { |
113 | type Target = str; |
114 | |
115 | fn deref(&self) -> &Self::Target { |
116 | self.as_str() |
117 | } |
118 | } |
119 | |
120 | impl DerefMut for Guid { |
121 | fn deref_mut(&mut self) -> &mut Self::Target { |
122 | &mut self.0 |
123 | } |
124 | } |
125 | |
126 | impl AsRef<str> for Guid { |
127 | fn as_ref(&self) -> &str { |
128 | self.as_str() |
129 | } |
130 | } |
131 | |
132 | impl AsMut<str> for Guid { |
133 | fn as_mut(&mut self) -> &mut str { |
134 | &mut self.0 |
135 | } |
136 | } |
137 | |
138 | impl Borrow<str> for Guid { |
139 | fn borrow(&self) -> &str { |
140 | self.as_str() |
141 | } |
142 | } |
143 | |
144 | impl BorrowMut<str> for Guid { |
145 | fn borrow_mut(&mut self) -> &mut str { |
146 | &mut self.0 |
147 | } |
148 | } |
149 | |
150 | #[cfg (test)] |
151 | mod tests { |
152 | use crate::Guid; |
153 | use test_log::test; |
154 | |
155 | #[test ] |
156 | fn generate() { |
157 | let u1 = Guid::generate(); |
158 | let u2 = Guid::generate(); |
159 | assert_eq!(u1.as_str().len(), 32); |
160 | assert_eq!(u2.as_str().len(), 32); |
161 | assert_ne!(u1, u2); |
162 | assert_ne!(u1.as_str(), u2.as_str()); |
163 | } |
164 | } |
165 | |