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