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