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    pub fn into_owned(self) -> ObjectPath<'static> {
110        ObjectPath(self.0.into_owned())
111    }
112}
113
114impl std::default::Default for ObjectPath<'_> {
115    fn default() -> Self {
116        ObjectPath::from_static_str_unchecked("/")
117    }
118}
119
120impl Basic for ObjectPath<'_> {
121    const SIGNATURE_CHAR: char = 'o';
122    const SIGNATURE_STR: &'static str = "o";
123}
124
125impl Type for ObjectPath<'_> {
126    const SIGNATURE: &'static crate::Signature = &crate::Signature::ObjectPath;
127}
128
129impl<'a> TryFrom<&'a [u8]> for ObjectPath<'a> {
130    type Error = Error;
131
132    fn try_from(value: &'a [u8]) -> Result<Self> {
133        validate(value)?;
134
135        // SAFETY: ensure_correct_object_path_str checks UTF-8
136        unsafe { Ok(Self::from_bytes_unchecked(value)) }
137    }
138}
139
140/// Try to create an ObjectPath from a string.
141impl<'a> TryFrom<&'a str> for ObjectPath<'a> {
142    type Error = Error;
143
144    fn try_from(value: &'a str) -> Result<Self> {
145        Self::try_from(value.as_bytes())
146    }
147}
148
149impl TryFrom<String> for ObjectPath<'_> {
150    type Error = Error;
151
152    fn try_from(value: String) -> Result<Self> {
153        validate(value.as_bytes())?;
154
155        Ok(Self::from_string_unchecked(value))
156    }
157}
158
159impl<'a> TryFrom<Cow<'a, str>> for ObjectPath<'a> {
160    type Error = Error;
161
162    fn try_from(value: Cow<'a, str>) -> Result<Self> {
163        match value {
164            Cow::Borrowed(s) => Self::try_from(s),
165            Cow::Owned(s) => Self::try_from(s),
166        }
167    }
168}
169
170impl<'o> From<&ObjectPath<'o>> for ObjectPath<'o> {
171    fn from(o: &ObjectPath<'o>) -> Self {
172        o.clone()
173    }
174}
175
176impl std::ops::Deref for ObjectPath<'_> {
177    type Target = str;
178
179    fn deref(&self) -> &Self::Target {
180        self.as_str()
181    }
182}
183
184impl PartialEq<str> for ObjectPath<'_> {
185    fn eq(&self, other: &str) -> bool {
186        self.as_str() == other
187    }
188}
189
190impl PartialEq<&str> for ObjectPath<'_> {
191    fn eq(&self, other: &&str) -> bool {
192        self.as_str() == *other
193    }
194}
195
196impl Debug for ObjectPath<'_> {
197    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198        f.debug_tuple("ObjectPath").field(&self.as_str()).finish()
199    }
200}
201
202impl std::fmt::Display for ObjectPath<'_> {
203    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204        std::fmt::Display::fmt(&self.as_str(), f)
205    }
206}
207
208impl Serialize for ObjectPath<'_> {
209    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
210    where
211        S: Serializer,
212    {
213        serializer.serialize_str(self.as_str())
214    }
215}
216
217impl<'de: 'a, 'a> Deserialize<'de> for ObjectPath<'a> {
218    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
219    where
220        D: Deserializer<'de>,
221    {
222        let visitor = ObjectPathVisitor;
223
224        deserializer.deserialize_str(visitor)
225    }
226}
227
228struct ObjectPathVisitor;
229
230impl<'de> Visitor<'de> for ObjectPathVisitor {
231    type Value = ObjectPath<'de>;
232
233    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234        formatter.write_str("an ObjectPath")
235    }
236
237    #[inline]
238    fn visit_borrowed_str<E>(self, value: &'de str) -> core::result::Result<ObjectPath<'de>, E>
239    where
240        E: serde::de::Error,
241    {
242        ObjectPath::try_from(value).map_err(serde::de::Error::custom)
243    }
244}
245
246fn validate(path: &[u8]) -> Result<()> {
247    use winnow::{combinator::separated, stream::AsChar, token::take_while, Parser};
248    // Rules
249    //
250    // * At least 1 character.
251    // * First character must be `/`
252    // * No trailing `/`
253    // * No `//`
254    // * Only ASCII alphanumeric, `_` or '/'
255
256    let allowed_chars = (AsChar::is_alphanum, b'_');
257    let name = take_while::<_, _, ()>(1.., allowed_chars);
258    let mut full_path = (b'/', separated(0.., name, b'/')).map(|_: (u8, ())| ());
259
260    full_path.parse(path).map_err(|_| Error::InvalidObjectPath)
261}
262
263/// Owned [`ObjectPath`](struct.ObjectPath.html)
264#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, serde::Serialize, Type)]
265pub struct OwnedObjectPath(ObjectPath<'static>);
266
267impl OwnedObjectPath {
268    pub fn into_inner(self) -> ObjectPath<'static> {
269        self.0
270    }
271}
272
273impl Basic for OwnedObjectPath {
274    const SIGNATURE_CHAR: char = ObjectPath::SIGNATURE_CHAR;
275    const SIGNATURE_STR: &'static str = ObjectPath::SIGNATURE_STR;
276}
277
278impl std::ops::Deref for OwnedObjectPath {
279    type Target = ObjectPath<'static>;
280
281    fn deref(&self) -> &Self::Target {
282        &self.0
283    }
284}
285
286impl<'a> Borrow<ObjectPath<'a>> for OwnedObjectPath {
287    fn borrow(&self) -> &ObjectPath<'a> {
288        &self.0
289    }
290}
291
292impl std::convert::From<OwnedObjectPath> for ObjectPath<'static> {
293    fn from(o: OwnedObjectPath) -> Self {
294        o.into_inner()
295    }
296}
297
298impl std::convert::From<OwnedObjectPath> for crate::Value<'_> {
299    fn from(o: OwnedObjectPath) -> Self {
300        o.into_inner().into()
301    }
302}
303
304impl<'unowned, 'owned: 'unowned> From<&'owned OwnedObjectPath> for ObjectPath<'unowned> {
305    fn from(o: &'owned OwnedObjectPath) -> Self {
306        ObjectPath::from_str_unchecked(o.as_str())
307    }
308}
309
310impl<'a> std::convert::From<ObjectPath<'a>> for OwnedObjectPath {
311    fn from(o: ObjectPath<'a>) -> Self {
312        OwnedObjectPath(o.into_owned())
313    }
314}
315
316impl TryFrom<&'_ str> for OwnedObjectPath {
317    type Error = Error;
318
319    fn try_from(value: &str) -> Result<Self> {
320        Ok(Self::from(ObjectPath::try_from(value)?))
321    }
322}
323
324impl TryFrom<String> for OwnedObjectPath {
325    type Error = Error;
326
327    fn try_from(value: String) -> Result<Self> {
328        Ok(Self::from(ObjectPath::try_from(value)?))
329    }
330}
331
332impl<'de> Deserialize<'de> for OwnedObjectPath {
333    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
334    where
335        D: Deserializer<'de>,
336    {
337        String::deserialize(deserializer)
338            .and_then(|s| ObjectPath::try_from(s).map_err(|e| de::Error::custom(e.to_string())))
339            .map(Self)
340    }
341}
342
343impl std::fmt::Display for OwnedObjectPath {
344    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
345        std::fmt::Display::fmt(&self.as_str(), f)
346    }
347}
348
349#[cfg(test)]
350mod unit {
351    use super::*;
352
353    #[test]
354    fn owned_from_reader() {
355        // See https://github.com/dbus2/zbus/issues/287
356        let json_str = "\"/some/path\"";
357        serde_json::de::from_reader::<_, OwnedObjectPath>(json_str.as_bytes()).unwrap();
358    }
359}