tokio_dbus/object_path/
object_path.rs

1use std::fmt;
2use std::str::from_utf8_unchecked;
3
4use crate::buf::UnalignedBuf;
5use crate::{Body, BodyBuf};
6use crate::{ObjectPathBuf, Read, Result, Signature, Write};
7
8use super::{validate, Iter, ObjectPathError};
9
10/// A validated object path.
11///
12/// The following rules define a [valid object path]. Implementations must not
13/// send or accept messages with invalid object paths.
14///
15/// [valid object path]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-marshaling-object-path
16///
17/// * The path may be of any length.
18/// * The path must begin with an ASCII '/' (integer 47) character, and must
19///   consist of elements separated by slash characters.
20/// * Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_"
21/// * No element may be the empty string.
22/// * Multiple '/' characters cannot occur in sequence.
23/// * A trailing '/' character is not allowed unless the path is the root path
24///   (a single '/' character).
25#[derive(PartialEq, Eq)]
26#[repr(transparent)]
27pub struct ObjectPath([u8]);
28
29impl ObjectPath {
30    /// The special `"/"` object path.
31    ///
32    /// # Examples
33    ///
34    /// ```
35    /// use tokio_dbus::ObjectPath;
36    ///
37    /// assert_eq!(ObjectPath::ROOT, ObjectPath::new(b"/")?);
38    /// # Ok::<_, tokio_dbus::Error>(())
39    /// ```
40    pub const ROOT: &'static Self = Self::new_const(b"/");
41
42    /// Construct a new object path.
43    ///
44    /// # Panics
45    ///
46    /// Panics if the argument is not a valid object.
47    ///
48    /// See [`ObjectPath`] for more information.
49    #[track_caller]
50    pub const fn new_const(path: &[u8]) -> &Self {
51        if !validate(path) {
52            panic!("Invalid D-Bus object path");
53        }
54
55        // SAFETY: The byte slice is repr transparent over this type.
56        unsafe { Self::new_unchecked(path) }
57    }
58
59    /// Construct a new validated object path.
60    ///
61    /// # Errors
62    ///
63    /// Errors if the argument is not a valid object.
64    ///
65    /// See [`ObjectPath`] for more information.
66    pub fn new<P>(path: &P) -> Result<&Self, ObjectPathError>
67    where
68        P: ?Sized + AsRef<[u8]>,
69    {
70        let path = path.as_ref();
71
72        if !validate(path) {
73            return Err(ObjectPathError);
74        }
75
76        // SAFETY: The byte slice is repr transparent over this type.
77        unsafe { Ok(Self::new_unchecked(path)) }
78    }
79
80    /// Construct an iterator over the object path.
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// use tokio_dbus::ObjectPath;
86    ///
87    /// let mut it = ObjectPath::new_const(b"/").iter();
88    /// assert!(it.next().is_none());
89    ///
90    /// let mut it = ObjectPath::new_const(b"/foo").iter();
91    /// assert_eq!(it.next(), Some("foo"));
92    /// assert!(it.next().is_none());
93    ///
94    /// let mut it = ObjectPath::new_const(b"/foo/bar").iter();
95    /// assert_eq!(it.next_back(), Some("bar"));
96    /// assert_eq!(it.next(), Some("foo"));
97    /// assert!(it.next().is_none());
98    /// ```
99    pub fn iter(&self) -> Iter<'_> {
100        Iter::new(&self.0)
101    }
102
103    /// Test if one part starts with another.
104    ///
105    /// # Examples
106    ///
107    /// ```
108    /// use tokio_dbus::ObjectPath;
109    ///
110    /// const FOO: &ObjectPath = ObjectPath::new_const(b"/foo");
111    /// const FOO_BAR: &ObjectPath = ObjectPath::new_const(b"/foo/bar");
112    ///
113    /// assert!(FOO_BAR.starts_with(FOO));
114    /// ```
115    #[must_use]
116    pub fn starts_with(&self, other: &ObjectPath) -> bool {
117        self.0.starts_with(&other.0)
118    }
119
120    /// Construct a new unchecked object path.
121    ///
122    /// # Safety
123    ///
124    /// The caller must ensure that the path is a valid object path.
125    #[must_use]
126    pub(super) const unsafe fn new_unchecked(path: &[u8]) -> &Self {
127        &*(path as *const _ as *const Self)
128    }
129
130    /// Get the object path as a string.
131    pub(crate) fn as_str(&self) -> &str {
132        // SAFETY: Validation indirectly ensures that the signature is valid
133        // UTF-8.
134        unsafe { from_utf8_unchecked(&self.0) }
135    }
136}
137
138impl fmt::Display for ObjectPath {
139    #[inline]
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        self.as_str().fmt(f)
142    }
143}
144
145impl fmt::Debug for ObjectPath {
146    #[inline]
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        self.as_str().fmt(f)
149    }
150}
151
152impl AsRef<ObjectPath> for ObjectPath {
153    #[inline]
154    fn as_ref(&self) -> &ObjectPath {
155        self
156    }
157}
158
159impl AsRef<[u8]> for ObjectPath {
160    #[inline]
161    fn as_ref(&self) -> &[u8] {
162        &self.0
163    }
164}
165
166impl ToOwned for ObjectPath {
167    type Owned = ObjectPathBuf;
168
169    #[inline]
170    fn to_owned(&self) -> Self::Owned {
171        // SAFETY: Type ensures that it contains a valid object path during
172        // construction.
173        unsafe { ObjectPathBuf::from_raw_vec(self.0.to_vec()) }
174    }
175}
176
177impl From<&ObjectPath> for Box<ObjectPath> {
178    #[inline]
179    fn from(object_path: &ObjectPath) -> Self {
180        // SAFETY: ObjectPath is repr(transparent) over [u8].
181        unsafe {
182            Box::from_raw(Box::into_raw(Box::<[u8]>::from(&object_path.0)) as *mut ObjectPath)
183        }
184    }
185}
186
187impl Clone for Box<ObjectPath> {
188    #[inline]
189    fn clone(&self) -> Self {
190        Box::<ObjectPath>::from(&**self)
191    }
192}
193
194/// The [`IntoIterator`] implementation for [`ObjectPath`].
195///
196/// # Examples
197///
198/// ```
199/// use tokio_dbus::ObjectPath;
200///
201/// const PATH: &ObjectPath = ObjectPath::new_const(b"/foo/bar");
202///
203/// let mut values = Vec::new();
204///
205/// for s in PATH {
206///     values.push(s);
207/// }
208///
209/// assert_eq!(values, ["foo", "bar"]);
210/// ```
211impl<'a> IntoIterator for &'a ObjectPath {
212    type Item = &'a str;
213    type IntoIter = Iter<'a>;
214
215    #[inline]
216    fn into_iter(self) -> Self::IntoIter {
217        self.iter()
218    }
219}
220
221impl crate::write::sealed::Sealed for ObjectPath {}
222
223impl Write for ObjectPath {
224    const SIGNATURE: &'static Signature = Signature::OBJECT_PATH;
225
226    #[inline]
227    fn write_to(&self, buf: &mut BodyBuf) {
228        buf.store_frame(self.0.len() as u32);
229        buf.extend_from_slice_nul(&self.0);
230    }
231
232    #[inline]
233    fn write_to_unaligned(&self, buf: &mut UnalignedBuf) {
234        buf.store(self.0.len() as u32);
235        buf.extend_from_slice_nul(&self.0);
236    }
237}
238
239impl_traits_for_write!(
240    ObjectPath,
241    ObjectPath::new("/se/tedro/DBusExample")?,
242    "qo",
243    ObjectPath
244);
245
246impl crate::read::sealed::Sealed for ObjectPath {}
247
248impl Read for ObjectPath {
249    #[inline]
250    fn read_from<'de>(buf: &mut Body<'de>) -> Result<&'de Self> {
251        let len = buf.load::<u32>()? as usize;
252        let bytes = buf.load_slice_nul(len)?;
253        Ok(ObjectPath::new(bytes)?)
254    }
255}