dbus_message_parser/value/
member.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    AlphabeticAndUnderscore,
10    /// [0-9]
11    Digit,
12}
13
14impl TryFrom<u8> for Input {
15    type Error = MemberError;
16
17    fn try_from(c: u8) -> Result<Self, Self::Error> {
18        if c.is_ascii_alphabetic() || c == b'_' {
19            Ok(Input::AlphabeticAndUnderscore)
20        } else if c.is_ascii_digit() {
21            Ok(Input::Digit)
22        } else {
23            Err(MemberError::InvalidChar(c))
24        }
25    }
26}
27
28/// Check if the given bytes is a valid [member name].
29///
30/// [member name]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-member
31fn check(member: &[u8]) -> Result<(), MemberError> {
32    let member_len = member.len();
33    if MAXIMUM_NAME_LENGTH < member_len {
34        return Err(MemberError::ExceedMaximum(member_len));
35    }
36
37    let mut member_iter = member.iter();
38    match member_iter.next() {
39        Some(c) => {
40            if let Input::Digit = Input::try_from(*c)? {
41                return Err(MemberError::BeginDigit);
42            }
43        }
44        None => return Err(MemberError::Empty),
45    }
46
47    for c in member_iter {
48        Input::try_from(*c)?;
49    }
50    Ok(())
51}
52
53/// This represents a [member name].
54///
55/// [member name]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-member
56#[derive(Debug, Clone, PartialOrd, PartialEq, Ord, Eq, Hash)]
57pub struct Member(String);
58
59/// An enum representing all errors, which can occur during the handling of a [`Member`].
60#[derive(Debug, PartialEq, Eq, Error)]
61pub enum MemberError {
62    #[error("Member must not be empty")]
63    Empty,
64    #[error("Member must not begin with a digit")]
65    BeginDigit,
66    #[error("Member must not exceed the maximum length: {MAXIMUM_NAME_LENGTH} < {0}")]
67    ExceedMaximum(usize),
68    #[error("Member contians an invalid char: {0}")]
69    InvalidChar(u8),
70}
71
72impl From<Member> for String {
73    fn from(member: Member) -> Self {
74        member.0
75    }
76}
77
78impl TryFrom<String> for Member {
79    type Error = MemberError;
80
81    fn try_from(member: String) -> Result<Self, Self::Error> {
82        check(member.as_bytes())?;
83        Ok(Member(member))
84    }
85}
86
87impl TryFrom<&str> for Member {
88    type Error = MemberError;
89
90    fn try_from(member: &str) -> Result<Self, Self::Error> {
91        check(member.as_bytes())?;
92        Ok(Member(member.to_owned()))
93    }
94}
95
96impl TryFrom<&[u8]> for Member {
97    type Error = MemberError;
98
99    fn try_from(member: &[u8]) -> Result<Self, Self::Error> {
100        check(member)?;
101        let member = member.to_vec();
102        //  The vector only contains valid UTF-8 (ASCII) characters because it was already
103        //  checked by the `check` function above
104        unsafe { Ok(Member(String::from_utf8_unchecked(member))) }
105    }
106}
107
108impl Display for Member {
109    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
110        write!(f, "{}", self.0)
111    }
112}
113
114impl AsRef<str> for Member {
115    fn as_ref(&self) -> &str {
116        &self.0
117    }
118}
119
120impl PartialEq<str> for Member {
121    fn eq(&self, other: &str) -> bool {
122        self.0 == other
123    }
124}