dbus_message_parser/value/
error.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 as ThisError;
6
7enum Input {
8    /// [A-Z][a-z]_
9    AlphabeticAndUnderscore,
10    /// [0-9]
11    Digit,
12    /// .
13    Dot,
14}
15
16impl TryFrom<u8> for Input {
17    type Error = ErrorError;
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(ErrorError::InvalidChar(c))
28        }
29    }
30}
31
32enum State {
33    /// The first character of the first element.
34    FirstElementBegin,
35    /// The second or subsequent character of the first element.
36    FirstElement,
37    /// The first character of the second or subsequent element.
38    ElementBegin,
39    /// The second or subsequent character of the second or subsequent element.
40    Element,
41}
42
43impl State {
44    #[inline]
45    fn consume(self, i: Input) -> Result<State, ErrorError> {
46        match self {
47            State::FirstElementBegin => match i {
48                Input::AlphabeticAndUnderscore => Ok(State::FirstElement),
49                Input::Digit => Err(ErrorError::ElementBeginDigit),
50                Input::Dot => Err(ErrorError::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(ErrorError::ElementBeginDigit),
60                Input::Dot => Err(ErrorError::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
71/// Check if the given bytes is a valid [error name].
72///
73/// [error name]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-error
74fn check(error: &[u8]) -> Result<(), ErrorError> {
75    let error_len = error.len();
76    if MAXIMUM_NAME_LENGTH < error_len {
77        return Err(ErrorError::ExceedMaximum(error_len));
78    }
79
80    let mut state = State::FirstElementBegin;
81    for c in error {
82        let i = Input::try_from(*c)?;
83        state = state.consume(i)?;
84    }
85
86    match state {
87        State::FirstElementBegin => Err(ErrorError::Empty),
88        State::FirstElement => Err(ErrorError::Elements),
89        State::ElementBegin => Err(ErrorError::EndDot),
90        State::Element => Ok(()),
91    }
92}
93
94/// This represents an [error name].
95///
96/// [error name]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-error
97#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
98pub struct Error(String);
99
100/// An enum representing all errors, which can occur during the handling of a [`Error`].
101#[derive(Debug, PartialEq, Eq, ThisError)]
102pub enum ErrorError {
103    #[error("Error element must not begin with a digit")]
104    ElementBeginDigit,
105    #[error("Error element must not beign with a '.'")]
106    ElementBeginDot,
107    #[error("Error must not end with '.'")]
108    EndDot,
109    #[error("Error most not be empty")]
110    Empty,
111    #[error("Error have to be composed of 2 or more elements")]
112    Elements,
113    #[error("Error must not exceed the maximum length: {MAXIMUM_NAME_LENGTH} < {0}")]
114    ExceedMaximum(usize),
115    #[error("Error must only contain '[A-Z][a-z][0-9]_.': {0}")]
116    InvalidChar(u8),
117}
118
119impl From<Error> for String {
120    fn from(error: Error) -> Self {
121        error.0
122    }
123}
124
125impl TryFrom<String> for Error {
126    type Error = ErrorError;
127
128    fn try_from(error: String) -> Result<Self, Self::Error> {
129        check(error.as_bytes())?;
130        Ok(Error(error))
131    }
132}
133
134impl TryFrom<&str> for Error {
135    type Error = ErrorError;
136
137    fn try_from(error: &str) -> Result<Self, Self::Error> {
138        check(error.as_bytes())?;
139        Ok(Error(error.to_owned()))
140    }
141}
142
143impl TryFrom<&[u8]> for Error {
144    type Error = ErrorError;
145
146    fn try_from(error: &[u8]) -> Result<Self, Self::Error> {
147        check(error)?;
148        let error = error.to_vec();
149        //  The vector only contains valid UTF-8 (ASCII) characters because it was already
150        //  checked by the `check` function above
151        unsafe { Ok(Error(String::from_utf8_unchecked(error))) }
152    }
153}
154
155impl Display for Error {
156    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
157        write!(f, "{}", self.0)
158    }
159}
160
161impl AsRef<str> for Error {
162    fn as_ref(&self) -> &str {
163        &self.0
164    }
165}
166
167impl PartialEq<str> for Error {
168    fn eq(&self, other: &str) -> bool {
169        self.as_ref() == other
170    }
171}