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