zbus_names/
member_name.rs

1use crate::{
2    utils::{impl_str_basic, impl_try_from},
3    Error, Result,
4};
5use serde::{de, Deserialize, Serialize};
6use static_assertions::assert_impl_all;
7use std::{
8    borrow::{Borrow, Cow},
9    fmt::{self, Debug, Display, Formatter},
10    ops::Deref,
11    sync::Arc,
12};
13use zvariant::{NoneValue, OwnedValue, Str, Type, Value};
14
15/// String that identifies an [member (method or signal) name][in] on the bus.
16///
17/// # Examples
18///
19/// ```
20/// use zbus_names::MemberName;
21///
22/// // Valid member names.
23/// let name = MemberName::try_from("Member_for_you").unwrap();
24/// assert_eq!(name, "Member_for_you");
25/// let name = MemberName::try_from("CamelCase101").unwrap();
26/// assert_eq!(name, "CamelCase101");
27/// let name = MemberName::try_from("a_very_loooooooooooooooooo_ooooooo_0000o0ngName").unwrap();
28/// assert_eq!(name, "a_very_loooooooooooooooooo_ooooooo_0000o0ngName");
29///
30/// // Invalid member names
31/// MemberName::try_from("").unwrap_err();
32/// MemberName::try_from(".").unwrap_err();
33/// MemberName::try_from("1startWith_a_Digit").unwrap_err();
34/// MemberName::try_from("contains.dots_in_the_name").unwrap_err();
35/// MemberName::try_from("contains-dashes-in_the_name").unwrap_err();
36/// ```
37///
38/// [in]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-member
39#[derive(
40    Clone, Debug, Hash, PartialEq, Eq, Serialize, Type, Value, PartialOrd, Ord, OwnedValue,
41)]
42pub struct MemberName<'name>(Str<'name>);
43
44assert_impl_all!(MemberName<'_>: Send, Sync, Unpin);
45
46impl_str_basic!(MemberName<'_>);
47
48impl<'name> MemberName<'name> {
49    /// This is faster than `Clone::clone` when `self` contains owned data.
50    pub fn as_ref(&self) -> MemberName<'_> {
51        MemberName(self.0.as_ref())
52    }
53
54    /// The member name as string.
55    pub fn as_str(&self) -> &str {
56        self.0.as_str()
57    }
58
59    /// Create a new `MemberName` from the given string.
60    ///
61    /// Since the passed string is not checked for correctness, prefer using the
62    /// `TryFrom<&str>` implementation.
63    pub fn from_str_unchecked(name: &'name str) -> Self {
64        Self(Str::from(name))
65    }
66
67    /// Same as `try_from`, except it takes a `&'static str`.
68    pub fn from_static_str(name: &'static str) -> Result<Self> {
69        ensure_correct_member_name(name)?;
70        Ok(Self(Str::from_static(name)))
71    }
72
73    /// Same as `from_str_unchecked`, except it takes a `&'static str`.
74    pub const fn from_static_str_unchecked(name: &'static str) -> Self {
75        Self(Str::from_static(name))
76    }
77
78    /// Same as `from_str_unchecked`, except it takes an owned `String`.
79    ///
80    /// Since the passed string is not checked for correctness, prefer using the
81    /// `TryFrom<String>` implementation.
82    pub fn from_string_unchecked(name: String) -> Self {
83        Self(Str::from(name))
84    }
85
86    /// Creates an owned clone of `self`.
87    pub fn to_owned(&self) -> MemberName<'static> {
88        MemberName(self.0.to_owned())
89    }
90
91    /// Creates an owned clone of `self`.
92    pub fn into_owned(self) -> MemberName<'static> {
93        MemberName(self.0.into_owned())
94    }
95}
96
97impl Deref for MemberName<'_> {
98    type Target = str;
99
100    fn deref(&self) -> &Self::Target {
101        self.as_str()
102    }
103}
104
105impl Borrow<str> for MemberName<'_> {
106    fn borrow(&self) -> &str {
107        self.as_str()
108    }
109}
110
111impl Display for MemberName<'_> {
112    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
113        Display::fmt(&self.as_str(), f)
114    }
115}
116
117impl PartialEq<str> for MemberName<'_> {
118    fn eq(&self, other: &str) -> bool {
119        self.as_str() == other
120    }
121}
122
123impl PartialEq<&str> for MemberName<'_> {
124    fn eq(&self, other: &&str) -> bool {
125        self.as_str() == *other
126    }
127}
128
129impl PartialEq<OwnedMemberName> for MemberName<'_> {
130    fn eq(&self, other: &OwnedMemberName) -> bool {
131        *self == other.0
132    }
133}
134
135impl<'de: 'name, 'name> Deserialize<'de> for MemberName<'name> {
136    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
137    where
138        D: serde::Deserializer<'de>,
139    {
140        let name = <Cow<'name, str>>::deserialize(deserializer)?;
141
142        Self::try_from(name).map_err(|e| de::Error::custom(e.to_string()))
143    }
144}
145
146impl<'name> From<MemberName<'name>> for Str<'name> {
147    fn from(value: MemberName<'name>) -> Self {
148        value.0
149    }
150}
151
152impl_try_from! {
153    ty: MemberName<'s>,
154    owned_ty: OwnedMemberName,
155    validate_fn: ensure_correct_member_name,
156    try_from: [&'s str, String, Arc<str>, Cow<'s, str>, Str<'s>],
157}
158
159fn ensure_correct_member_name(name: &str) -> Result<()> {
160    // Rules
161    //
162    // * Only ASCII alphanumeric or `_`.
163    // * Must not begin with a digit.
164    // * Must contain at least 1 character.
165    // * <= 255 characters.
166    if name.is_empty() {
167        return Err(Error::InvalidMemberName(format!(
168            "`{}` is {} characters long, which is smaller than minimum allowed (1)",
169            name,
170            name.len(),
171        )));
172    } else if name.len() > 255 {
173        return Err(Error::InvalidMemberName(format!(
174            "`{}` is {} characters long, which is longer than maximum allowed (255)",
175            name,
176            name.len(),
177        )));
178    }
179
180    // SAFETY: We established above that there is at least 1 character so unwrap is fine.
181    if name.chars().next().unwrap().is_ascii_digit() {
182        return Err(Error::InvalidMemberName(String::from(
183            "must not start with a digit",
184        )));
185    }
186
187    for c in name.chars() {
188        if !c.is_ascii_alphanumeric() && c != '_' {
189            return Err(Error::InvalidMemberName(format!(
190                "`{c}` character not allowed"
191            )));
192        }
193    }
194
195    Ok(())
196}
197
198/// This never succeeds but is provided so it's easier to pass `Option::None` values for API
199/// requiring `Option<TryInto<impl BusName>>`, since type inference won't work here.
200impl TryFrom<()> for MemberName<'_> {
201    type Error = Error;
202
203    fn try_from(_value: ()) -> Result<Self> {
204        unreachable!("Conversion from `()` is not meant to actually work");
205    }
206}
207
208impl<'name> From<&MemberName<'name>> for MemberName<'name> {
209    fn from(name: &MemberName<'name>) -> Self {
210        name.clone()
211    }
212}
213
214impl<'name> NoneValue for MemberName<'name> {
215    type NoneType = &'name str;
216
217    fn null_value() -> Self::NoneType {
218        <&str>::default()
219    }
220}
221
222/// Owned sibling of [`MemberName`].
223#[derive(Clone, Hash, PartialEq, Eq, Serialize, Type, Value, PartialOrd, Ord, OwnedValue)]
224pub struct OwnedMemberName(#[serde(borrow)] MemberName<'static>);
225
226assert_impl_all!(OwnedMemberName: Send, Sync, Unpin);
227
228impl_str_basic!(OwnedMemberName);
229
230impl OwnedMemberName {
231    /// Convert to the inner `MemberName`, consuming `self`.
232    pub fn into_inner(self) -> MemberName<'static> {
233        self.0
234    }
235
236    /// Get a reference to the inner `MemberName`.
237    pub fn inner(&self) -> &MemberName<'static> {
238        &self.0
239    }
240}
241
242impl Deref for OwnedMemberName {
243    type Target = MemberName<'static>;
244
245    fn deref(&self) -> &Self::Target {
246        &self.0
247    }
248}
249
250impl Borrow<str> for OwnedMemberName {
251    fn borrow(&self) -> &str {
252        self.0.as_str()
253    }
254}
255
256impl From<OwnedMemberName> for MemberName<'_> {
257    fn from(o: OwnedMemberName) -> Self {
258        o.into_inner()
259    }
260}
261
262impl<'unowned, 'owned: 'unowned> From<&'owned OwnedMemberName> for MemberName<'unowned> {
263    fn from(name: &'owned OwnedMemberName) -> Self {
264        MemberName::from_str_unchecked(name.as_str())
265    }
266}
267
268impl From<MemberName<'_>> for OwnedMemberName {
269    fn from(name: MemberName<'_>) -> Self {
270        OwnedMemberName(name.into_owned())
271    }
272}
273
274impl From<OwnedMemberName> for Str<'_> {
275    fn from(value: OwnedMemberName) -> Self {
276        value.into_inner().0
277    }
278}
279
280impl<'de> Deserialize<'de> for OwnedMemberName {
281    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
282    where
283        D: de::Deserializer<'de>,
284    {
285        String::deserialize(deserializer)
286            .and_then(|n| MemberName::try_from(n).map_err(|e| de::Error::custom(e.to_string())))
287            .map(Self)
288    }
289}
290
291impl PartialEq<&str> for OwnedMemberName {
292    fn eq(&self, other: &&str) -> bool {
293        self.as_str() == *other
294    }
295}
296
297impl PartialEq<MemberName<'_>> for OwnedMemberName {
298    fn eq(&self, other: &MemberName<'_>) -> bool {
299        self.0 == *other
300    }
301}
302
303impl Debug for OwnedMemberName {
304    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
305        f.debug_tuple("OwnedMemberName")
306            .field(&self.as_str())
307            .finish()
308    }
309}
310
311impl Display for OwnedMemberName {
312    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
313        Display::fmt(&MemberName::from(self), f)
314    }
315}
316
317impl NoneValue for OwnedMemberName {
318    type NoneType = <MemberName<'static> as NoneValue>::NoneType;
319
320    fn null_value() -> Self::NoneType {
321        MemberName::null_value()
322    }
323}