dbus_message_parser/value/
interface.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 AlphabeticAndUnderscore,
10 Digit,
12 Dot,
14}
15
16impl TryFrom<u8> for Input {
17 type Error = InterfaceError;
18
19 fn try_from(c: u8) -> Result<Self, Self::Error> {
20 if c.is_ascii_alphabetic() || c == b'_' {
21 Ok(Input::AlphabeticAndUnderscore)
22 } else if c.is_ascii_digit() {
23 Ok(Input::Digit)
24 } else if c == b'.' {
25 Ok(Input::Dot)
26 } else {
27 Err(InterfaceError::InvalidChar(c))
28 }
29 }
30}
31
32enum State {
33 FirstElementBegin,
35 FirstElement,
37 ElementBegin,
39 Element,
41}
42
43impl State {
44 #[inline]
45 fn consume(self, i: Input) -> Result<State, InterfaceError> {
46 match self {
47 State::FirstElementBegin => match i {
48 Input::AlphabeticAndUnderscore => Ok(State::FirstElement),
49 Input::Digit => Err(InterfaceError::ElementBeginDigit),
50 Input::Dot => Err(InterfaceError::ElementBeginDot),
51 },
52 State::FirstElement => match i {
53 Input::AlphabeticAndUnderscore => Ok(State::FirstElement),
54 Input::Digit => Ok(State::FirstElement),
55 Input::Dot => Ok(State::ElementBegin),
56 },
57 State::ElementBegin => match i {
58 Input::AlphabeticAndUnderscore => Ok(State::Element),
59 Input::Digit => Err(InterfaceError::ElementBeginDigit),
60 Input::Dot => Err(InterfaceError::ElementBeginDot),
61 },
62 State::Element => match i {
63 Input::AlphabeticAndUnderscore => Ok(State::Element),
64 Input::Digit => Ok(State::Element),
65 Input::Dot => Ok(State::ElementBegin),
66 },
67 }
68 }
69}
70
71fn check(interface: &[u8]) -> Result<(), InterfaceError> {
75 let interface_len = interface.len();
76 if MAXIMUM_NAME_LENGTH < interface_len {
77 return Err(InterfaceError::ExceedMaximum(interface_len));
78 }
79
80 let mut state = State::FirstElementBegin;
81 for c in interface {
82 let i = Input::try_from(*c)?;
83 state = state.consume(i)?;
84 }
85
86 match state {
87 State::FirstElementBegin => Err(InterfaceError::Empty),
88 State::FirstElement => Err(InterfaceError::Elements),
89 State::ElementBegin => Err(InterfaceError::EndDot),
90 State::Element => Ok(()),
91 }
92}
93
94#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
98pub struct Interface(String);
99
100#[derive(Debug, PartialEq, Eq, Error)]
102pub enum InterfaceError {
103 #[error("Interface element must not begin with a digit")]
104 ElementBeginDigit,
105 #[error("Interface element must not beign with a '.'")]
106 ElementBeginDot,
107 #[error("Interface must not end with '.'")]
108 EndDot,
109 #[error("Interface must not be empty")]
110 Empty,
111 #[error("Interface have to be composed of 2 or more elements")]
112 Elements,
113 #[error("Interface must not exceed the maximum length: {MAXIMUM_NAME_LENGTH} < {0}")]
114 ExceedMaximum(usize),
115 #[error("Interface must only contain '[A-Z][a-z][0-9]_.': {0}")]
116 InvalidChar(u8),
117}
118
119impl From<Interface> for String {
120 fn from(interface: Interface) -> Self {
121 interface.0
122 }
123}
124
125impl TryFrom<String> for Interface {
126 type Error = InterfaceError;
127
128 fn try_from(interface: String) -> Result<Self, Self::Error> {
129 check(interface.as_bytes())?;
130 Ok(Interface(interface))
131 }
132}
133
134impl TryFrom<&str> for Interface {
135 type Error = InterfaceError;
136
137 fn try_from(interface: &str) -> Result<Self, Self::Error> {
138 check(interface.as_bytes())?;
139 Ok(Interface(interface.to_owned()))
140 }
141}
142
143impl TryFrom<&[u8]> for Interface {
144 type Error = InterfaceError;
145
146 fn try_from(interface: &[u8]) -> Result<Self, Self::Error> {
147 check(interface)?;
148 let interface = interface.to_vec();
149 unsafe { Ok(Interface(String::from_utf8_unchecked(interface))) }
152 }
153}
154
155impl Display for Interface {
156 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
157 write!(f, "{}", self.0)
158 }
159}
160
161impl AsRef<str> for Interface {
162 fn as_ref(&self) -> &str {
163 &self.0
164 }
165}
166
167impl PartialEq<str> for Interface {
168 fn eq(&self, other: &str) -> bool {
169 self.as_ref() == other
170 }
171}