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