zvariant/
object_path.rs

1use core::{fmt::Debug, str};
2use serde::{
3    de::{self, Deserialize, Deserializer, Visitor},
4    ser::{Serialize, Serializer},
5};
6use std::borrow::{Borrow, Cow};
7
8use crate::{Basic, Error, Result, Str, Type};
9
10/// String that identifies objects at a given destination on the D-Bus bus.
11///
12/// Mostly likely this is only useful in the D-Bus context.
13///
14/// # Examples
15///
16/// ```
17/// use zvariant::ObjectPath;
18///
19/// // Valid object paths
20/// let o = ObjectPath::try_from("/").unwrap();
21/// assert_eq!(o, "/");
22/// let o = ObjectPath::try_from("/Path/t0/0bject").unwrap();
23/// assert_eq!(o, "/Path/t0/0bject");
24/// let o = ObjectPath::try_from("/a/very/looooooooooooooooooooooooo0000o0ng/path").unwrap();
25/// assert_eq!(o, "/a/very/looooooooooooooooooooooooo0000o0ng/path");
26///
27/// // Invalid object paths
28/// ObjectPath::try_from("").unwrap_err();
29/// ObjectPath::try_from("/double//slashes/").unwrap_err();
30/// ObjectPath::try_from(".").unwrap_err();
31/// ObjectPath::try_from("/end/with/slash/").unwrap_err();
32/// ObjectPath::try_from("/ha.d").unwrap_err();
33/// ```
34#[derive(PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
35pub struct ObjectPath<'a>(Str<'a>);
36
37impl<'a> ObjectPath<'a> {
38    /// This is faster than `Clone::clone` when `self` contains owned data.
39    pub fn as_ref(&self) -> ObjectPath<'_> {
40        ObjectPath(self.0.as_ref())
41    }
42
43    /// The object path as a string.
44    pub fn as_str(&self) -> &str {
45        self.0.as_str()
46    }
47
48    /// The object path as bytes.
49    pub fn as_bytes(&self) -> &[u8] {
50        self.0.as_bytes()
51    }
52
53    /// Create a new `ObjectPath` from given bytes.
54    ///
55    /// Since the passed bytes are not checked for correctness, prefer using the
56    /// `TryFrom<&[u8]>` implementation.
57    ///
58    /// # Safety
59    ///
60    /// See [`std::str::from_utf8_unchecked`].
61    pub unsafe fn from_bytes_unchecked<'s: 'a>(bytes: &'s [u8]) -> Self {
62        Self(std::str::from_utf8_unchecked(bytes).into())
63    }
64
65    /// Create a new `ObjectPath` from the given string.
66    ///
67    /// Since the passed string is not checked for correctness, prefer using the
68    /// `TryFrom<&str>` implementation.
69    pub fn from_str_unchecked<'s: 'a>(path: &'s str) -> Self {
70        Self(path.into())
71    }
72
73    /// Same as `try_from`, except it takes a `&'static str`.
74    pub fn from_static_str(name: &'static str) -> Result<Self> {
75        validate(name.as_bytes())?;
76
77        Ok(Self::from_static_str_unchecked(name))
78    }
79
80    /// Same as `from_str_unchecked`, except it takes a `&'static str`.
81    pub const fn from_static_str_unchecked(name: &'static str) -> Self {
82        Self(Str::from_static(name))
83    }
84
85    /// Same as `from_str_unchecked`, except it takes an owned `String`.
86    ///
87    /// Since the passed string is not checked for correctness, prefer using the
88    /// `TryFrom<String>` implementation.
89    pub fn from_string_unchecked(path: String) -> Self {
90        Self(path.into())
91    }
92
93    /// the object path's length.
94    pub fn len(&self) -> usize {
95        self.0.len()
96    }
97
98    /// if the object path is empty.
99    pub fn is_empty(&self) -> bool {
100        self.0.is_empty()
101    }
102
103    /// Creates an owned clone of `self`.
104    pub fn to_owned(&self) -> ObjectPath<'static> {
105        ObjectPath(self.0.to_owned())
106    }
107
108    /// Creates an owned clone of `self`.
109    ///
110    /// Results in an extra allocation only if the lifetime of `self` is not static.
111    pub fn into_owned(self) -> ObjectPath<'static> {
112        ObjectPath(self.0.into_owned())
113    }
114}
115
116impl std::default::Default for ObjectPath<'_> {
117    fn default() -> Self {
118        ObjectPath::from_static_str_unchecked("/")
119    }
120}
121
122impl Basic for ObjectPath<'_> {
123    const SIGNATURE_CHAR: char = 'o';
124    const SIGNATURE_STR: &'static str = "o";
125}
126
127impl Type for ObjectPath<'_> {
128    const SIGNATURE: &'static crate::Signature = &crate::Signature::ObjectPath;
129}
130
131impl<'a> TryFrom<&'a [u8]> for ObjectPath<'a> {
132    type Error = Error;
133
134    fn try_from(value: &'a [u8]) -> Result<Self> {
135        validate(value)?;
136
137        // SAFETY: ensure_correct_object_path_str checks UTF-8
138        unsafe { Ok(Self::from_bytes_unchecked(value)) }
139    }
140}
141
142/// Try to create an ObjectPath from a string.
143impl<'a> TryFrom<&'a str> for ObjectPath<'a> {
144    type Error = Error;
145
146    fn try_from(value: &'a str) -> Result<Self> {
147        Self::try_from(value.as_bytes())
148    }
149}
150
151impl TryFrom<String> for ObjectPath<'_> {
152    type Error = Error;
153
154    fn try_from(value: String) -> Result<Self> {
155        validate(value.as_bytes())?;
156
157        Ok(Self::from_string_unchecked(value))
158    }
159}
160
161impl<'a> TryFrom<Cow<'a, str>> for ObjectPath<'a> {
162    type Error = Error;
163
164    fn try_from(value: Cow<'a, str>) -> Result<Self> {
165        match value {
166            Cow::Borrowed(s) => Self::try_from(s),
167            Cow::Owned(s) => Self::try_from(s),
168        }
169    }
170}
171
172impl<'o> From<&ObjectPath<'o>> for ObjectPath<'o> {
173    fn from(o: &ObjectPath<'o>) -> Self {
174        o.clone()
175    }
176}
177
178impl std::ops::Deref for ObjectPath<'_> {
179    type Target = str;
180
181    fn deref(&self) -> &Self::Target {
182        self.as_str()
183    }
184}
185
186impl PartialEq<str> for ObjectPath<'_> {
187    fn eq(&self, other: &str) -> bool {
188        self.as_str() == other
189    }
190}
191
192impl PartialEq<&str> for ObjectPath<'_> {
193    fn eq(&self, other: &&str) -> bool {
194        self.as_str() == *other
195    }
196}
197
198impl Debug for ObjectPath<'_> {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200        f.debug_tuple("ObjectPath").field(&self.as_str()).finish()
201    }
202}
203
204impl std::fmt::Display for ObjectPath<'_> {
205    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206        std::fmt::Display::fmt(&self.as_str(), f)
207    }
208}
209
210impl Serialize for ObjectPath<'_> {
211    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
212    where
213        S: Serializer,
214    {
215        serializer.serialize_str(self.as_str())
216    }
217}
218
219impl<'de: 'a, 'a> Deserialize<'de> for ObjectPath<'a> {
220    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
221    where
222        D: Deserializer<'de>,
223    {
224        let visitor = ObjectPathVisitor;
225
226        deserializer.deserialize_str(visitor)
227    }
228}
229
230struct ObjectPathVisitor;
231
232impl<'de> Visitor<'de> for ObjectPathVisitor {
233    type Value = ObjectPath<'de>;
234
235    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
236        formatter.write_str("an ObjectPath")
237    }
238
239    #[inline]
240    fn visit_borrowed_str<E>(self, value: &'de str) -> core::result::Result<ObjectPath<'de>, E>
241    where
242        E: serde::de::Error,
243    {
244        ObjectPath::try_from(value).map_err(serde::de::Error::custom)
245    }
246}
247
248fn validate(path: &[u8]) -> Result<()> {
249    use winnow::{combinator::separated, stream::AsChar, token::take_while, Parser};
250    // Rules
251    //
252    // * At least 1 character.
253    // * First character must be `/`
254    // * No trailing `/`
255    // * No `//`
256    // * Only ASCII alphanumeric, `_` or '/'
257
258    let allowed_chars = (AsChar::is_alphanum, b'_');
259    let name = take_while::<_, _, ()>(1.., allowed_chars);
260    let mut full_path = (b'/', separated(0.., name, b'/')).map(|_: (u8, ())| ());
261
262    full_path.parse(path).map_err(|_| Error::InvalidObjectPath)
263}
264
265/// Owned [`ObjectPath`](struct.ObjectPath.html)
266#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize, Type)]
267pub struct OwnedObjectPath(ObjectPath<'static>);
268
269impl OwnedObjectPath {
270    pub fn into_inner(self) -> ObjectPath<'static> {
271        self.0
272    }
273}
274
275impl Basic for OwnedObjectPath {
276    const SIGNATURE_CHAR: char = ObjectPath::SIGNATURE_CHAR;
277    const SIGNATURE_STR: &'static str = ObjectPath::SIGNATURE_STR;
278}
279
280impl std::ops::Deref for OwnedObjectPath {
281    type Target = ObjectPath<'static>;
282
283    fn deref(&self) -> &Self::Target {
284        &self.0
285    }
286}
287
288impl<'a> Borrow<ObjectPath<'a>> for OwnedObjectPath {
289    fn borrow(&self) -> &ObjectPath<'a> {
290        &self.0
291    }
292}
293
294impl std::convert::From<OwnedObjectPath> for ObjectPath<'static> {
295    fn from(o: OwnedObjectPath) -> Self {
296        o.into_inner()
297    }
298}
299
300impl std::convert::From<OwnedObjectPath> for crate::Value<'_> {
301    fn from(o: OwnedObjectPath) -> Self {
302        o.into_inner().into()
303    }
304}
305
306impl<'unowned, 'owned: 'unowned> From<&'owned OwnedObjectPath> for ObjectPath<'unowned> {
307    fn from(o: &'owned OwnedObjectPath) -> Self {
308        ObjectPath::from_str_unchecked(o.as_str())
309    }
310}
311
312impl<'a> std::convert::From<ObjectPath<'a>> for OwnedObjectPath {
313    fn from(o: ObjectPath<'a>) -> Self {
314        OwnedObjectPath(o.into_owned())
315    }
316}
317
318impl TryFrom<&'_ str> for OwnedObjectPath {
319    type Error = Error;
320
321    fn try_from(value: &str) -> Result<Self> {
322        Ok(Self::from(ObjectPath::try_from(value)?))
323    }
324}
325
326impl TryFrom<String> for OwnedObjectPath {
327    type Error = Error;
328
329    fn try_from(value: String) -> Result<Self> {
330        Ok(Self::from(ObjectPath::try_from(value)?))
331    }
332}
333
334impl<'de> Deserialize<'de> for OwnedObjectPath {
335    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
336    where
337        D: Deserializer<'de>,
338    {
339        String::deserialize(deserializer)
340            .and_then(|s| ObjectPath::try_from(s).map_err(|e| de::Error::custom(e.to_string())))
341            .map(Self)
342    }
343}
344
345impl std::fmt::Display for OwnedObjectPath {
346    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
347        std::fmt::Display::fmt(&self.as_str(), f)
348    }
349}
350
351#[cfg(test)]
352mod unit {
353    use super::*;
354
355    #[test]
356    fn owned_from_reader() {
357        // See https://github.com/dbus2/zbus/issues/287
358        let json_str = "\"/some/path\"";
359        serde_json::de::from_reader::<_, OwnedObjectPath>(json_str.as_bytes()).unwrap();
360    }
361}