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}