dbus_message_parser/value/bus/
unique_connection_name.rs1use 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 AlphanumericAndUnderscoreAndHyphen,
10 Dot,
12 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 Start,
35 BeginFirstElement,
37 FirstElement,
39 Dot,
41 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
79fn 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#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq)]
107pub struct UniqueConnectionName(String);
108
109#[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 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}