zbus_names/well_known_name.rs
1use crate::{Error, Result, utils::define_name_type_impls};
2use serde::Serialize;
3use zvariant::{OwnedValue, Str, Type, Value};
4
5/// String that identifies a [well-known bus name][wbn].
6///
7/// # Examples
8///
9/// ```
10/// use zbus_names::WellKnownName;
11///
12/// // Valid well-known names.
13/// let name = WellKnownName::try_from("org.gnome.Service-for_you").unwrap();
14/// assert_eq!(name, "org.gnome.Service-for_you");
15/// let name = WellKnownName::try_from("a.very.loooooooooooooooooo-ooooooo_0000o0ng.Name").unwrap();
16/// assert_eq!(name, "a.very.loooooooooooooooooo-ooooooo_0000o0ng.Name");
17///
18/// // Invalid well-known names
19/// WellKnownName::try_from("").unwrap_err();
20/// WellKnownName::try_from("double..dots").unwrap_err();
21/// WellKnownName::try_from(".").unwrap_err();
22/// WellKnownName::try_from(".start.with.dot").unwrap_err();
23/// WellKnownName::try_from("1st.element.starts.with.digit").unwrap_err();
24/// WellKnownName::try_from("the.2nd.element.starts.with.digit").unwrap_err();
25/// WellKnownName::try_from("no-dots").unwrap_err();
26/// ```
27///
28/// [wbn]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-bus
29#[derive(
30 Clone, Debug, Hash, PartialEq, Eq, Serialize, Type, Value, PartialOrd, Ord, OwnedValue,
31)]
32pub struct WellKnownName<'name>(pub(crate) Str<'name>);
33
34/// Owned sibling of [`WellKnownName`].
35#[derive(Clone, Hash, PartialEq, Eq, Serialize, Type, Value, PartialOrd, Ord, OwnedValue)]
36pub struct OwnedWellKnownName(#[serde(borrow)] WellKnownName<'static>);
37
38define_name_type_impls! {
39 name: WellKnownName,
40 owned: OwnedWellKnownName,
41 validate: validate,
42}
43
44fn validate(name: &str) -> Result<()> {
45 validate_bytes(name.as_bytes()).map_err(|_| {
46 Error::InvalidName(
47 "Invalid well-known name. \
48 See https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-bus"
49 )
50 })
51}
52
53pub(crate) fn validate_bytes(bytes: &[u8]) -> std::result::Result<(), ()> {
54 use winnow::{
55 Parser,
56 combinator::separated,
57 stream::AsChar,
58 token::{one_of, take_while},
59 };
60 // Rules
61 //
62 // * Only ASCII alphanumeric, `_` or '-'.
63 // * Must not begin with a `.`.
64 // * Must contain at least one `.`.
65 // * Each element must:
66 // * not begin with a digit.
67 // * be 1 character (so name must be minimum 3 characters long).
68 // * <= 255 characters.
69 let first_element_char = one_of((AsChar::is_alpha, b'_', b'-'));
70 let subsequent_element_chars = take_while::<_, _, ()>(0.., (AsChar::is_alphanum, b'_', b'-'));
71 let element = (first_element_char, subsequent_element_chars);
72 let mut well_known_name = separated(2.., element, b'.');
73
74 well_known_name
75 .parse(bytes)
76 .map_err(|_| ())
77 .and_then(|_: ()| {
78 // Least likely scenario so we check this last.
79 if bytes.len() > 255 {
80 return Err(());
81 }
82
83 Ok(())
84 })
85}