1 | use crate::{ |
2 | utils::{impl_str_basic, impl_try_from}, |
3 | Error, Result, |
4 | }; |
5 | use serde::{de, Deserialize, Serialize}; |
6 | use static_assertions::assert_impl_all; |
7 | use std::{ |
8 | borrow::{Borrow, Cow}, |
9 | fmt::{self, Debug, Display, Formatter}, |
10 | ops::Deref, |
11 | sync::Arc, |
12 | }; |
13 | use zvariant::{NoneValue, OwnedValue, Str, Type, Value}; |
14 | |
15 | /// String that identifies an [interface name][in] on the bus. |
16 | /// |
17 | /// # Examples |
18 | /// |
19 | /// ``` |
20 | /// use zbus_names::InterfaceName; |
21 | /// |
22 | /// // Valid interface names. |
23 | /// let name = InterfaceName::try_from("org.gnome.Interface_for_you" ).unwrap(); |
24 | /// assert_eq!(name, "org.gnome.Interface_for_you" ); |
25 | /// let name = InterfaceName::try_from("a.very.loooooooooooooooooo_ooooooo_0000o0ng.Name" ).unwrap(); |
26 | /// assert_eq!(name, "a.very.loooooooooooooooooo_ooooooo_0000o0ng.Name" ); |
27 | /// |
28 | /// // Invalid interface names |
29 | /// InterfaceName::try_from("" ).unwrap_err(); |
30 | /// InterfaceName::try_from(":start.with.a.colon" ).unwrap_err(); |
31 | /// InterfaceName::try_from("double..dots" ).unwrap_err(); |
32 | /// InterfaceName::try_from("." ).unwrap_err(); |
33 | /// InterfaceName::try_from(".start.with.dot" ).unwrap_err(); |
34 | /// InterfaceName::try_from("no-dots" ).unwrap_err(); |
35 | /// InterfaceName::try_from("1st.element.starts.with.digit" ).unwrap_err(); |
36 | /// InterfaceName::try_from("the.2nd.element.starts.with.digit" ).unwrap_err(); |
37 | /// InterfaceName::try_from("contains.dashes-in.the.name" ).unwrap_err(); |
38 | /// ``` |
39 | /// |
40 | /// [in]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-interface |
41 | #[derive ( |
42 | Clone, Debug, Hash, PartialEq, Eq, Serialize, Type, Value, PartialOrd, Ord, OwnedValue, |
43 | )] |
44 | pub struct InterfaceName<'name>(Str<'name>); |
45 | |
46 | impl_str_basic!(InterfaceName<'_>); |
47 | |
48 | assert_impl_all!(InterfaceName<'_>: Send, Sync, Unpin); |
49 | |
50 | impl<'name> InterfaceName<'name> { |
51 | /// This is faster than `Clone::clone` when `self` contains owned data. |
52 | pub fn as_ref(&self) -> InterfaceName<'_> { |
53 | InterfaceName(self.0.as_ref()) |
54 | } |
55 | |
56 | /// The interface name as string. |
57 | pub fn as_str(&self) -> &str { |
58 | self.0.as_str() |
59 | } |
60 | |
61 | /// Create a new `InterfaceName` from the given string. |
62 | /// |
63 | /// Since the passed string is not checked for correctness, prefer using the |
64 | /// `TryFrom<&str>` implementation. |
65 | pub fn from_str_unchecked(name: &'name str) -> Self { |
66 | Self(Str::from(name)) |
67 | } |
68 | |
69 | /// Same as `try_from`, except it takes a `&'static str`. |
70 | pub fn from_static_str(name: &'static str) -> Result<Self> { |
71 | ensure_correct_interface_name(name)?; |
72 | Ok(Self(Str::from_static(name))) |
73 | } |
74 | |
75 | /// Same as `from_str_unchecked`, except it takes a `&'static str`. |
76 | pub const fn from_static_str_unchecked(name: &'static str) -> Self { |
77 | Self(Str::from_static(name)) |
78 | } |
79 | |
80 | /// Same as `from_str_unchecked`, except it takes an owned `String`. |
81 | /// |
82 | /// Since the passed string is not checked for correctness, prefer using the |
83 | /// `TryFrom<String>` implementation. |
84 | pub fn from_string_unchecked(name: String) -> Self { |
85 | Self(Str::from(name)) |
86 | } |
87 | |
88 | /// Creates an owned clone of `self`. |
89 | pub fn to_owned(&self) -> InterfaceName<'static> { |
90 | InterfaceName(self.0.to_owned()) |
91 | } |
92 | |
93 | /// Creates an owned clone of `self`. |
94 | pub fn into_owned(self) -> InterfaceName<'static> { |
95 | InterfaceName(self.0.into_owned()) |
96 | } |
97 | } |
98 | |
99 | impl Deref for InterfaceName<'_> { |
100 | type Target = str; |
101 | |
102 | fn deref(&self) -> &Self::Target { |
103 | self.as_str() |
104 | } |
105 | } |
106 | |
107 | impl Borrow<str> for InterfaceName<'_> { |
108 | fn borrow(&self) -> &str { |
109 | self.as_str() |
110 | } |
111 | } |
112 | |
113 | impl Display for InterfaceName<'_> { |
114 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
115 | Display::fmt(&self.as_str(), f) |
116 | } |
117 | } |
118 | |
119 | impl PartialEq<str> for InterfaceName<'_> { |
120 | fn eq(&self, other: &str) -> bool { |
121 | self.as_str() == other |
122 | } |
123 | } |
124 | |
125 | impl PartialEq<&str> for InterfaceName<'_> { |
126 | fn eq(&self, other: &&str) -> bool { |
127 | self.as_str() == *other |
128 | } |
129 | } |
130 | |
131 | impl PartialEq<OwnedInterfaceName> for InterfaceName<'_> { |
132 | fn eq(&self, other: &OwnedInterfaceName) -> bool { |
133 | *self == other.0 |
134 | } |
135 | } |
136 | |
137 | impl<'de: 'name, 'name> Deserialize<'de> for InterfaceName<'name> { |
138 | fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error> |
139 | where |
140 | D: serde::Deserializer<'de>, |
141 | { |
142 | let name: Cow<'name, str> = <Cow<'name, str>>::deserialize(deserializer)?; |
143 | |
144 | Self::try_from(name).map_err(|e: Error| de::Error::custom(msg:e.to_string())) |
145 | } |
146 | } |
147 | |
148 | impl_try_from! { |
149 | ty:InterfaceName<'s>, |
150 | owned_ty: OwnedInterfaceName, |
151 | validate_fn: ensure_correct_interface_name, |
152 | try_from: [&'s str, String, Arc<str>, Cow<'s, str>, Str<'s>], |
153 | } |
154 | |
155 | impl<'name> From<InterfaceName<'name>> for Str<'name> { |
156 | fn from(value: InterfaceName<'name>) -> Self { |
157 | value.0 |
158 | } |
159 | } |
160 | |
161 | fn ensure_correct_interface_name(name: &str) -> Result<()> { |
162 | // Rules |
163 | // |
164 | // * Only ASCII alphanumeric or `_`. |
165 | // * Must not begin with a `.`. |
166 | // * Must contain at least one `.`. |
167 | // * Each element must: |
168 | // * not begin with a digit. |
169 | // * be 1 character (so name must be minimum 3 characters long). |
170 | // * <= 255 characters. |
171 | if name.len() < 3 { |
172 | return Err(Error::InvalidInterfaceName(format!( |
173 | "` {}` is {} characters long, which is smaller than minimum allowed (3)" , |
174 | name, |
175 | name.len(), |
176 | ))); |
177 | } else if name.len() > 255 { |
178 | return Err(Error::InvalidInterfaceName(format!( |
179 | "` {}` is {} characters long, which is longer than maximum allowed (255)" , |
180 | name, |
181 | name.len(), |
182 | ))); |
183 | } |
184 | |
185 | let mut prev = None; |
186 | let mut no_dot = true; |
187 | for c in name.chars() { |
188 | if c == '.' { |
189 | if prev.is_none() || prev == Some('.' ) { |
190 | return Err(Error::InvalidInterfaceName(String::from( |
191 | "must not contain a double `.`" , |
192 | ))); |
193 | } |
194 | |
195 | if no_dot { |
196 | no_dot = false; |
197 | } |
198 | } else if c.is_ascii_digit() && (prev.is_none() || prev == Some('.' )) { |
199 | return Err(Error::InvalidInterfaceName(String::from( |
200 | "each element must not start with a digit" , |
201 | ))); |
202 | } else if !c.is_ascii_alphanumeric() && c != '_' { |
203 | return Err(Error::InvalidInterfaceName(format!( |
204 | "` {c}` character not allowed" |
205 | ))); |
206 | } |
207 | |
208 | prev = Some(c); |
209 | } |
210 | |
211 | if no_dot { |
212 | return Err(Error::InvalidInterfaceName(String::from( |
213 | "must contain at least 1 `.`" , |
214 | ))); |
215 | } |
216 | |
217 | Ok(()) |
218 | } |
219 | |
220 | /// This never succeeds but is provided so it's easier to pass `Option::None` values for API |
221 | /// requiring `Option<TryInto<impl BusName>>`, since type inference won't work here. |
222 | impl TryFrom<()> for InterfaceName<'_> { |
223 | type Error = Error; |
224 | |
225 | fn try_from(_value: ()) -> Result<Self> { |
226 | unreachable!("Conversion from `()` is not meant to actually work" ); |
227 | } |
228 | } |
229 | |
230 | impl<'name> From<&InterfaceName<'name>> for InterfaceName<'name> { |
231 | fn from(name: &InterfaceName<'name>) -> Self { |
232 | name.clone() |
233 | } |
234 | } |
235 | |
236 | impl<'name> NoneValue for InterfaceName<'name> { |
237 | type NoneType = &'name str; |
238 | |
239 | fn null_value() -> Self::NoneType { |
240 | <&str>::default() |
241 | } |
242 | } |
243 | |
244 | /// Owned sibling of [`InterfaceName`]. |
245 | #[derive (Clone, Hash, PartialEq, Eq, Serialize, Type, Value, PartialOrd, Ord, OwnedValue)] |
246 | pub struct OwnedInterfaceName(#[serde(borrow)] InterfaceName<'static>); |
247 | |
248 | assert_impl_all!(OwnedInterfaceName: Send, Sync, Unpin); |
249 | |
250 | impl_str_basic!(OwnedInterfaceName); |
251 | |
252 | impl OwnedInterfaceName { |
253 | /// Convert to the inner `InterfaceName`, consuming `self`. |
254 | pub fn into_inner(self) -> InterfaceName<'static> { |
255 | self.0 |
256 | } |
257 | |
258 | /// Get a reference to the inner `InterfaceName`. |
259 | pub fn inner(&self) -> &InterfaceName<'static> { |
260 | &self.0 |
261 | } |
262 | } |
263 | |
264 | impl Deref for OwnedInterfaceName { |
265 | type Target = InterfaceName<'static>; |
266 | |
267 | fn deref(&self) -> &Self::Target { |
268 | &self.0 |
269 | } |
270 | } |
271 | |
272 | impl Borrow<str> for OwnedInterfaceName { |
273 | fn borrow(&self) -> &str { |
274 | self.0.as_str() |
275 | } |
276 | } |
277 | |
278 | impl From<OwnedInterfaceName> for InterfaceName<'_> { |
279 | fn from(o: OwnedInterfaceName) -> Self { |
280 | o.into_inner() |
281 | } |
282 | } |
283 | |
284 | impl<'unowned, 'owned: 'unowned> From<&'owned OwnedInterfaceName> for InterfaceName<'unowned> { |
285 | fn from(name: &'owned OwnedInterfaceName) -> Self { |
286 | InterfaceName::from_str_unchecked(name.as_str()) |
287 | } |
288 | } |
289 | |
290 | impl From<InterfaceName<'_>> for OwnedInterfaceName { |
291 | fn from(name: InterfaceName<'_>) -> Self { |
292 | OwnedInterfaceName(name.into_owned()) |
293 | } |
294 | } |
295 | |
296 | impl From<OwnedInterfaceName> for Str<'_> { |
297 | fn from(value: OwnedInterfaceName) -> Self { |
298 | value.into_inner().0 |
299 | } |
300 | } |
301 | |
302 | impl<'de> Deserialize<'de> for OwnedInterfaceName { |
303 | fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error> |
304 | where |
305 | D: de::Deserializer<'de>, |
306 | { |
307 | String::deserialize(deserializer) |
308 | .and_then(|n| InterfaceName::try_from(n).map_err(|e| de::Error::custom(e.to_string()))) |
309 | .map(Self) |
310 | } |
311 | } |
312 | |
313 | impl PartialEq<&str> for OwnedInterfaceName { |
314 | fn eq(&self, other: &&str) -> bool { |
315 | self.as_str() == *other |
316 | } |
317 | } |
318 | |
319 | impl PartialEq<InterfaceName<'_>> for OwnedInterfaceName { |
320 | fn eq(&self, other: &InterfaceName<'_>) -> bool { |
321 | self.0 == *other |
322 | } |
323 | } |
324 | |
325 | impl Debug for OwnedInterfaceName { |
326 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
327 | f&mut DebugTuple<'_, '_>.debug_tuple(name:"OwnedInterfaceName" ) |
328 | .field(&self.as_str()) |
329 | .finish() |
330 | } |
331 | } |
332 | |
333 | impl Display for OwnedInterfaceName { |
334 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { |
335 | Display::fmt(&InterfaceName::from(self), f) |
336 | } |
337 | } |
338 | |
339 | impl NoneValue for OwnedInterfaceName { |
340 | type NoneType = <InterfaceName<'static> as NoneValue>::NoneType; |
341 | |
342 | fn null_value() -> Self::NoneType { |
343 | InterfaceName::null_value() |
344 | } |
345 | } |
346 | |