dbus_message_parser/value/bus/
well_known_bus_name.rs

1use crate::value::MAXIMUM_NAME_LENGTH;
2use std::cmp::{Eq, PartialEq};
3use std::convert::{From, TryFrom};
4use std::fmt::{Display, Formatter, Result as FmtResult};
5use thiserror::Error;
6
7enum Input {
8    /// [A-Z][a-z]_-
9    AlphabeticAndUnderscoreAndHyphen,
10    /// [0-9]
11    Digit,
12    /// .
13    Dot,
14}
15
16impl TryFrom<u8> for Input {
17    type Error = WellKnownBusNameError;
18
19    fn try_from(c: u8) -> Result<Self, Self::Error> {
20        if c.is_ascii_alphabetic() || c == b'_' || c == b'-' {
21            Ok(Input::AlphabeticAndUnderscoreAndHyphen)
22        } else if c.is_ascii_digit() {
23            Ok(Input::Digit)
24        } else if c == b'.' {
25            Ok(Input::Dot)
26        } else {
27            Err(WellKnownBusNameError::InvalidChar(c))
28        }
29    }
30}
31
32enum State {
33    /// Start state.
34    Start,
35    /// The first element.
36    FirstElement,
37    /// The beginning of the second or subsequent element.
38    Dot,
39    /// The second or subsequent character of the second or subsequent element.
40    SubsequentElement,
41}
42
43impl State {
44    fn consume(self, i: Input) -> Result<State, WellKnownBusNameError> {
45        match self {
46            State::Start => match i {
47                Input::AlphabeticAndUnderscoreAndHyphen => Ok(State::FirstElement),
48                Input::Digit => Err(WellKnownBusNameError::BeginDigit),
49                Input::Dot => Err(WellKnownBusNameError::BeginDot),
50            },
51            State::FirstElement => match i {
52                Input::AlphabeticAndUnderscoreAndHyphen => Ok(State::FirstElement),
53                Input::Digit => Ok(State::FirstElement),
54                Input::Dot => Ok(State::Dot),
55            },
56            State::Dot => match i {
57                Input::AlphabeticAndUnderscoreAndHyphen => Ok(State::SubsequentElement),
58                Input::Digit => Err(WellKnownBusNameError::ElementBeginDigit),
59                Input::Dot => Err(WellKnownBusNameError::ElementBeginDot),
60            },
61            State::SubsequentElement => match i {
62                Input::AlphabeticAndUnderscoreAndHyphen => Ok(State::SubsequentElement),
63                Input::Digit => Ok(State::SubsequentElement),
64                Input::Dot => Ok(State::Dot),
65            },
66        }
67    }
68}
69
70/// Check if the given bytes is a valid [well-known bus name].
71///
72/// [well-known bus name]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-bus
73fn check(well_known_bus_name: &[u8]) -> Result<(), WellKnownBusNameError> {
74    let error_len = well_known_bus_name.len();
75    if MAXIMUM_NAME_LENGTH < error_len {
76        return Err(WellKnownBusNameError::ExceedMaximum(error_len));
77    }
78
79    let mut state = State::Start;
80    for c in well_known_bus_name {
81        let i = Input::try_from(*c)?;
82        state = state.consume(i)?;
83    }
84
85    match state {
86        State::Start => Err(WellKnownBusNameError::Empty),
87        State::FirstElement => Err(WellKnownBusNameError::Elements),
88        State::Dot => Err(WellKnownBusNameError::EndDot),
89        State::SubsequentElement => Ok(()),
90    }
91}
92
93/// This represents a [well-known bus name].
94///
95/// [well-known bus name]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-bus
96#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq)]
97pub struct WellKnownBusName(String);
98
99/// An enum representing all errors, which can occur during the handling of a [`WellKnownBusName`].
100#[derive(Debug, PartialEq, Eq, Error)]
101pub enum WellKnownBusNameError {
102    #[error("Well-known bus name must not begin with a digit")]
103    BeginDigit,
104    #[error("Well-known bus name must not begin with a '.'")]
105    BeginDot,
106    #[error("Well-known bus name must not end with '.'")]
107    EndDot,
108    #[error("Well-known bus name element must not begin with a digit")]
109    ElementBeginDigit,
110    #[error("Well-known bus name element must not begin with a '.'")]
111    ElementBeginDot,
112    #[error("Well-known bus name is empty")]
113    Empty,
114    #[error("Well-known bus name have to be composed of 2 or more elements")]
115    Elements,
116    #[error("Well-known bus name must not exceed the maximum length: {MAXIMUM_NAME_LENGTH} < {0}")]
117    ExceedMaximum(usize),
118    #[error("Bus must only contain '[A-Z][a-z][0-9]_-.': {0}")]
119    InvalidChar(u8),
120}
121
122impl From<WellKnownBusName> for String {
123    fn from(well_known_bus_name: WellKnownBusName) -> Self {
124        well_known_bus_name.0
125    }
126}
127
128impl TryFrom<String> for WellKnownBusName {
129    type Error = WellKnownBusNameError;
130
131    fn try_from(well_known_bus_name: String) -> Result<Self, Self::Error> {
132        check(well_known_bus_name.as_bytes())?;
133        Ok(WellKnownBusName(well_known_bus_name))
134    }
135}
136
137impl TryFrom<&str> for WellKnownBusName {
138    type Error = WellKnownBusNameError;
139
140    fn try_from(well_known_bus_name: &str) -> Result<Self, Self::Error> {
141        check(well_known_bus_name.as_bytes())?;
142        Ok(WellKnownBusName(well_known_bus_name.to_owned()))
143    }
144}
145
146impl TryFrom<&[u8]> for WellKnownBusName {
147    type Error = WellKnownBusNameError;
148
149    fn try_from(well_known_bus_name: &[u8]) -> Result<Self, Self::Error> {
150        check(well_known_bus_name)?;
151        let well_known_bus_name = well_known_bus_name.to_vec();
152        //  The vector only contains valid UTF-8 (ASCII) characters because it was already
153        //  checked by the `check` function above
154        unsafe {
155            Ok(WellKnownBusName(String::from_utf8_unchecked(
156                well_known_bus_name,
157            )))
158        }
159    }
160}
161
162impl Display for WellKnownBusName {
163    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
164        write!(f, "{}", self.0)
165    }
166}
167
168impl AsRef<str> for WellKnownBusName {
169    fn as_ref(&self) -> &str {
170        &self.0
171    }
172}
173
174impl PartialEq<str> for WellKnownBusName {
175    fn eq(&self, other: &str) -> bool {
176        self.as_ref() == other
177    }
178}