zbus_names/
unique_name.rs

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