zbus_names/
unique_name.rs

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