Skip to main content

tokio_dbus/object_path/
object_path.rs

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