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}