async_rustbus/rustbus_core/
path.rs

1//! Structs for validating, marshalling and unmarshalling DBus object paths.
2//!
3//! [`ObjectPath`] and [`ObjectPathBuf`] are based on [`Path`] and [`PathBuf`] from `std::path` respectively.
4//! Most of the same methods are implemented with minor semantic differences.
5//! These differences arise because all valid `ObjectPath`s are absolute.
6//! See each method for details.
7//! `ObjectPath` implements `Deref` for `Path`
8//! allowing it to be easily used in context requring a standard library path.
9//! Also because all `ObjectPath`s are valid Rust `str`s,
10//! there are easy methods to convert them to `&str`s.
11//!
12//! `ObjectPath`s are subsets of `str`s and `Path`s
13//! and can be created from them if they meet the rules detailed in the section below.
14//! These methods can also be used to simpily validate `str`s or `Path`s.
15//!
16//! # Restrictions on valid DBus object paths
17//! * All DBus object paths are absolute. They always start with a `/`.
18//! * Each element in the path are seperated by `/`.
19//!   These elements can contain the ASCII characters `[A-Z][a-z][0-9]_`.
20//! * There cannot be consecutive `/` seperators.
21//!   In otherwords, each element must be a non-zero length.
22//! * The last character cannot be a `/` seperator, unless the path is a root path (a single `/`).
23//!
24//! The relevant portion of the DBus Specification can be found [here].
25//!
26//! [here]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-marshaling-object-path
27use crate::rustbus_core;
28use rustbus_core::signature::{Base, Type};
29use rustbus_core::wire::marshal::traits::{Marshal, Signature, SignatureBuffer};
30use rustbus_core::wire::marshal::MarshalContext;
31use rustbus_core::wire::unmarshal::traits::Unmarshal;
32use rustbus_core::wire::unmarshal::Error as UnmarshalError;
33use rustbus_core::wire::unmarshal::{UnmarshalContext, UnmarshalResult};
34
35use std::borrow::{Borrow, ToOwned};
36use std::cmp::Ordering;
37use std::convert::TryFrom;
38use std::ffi::{OsStr, OsString};
39use std::fmt::{Display, Formatter};
40use std::hash::{Hash, Hasher};
41use std::ops::Deref;
42use std::os::unix::ffi::{OsStrExt, OsStringExt};
43use std::path::{Component, Path, PathBuf, StripPrefixError};
44use std::str::FromStr;
45
46/// Error type enumerating typical ways a standard path may be an invalid object path.
47#[derive(Debug)]
48pub enum InvalidObjectPath {
49    NoRoot,
50    ContainsInvalidCharacters,
51    ConsecutiveSlashes,
52    TrailingSlash,
53}
54
55/// A slice of a Dbus object path akin to a [`str`] or [`std::path::Path`].
56///
57/// Contains some methods for manipulating Dbus object paths,
58/// similiar to `std::path::Path` with some minor differences.
59#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
60pub struct ObjectPath {
61    inner: Path,
62}
63impl ObjectPath {
64    fn validate_skip_root(path_str: &str) -> Result<(), InvalidObjectPath> {
65        let mut last_was_sep = false;
66        for character in path_str.chars() {
67            match character {
68                'A'..='Z' | 'a'..='z' | '0'..='9' | '_' => {
69                    last_was_sep = false;
70                }
71                '/' => {
72                    if last_was_sep {
73                        return Err(InvalidObjectPath::ConsecutiveSlashes);
74                    } else {
75                        last_was_sep = true;
76                    }
77                }
78                _ => return Err(InvalidObjectPath::ContainsInvalidCharacters),
79            }
80        }
81        if path_str.len() != 1 && path_str.ends_with('/') {
82            return Err(InvalidObjectPath::TrailingSlash);
83        }
84        Ok(())
85    }
86    fn validate_str(path_str: &str) -> Result<(), InvalidObjectPath> {
87        if !path_str.starts_with('/') {
88            return Err(InvalidObjectPath::NoRoot);
89        }
90        Self::validate_skip_root(path_str)
91    }
92    fn validate<P: AsRef<Path>>(path: P) -> Result<(), InvalidObjectPath> {
93        let path = path.as_ref();
94        if !path.has_root() {
95            return Err(InvalidObjectPath::NoRoot);
96        }
97        let path_str = path
98            .to_str()
99            .ok_or(InvalidObjectPath::ContainsInvalidCharacters)?;
100        Self::validate_skip_root(path_str)
101    }
102    fn debug_assert_validitity(&self) {
103        #[cfg(debug_assertions)]
104        Self::validate(self).expect("Failed to validate the object path!");
105    }
106    /// Validate and make a `ObjectPath` from a normal path.
107    ///
108    /// See module [root] for the rules of a valid `ObjectPath`.
109    /// # Examples
110    /// ```
111    /// use async_rustbus::rustbus_core::path::ObjectPath;
112    /// let path = ObjectPath::from_str("/example/path").unwrap();
113    /// ObjectPath::from_str("invalid/because/not/absolute").unwrap_err();
114    /// ObjectPath::from_str("/invalid/because//double/sep").unwrap_err();
115    /// ```
116    /// [root]: ./index.html#restrictions-on-valid-dbus-object-paths
117    #[inline]
118    pub fn from_path<P: AsRef<Path> + ?Sized>(p: &P) -> Result<&ObjectPath, InvalidObjectPath> {
119        let path = p.as_ref();
120        let ret = unsafe {
121            Self::validate(path)?;
122            Self::new_no_val(path)
123        };
124        Ok(ret)
125    }
126    #[inline]
127    #[allow(clippy::should_implement_trait)]
128    pub fn from_str(s: &str) -> Result<&ObjectPath, InvalidObjectPath> {
129        ObjectPath::validate_str(s)?;
130        unsafe { Ok(ObjectPath::new_no_val(s.as_ref())) }
131    }
132    unsafe fn new_no_val(p: &Path) -> &ObjectPath {
133        &*(p as *const Path as *const ObjectPath)
134    }
135    /// Get the bytes that make up an `ObjectPath`.
136    #[inline]
137    pub fn as_bytes(&self) -> &[u8] {
138        self.inner.as_os_str().as_bytes()
139    }
140    /// Get the `ObjectPath` as a `&str`.
141    ///
142    /// Unlike ordinary `std::path::Path`, `ObjectPath`s are always valid Rust `str`s making this possible.
143    #[inline]
144    pub fn as_str(&self) -> &str {
145        self.debug_assert_validitity();
146
147        let bytes = self.as_bytes();
148        unsafe { std::str::from_utf8_unchecked(bytes) }
149    }
150    /// Strip the prefix of the `ObjectPath`.
151    ///
152    /// Unlike [`Path::strip_prefix`] this method will always leave the path will always remain absolute.
153    /// # Examples
154    /// ```
155    /// use async_rustbus::rustbus_core::path::ObjectPath;
156    /// let original  = ObjectPath::from_str("/example/path/to_strip").unwrap();
157    /// let target = ObjectPath::from_str("/path/to_strip").unwrap();
158    /// /* These two lines are equivelent because paths must always remain absolute,
159    ///    so the root '/' is readded in the second example.
160    ///    Note the second line is not a valid ObjectPath */
161    /// let stripped0 = original.strip_prefix("/example").unwrap();
162    /// let stripped1 = original.strip_prefix("/example/").unwrap();
163    /// assert_eq!(stripped0, target);
164    /// assert_eq!(stripped1, target);
165    ///
166    /// original.strip_prefix("/example/other").unwrap_err();
167    /// original.strip_prefix("/example/pa").unwrap_err();
168    ///
169    /// // Because the only thing stripped is the root sep this does nothing as it gets readded.
170    /// let stripped2 = original.strip_prefix("/").unwrap();
171    /// assert_eq!(stripped2, original);
172    /// let stripped3 = original.strip_prefix(original).unwrap();
173    /// assert_eq!(stripped3, ObjectPath::root_path());
174    /// ```
175    /// [`Path::strip_prefix`]: https://doc.rust-lang.org/std/path/struct.Path.html#method.strip_prefix
176    pub fn strip_prefix<P: AsRef<Path> + ?Sized>(
177        &self,
178        p: &P,
179    ) -> Result<&ObjectPath, StripPrefixError> {
180        let stripped = self.inner.strip_prefix(p.as_ref())?;
181        if stripped == Path::new("") {
182            Ok(unsafe { ObjectPath::new_no_val("/".as_ref()) })
183        } else {
184            // Get a stripped path that includes the leading seperator.
185            // This leading seperator is exists because ObjectPath.inner must be absolute;
186            let self_bytes = self.as_bytes();
187            let self_len = self_bytes.len(); // Unix-only
188            let stripped_len = stripped.as_os_str().len();
189            let ret_bytes = &self_bytes[self_len - 1 - stripped_len..];
190
191            // convert bytes to ObjectPath
192            let ret = OsStr::from_bytes(ret_bytes);
193            let ret = unsafe { ObjectPath::new_no_val(ret.as_ref()) };
194            ret.debug_assert_validitity();
195            Ok(ret)
196        }
197    }
198    /// Get the parent of the `ObjectPath` by removing the last element.
199    /// If the `ObjectPath` is a root path then `None` is returned.
200    pub fn parent(&self) -> Option<&ObjectPath> {
201        let pp = self.inner.parent()?;
202        let ret = unsafe { Self::new_no_val(pp) };
203        ret.debug_assert_validitity();
204        Some(ret)
205    }
206    /// Retrieves the last element of the `ObjectPath`.
207    /// If the `ObjectPath` is a root path then `None` is returned.
208    #[inline]
209    pub fn file_name(&self) -> Option<&str> {
210        self.debug_assert_validitity();
211        let bytes = self.inner.file_name()?.as_bytes();
212        unsafe { Some(std::str::from_utf8_unchecked(bytes)) }
213    }
214    /// Return a `ObjectPath` consisting of a single `/` seperator.
215    #[inline]
216    pub fn root_path() -> &'static Self {
217        unsafe { ObjectPath::new_no_val("/".as_ref()) }
218    }
219    /// Returns an `Iterator` over the elements of an `ObjectPath`.
220    pub fn components(&self) -> impl Iterator<Item = &str> {
221        self.debug_assert_validitity();
222        self.inner.components().skip(1).map(|c| match c {
223            Component::Normal(os) => unsafe { std::str::from_utf8_unchecked(os.as_bytes()) },
224            _ => unreachable!("All the components of a ObjectPath are normal!"),
225        })
226    }
227    pub fn to_object_path_buf(&self) -> ObjectPathBuf {
228        ObjectPathBuf::from(self)
229    }
230}
231impl Display for ObjectPath {
232    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
233        write!(f, "{}", self.as_str())
234    }
235}
236impl Display for ObjectPathBuf {
237    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
238        self.deref().fmt(f)
239    }
240}
241impl Marshal for &ObjectPath {
242    fn marshal(&self, ctx: &mut MarshalContext) -> Result<(), rustbus_core::Error> {
243        self.as_str().marshal(ctx)
244    }
245}
246impl Marshal for ObjectPathBuf {
247    fn marshal(&self, ctx: &mut MarshalContext) -> Result<(), rustbus_core::Error> {
248        self.deref().marshal(ctx)
249    }
250}
251
252impl Deref for ObjectPath {
253    type Target = std::path::Path;
254
255    #[inline]
256    fn deref(&self) -> &Self::Target {
257        &self.inner
258    }
259}
260impl ToOwned for ObjectPath {
261    type Owned = ObjectPathBuf;
262    #[inline]
263    fn to_owned(&self) -> Self::Owned {
264        self.to_object_path_buf()
265    }
266}
267
268impl Borrow<Path> for ObjectPath {
269    #[inline]
270    fn borrow(&self) -> &Path {
271        self.deref()
272    }
273}
274impl AsRef<Path> for ObjectPath {
275    #[inline]
276    fn as_ref(&self) -> &Path {
277        self.deref()
278    }
279}
280impl AsRef<ObjectPath> for ObjectPath {
281    #[inline]
282    fn as_ref(&self) -> &ObjectPath {
283        self
284    }
285}
286impl AsRef<str> for ObjectPath {
287    #[inline]
288    fn as_ref(&self) -> &str {
289        self.as_str()
290    }
291}
292impl AsRef<OsStr> for ObjectPath {
293    #[inline]
294    fn as_ref(&self) -> &OsStr {
295        self.inner.as_ref()
296    }
297}
298
299impl<'a> From<&'a ObjectPath> for &'a str {
300    #[inline]
301    fn from(path: &'a ObjectPath) -> Self {
302        path.as_str()
303    }
304}
305impl<'a> TryFrom<&'a str> for &'a ObjectPath {
306    type Error = InvalidObjectPath;
307    #[inline]
308    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
309        ObjectPath::from_str(s)
310    }
311}
312impl<'a> TryFrom<&'a Path> for &'a ObjectPath {
313    type Error = InvalidObjectPath;
314    #[inline]
315    fn try_from(p: &'a Path) -> Result<Self, Self::Error> {
316        ObjectPath::from_path(p)
317    }
318}
319
320impl Signature for &ObjectPath {
321    #[inline]
322    fn signature() -> Type {
323        Type::Base(Base::ObjectPath)
324    }
325    #[inline]
326    fn alignment() -> usize {
327        Self::signature().get_alignment()
328    }
329    #[inline]
330    fn sig_str(s_buf: &mut SignatureBuffer) {
331        s_buf.push_static("o");
332    }
333}
334
335impl<'buf, 'fds> Unmarshal<'buf, 'fds> for &'buf ObjectPath {
336    fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> UnmarshalResult<Self> {
337        let (bytes, val) = <&str>::unmarshal(ctx)?;
338        let path = ObjectPath::from_str(val).map_err(|_| UnmarshalError::InvalidType)?;
339        Ok((bytes, path))
340    }
341}
342impl Signature for ObjectPathBuf {
343    #[inline]
344    fn signature() -> Type {
345        <&ObjectPath>::signature()
346    }
347    #[inline]
348    fn alignment() -> usize {
349        <&ObjectPath>::alignment()
350    }
351    #[inline]
352    fn sig_str(s_buf: &mut SignatureBuffer) {
353        <&ObjectPath>::sig_str(s_buf);
354    }
355}
356impl<'buf, 'fds> Unmarshal<'buf, 'fds> for ObjectPathBuf {
357    fn unmarshal(ctx: &mut UnmarshalContext<'fds, 'buf>) -> UnmarshalResult<Self> {
358        <&ObjectPath>::unmarshal(ctx).map(|(size, op)| (size, op.to_owned()))
359    }
360}
361
362#[derive(Eq, Clone, Debug, Default)]
363pub struct ObjectPathBuf {
364    inner: Option<PathBuf>,
365}
366impl Hash for ObjectPathBuf {
367    fn hash<H: Hasher>(&self, state: &mut H) {
368        self.deref().hash(state);
369    }
370}
371impl PartialEq<ObjectPathBuf> for ObjectPathBuf {
372    fn eq(&self, other: &ObjectPathBuf) -> bool {
373        self.deref().eq(other.deref())
374    }
375}
376impl PartialOrd<ObjectPathBuf> for ObjectPathBuf {
377    fn partial_cmp(&self, other: &ObjectPathBuf) -> Option<Ordering> {
378        self.deref().partial_cmp(other)
379    }
380}
381impl Ord for ObjectPathBuf {
382    fn cmp(&self, other: &Self) -> Ordering {
383        self.deref().cmp(other)
384    }
385}
386/// An owned, mutable Dbus object path akin to [`String`] or [`std::path::PathBuf`].
387///
388/// [`push`], [`pop`] and others can be used used to modify the `ObjectPathBuf` in-place.
389/// `ObjectPathBuf` implements `Deref` to `ObjectPath`
390/// allowing for all methods on `ObjectPath` to be used on `ObjectPathBuf`.
391/// # Notes
392/// * `ObjectPathBuf` is stored as a wrapper around `Option<PathBuf>`, where the `None` case
393///   is equivelent to a root path.
394///   
395/// * As a result of the above point,
396///   root paths (a single `/` seperator) are special case that does not result in a heap allocation.
397///   This means that [`new`] does not result in an allocation on its own.
398///
399/// [`push`]: ./struct.ObjectPathBuf.html#method.push
400/// [`pop`]: ./struct.ObjectPathBuf.html#method.pop
401/// [`new`]: ./struct.ObjectPathBuf.html#method.new
402impl ObjectPathBuf {
403    /// Create a new root path consisting of a single `/` seperator.
404    ///
405    /// The `ObjectPathBuf` returned by this method does not result in an allocation until it is modified.
406    #[inline]
407    pub fn new() -> ObjectPathBuf {
408        ObjectPathBuf { inner: None }
409    }
410    /// Create a new root path and preallocate space on the heap for additions to the path.
411    ///
412    /// If the size of the object path is known ahead time,
413    /// this method can provide a performance benefit by avoid multiple reallocations.
414    ///
415    /// When `capacity` is zero this method is equivelent to `new`.
416    pub fn with_capacity(capacity: usize) -> ObjectPathBuf {
417        let inner = if capacity == 0 {
418            None
419        } else {
420            let mut pb = PathBuf::with_capacity(capacity);
421            pb.push("/");
422            Some(pb)
423        };
424        ObjectPathBuf { inner }
425    }
426    /// Coerces to a [`ObjectPath`] slice.
427    #[inline]
428    pub fn as_object_path(&self) -> &ObjectPath {
429        self.deref()
430    }
431    unsafe fn from_path_buf(pb: PathBuf) -> Self {
432        Self { inner: Some(pb) }
433    }
434    /// Truncates the object path into a root path.
435    ///
436    /// This does not affect the capacity of the `ObjectPathBuf`.
437    #[inline]
438    pub fn clear(&mut self) {
439        if let Some(buf) = &mut self.inner {
440            buf.clear();
441        }
442    }
443    #[inline]
444    /// Append an `ObjectPath` to this one.
445    pub fn push(&mut self, path: &ObjectPath) {
446        let path = Path::strip_prefix(path, "/").expect("All object paths start with '/'");
447        unsafe {
448            self.push_path_unchecked(path);
449        }
450    }
451    unsafe fn push_path_unchecked(&mut self, path: &Path) {
452        let len = path.as_os_str().len();
453        if len == 0 {
454            return;
455        }
456        self.reserve(len + 1);
457        let inner = self
458            .inner
459            .as_mut()
460            .expect("The reserve call cause a PathBuf to allows be allocated.");
461        inner.push(path);
462        self.debug_assert_validitity();
463    }
464    /// Append a `Path` to this one.
465    ///
466    /// If `path` is invalid this method panics.
467    /// If it is unknown if `path` is valid use [`push_path_checked`] instead.
468    ///
469    /// # Panics
470    /// `path` must be a valid object path with two exceptions:
471    /// `path` can be relative or empty.
472    /// If the above conditions are not met this function will panic.
473    ///
474    /// # Examples
475    /// ```
476    /// use std::convert::TryFrom;
477    /// use async_rustbus::rustbus_core::path::{ObjectPath, ObjectPathBuf};
478    /// let target = ObjectPath::from_str("/example/path/to_append").unwrap();
479    ///
480    /// let mut opb0  = ObjectPathBuf::try_from("/example").unwrap();
481    /// let mut opb1 = opb0.clone();
482    /// opb0.push_path("/path/to_append");
483    /// opb1.push_path("path/to_append");
484    /// assert_eq!(&opb0, target);
485    /// assert_eq!(&opb1, target);
486    /// ```
487    /// These should panic for different reasons.
488    /// ```should_panic
489    /// use std::convert::TryFrom;
490    /// use async_rustbus::rustbus_core::path::{ObjectPath, ObjectPathBuf};
491    /// let target = ObjectPath::from_str("/example/path/to_append").unwrap();
492    /// let mut original = ObjectPathBuf::try_from("/example").unwrap();
493    ///
494    /// // Each line panics for different reasons
495    /// original.push_path("/path//consecutive/slash");
496    /// original.push_path("/p@th");
497    /// ```
498    #[inline]
499    pub fn push_path<P: AsRef<Path>>(&mut self, path: P) {
500        self.push_path_checked(path)
501            .expect("Invalid path was passed!");
502    }
503    /// Check and append `path` to the `ObjectPathBuf` if it is valid.
504    ///
505    /// `path` must be a valid DBus object path with two exceptions:
506    /// `path` can be relative or empty.
507    /// If these conditions are not met then an `Err` is returned.
508    /// # Examples
509    /// ```
510    /// use std::convert::TryFrom;
511    /// use async_rustbus::rustbus_core::path::{ObjectPath, ObjectPathBuf};
512    /// let target = ObjectPath::from_str("/example/path/to_append").unwrap();
513    ///
514    /// let mut opb0  = ObjectPathBuf::try_from("/example").unwrap();
515    /// let mut opb1 = opb0.clone();
516    /// opb0.push_path_checked("/path/to_append").unwrap();
517    /// opb1.push_path_checked("path/to_append").unwrap();
518    /// assert_eq!(&opb0, target);
519    /// assert_eq!(&opb1, target);
520    ///
521    /// opb0.push_path_checked("/path//consecutive/slash").unwrap_err();
522    /// opb1.push_path_checked("/p@th").unwrap_err();
523    /// ```
524    pub fn push_path_checked<P: AsRef<Path>>(&mut self, path: P) -> Result<(), InvalidObjectPath> {
525        let path = path.as_ref();
526        let path = path.strip_prefix("/").unwrap_or(path);
527        let path_str = path
528            .to_str()
529            .ok_or(InvalidObjectPath::ContainsInvalidCharacters)?;
530        ObjectPath::validate_skip_root(path_str)?;
531        unsafe {
532            self.push_path_unchecked(path);
533        };
534        Ok(())
535    }
536    /// Truncate the `ObjectPathBuf` to [`parent`].
537    /// Returns true if the path changed.
538    ///
539    /// [`parent`]: ./struct.ObjectPath.html#method.parent
540    #[inline]
541    pub fn pop(&mut self) -> bool {
542        self.inner.as_mut().map_or(false, PathBuf::pop)
543    }
544    pub fn reserve(&mut self, additional: usize) {
545        if additional == 0 {
546            return;
547        }
548        match &mut self.inner {
549            Some(buf) => buf.reserve(additional),
550            None => *self = Self::with_capacity(additional + 1),
551        }
552    }
553    pub fn reserve_exact(&mut self, additional: usize) {
554        if additional == 0 {
555            return;
556        }
557        match &mut self.inner {
558            Some(buf) => buf.reserve_exact(additional),
559            None => {
560                let mut buf = PathBuf::new();
561                buf.reserve_exact(additional + 1);
562                self.inner = Some(buf);
563            }
564        }
565    }
566    /// Get the capacity of the current heap allocation.
567    ///
568    /// If capacity is zero then there is no heap allocation, and the `ObjectPathBuf` is a root path.
569    /// The root path is special case that can be stored without a heap allocation despite being length 1.
570    #[inline]
571    pub fn capacity(&self) -> usize {
572        self.inner.as_ref().map_or(0, PathBuf::capacity)
573    }
574}
575impl TryFrom<OsString> for ObjectPathBuf {
576    type Error = InvalidObjectPath;
577    fn try_from(value: OsString) -> Result<Self, Self::Error> {
578        ObjectPath::validate(&value)?;
579        Ok(unsafe { ObjectPathBuf::from_path_buf(value.into()) })
580    }
581}
582impl TryFrom<String> for ObjectPathBuf {
583    type Error = InvalidObjectPath;
584    #[inline]
585    fn try_from(value: String) -> Result<Self, Self::Error> {
586        Self::try_from(OsString::from(value))
587    }
588}
589impl TryFrom<PathBuf> for ObjectPathBuf {
590    type Error = InvalidObjectPath;
591    #[inline]
592    fn try_from(value: PathBuf) -> Result<Self, Self::Error> {
593        ObjectPath::validate(&value)?;
594        Ok(unsafe { ObjectPathBuf::from_path_buf(value) })
595    }
596}
597impl TryFrom<&str> for ObjectPathBuf {
598    type Error = InvalidObjectPath;
599    #[inline]
600    fn try_from(value: &str) -> Result<Self, Self::Error> {
601        ObjectPathBuf::from_str(value)
602    }
603}
604impl TryFrom<&OsStr> for ObjectPathBuf {
605    type Error = InvalidObjectPath;
606    #[inline]
607    fn try_from(value: &OsStr) -> Result<Self, Self::Error> {
608        ObjectPath::from_path(value).map(ToOwned::to_owned)
609    }
610}
611impl TryFrom<&Path> for ObjectPathBuf {
612    type Error = InvalidObjectPath;
613    #[inline]
614    fn try_from(value: &Path) -> Result<Self, Self::Error> {
615        ObjectPath::from_path(value).map(ToOwned::to_owned)
616    }
617}
618
619impl Deref for ObjectPathBuf {
620    type Target = ObjectPath;
621    fn deref(&self) -> &Self::Target {
622        match &self.inner {
623            Some(buf) => unsafe { ObjectPath::new_no_val(buf) },
624            None => ObjectPath::root_path(),
625        }
626    }
627}
628impl Borrow<ObjectPath> for ObjectPathBuf {
629    #[inline]
630    fn borrow(&self) -> &ObjectPath {
631        self.deref()
632    }
633}
634impl AsRef<ObjectPath> for ObjectPathBuf {
635    #[inline]
636    fn as_ref(&self) -> &ObjectPath {
637        self.deref()
638    }
639}
640impl AsRef<str> for ObjectPathBuf {
641    #[inline]
642    fn as_ref(&self) -> &str {
643        self.deref().as_ref()
644    }
645}
646impl AsRef<Path> for ObjectPathBuf {
647    #[inline]
648    fn as_ref(&self) -> &Path {
649        self.deref().as_ref()
650    }
651}
652impl FromStr for ObjectPathBuf {
653    type Err = InvalidObjectPath;
654    #[inline]
655    fn from_str(s: &str) -> Result<Self, Self::Err> {
656        ObjectPath::from_str(s).map(ToOwned::to_owned)
657    }
658}
659impl From<ObjectPathBuf> for PathBuf {
660    #[inline]
661    fn from(buf: ObjectPathBuf) -> Self {
662        match buf.inner {
663            Some(buf) => buf,
664            None => PathBuf::from("/"),
665        }
666    }
667}
668impl From<ObjectPathBuf> for String {
669    fn from(path: ObjectPathBuf) -> Self {
670        path.debug_assert_validitity();
671        let bytes = match path.inner {
672            Some(buf) => buf.into_os_string().into_vec(),
673            None => Vec::from(&b"/"[..]),
674        };
675
676        unsafe { std::string::String::from_utf8_unchecked(bytes) }
677    }
678}
679impl From<&ObjectPath> for ObjectPathBuf {
680    fn from(path: &ObjectPath) -> Self {
681        let ret = if path == ObjectPath::root_path() {
682            ObjectPathBuf::new()
683        } else {
684            unsafe { ObjectPathBuf::from_path_buf(path.into()) }
685        };
686        ret.debug_assert_validitity();
687        ret
688    }
689}
690
691impl PartialEq<ObjectPath> for ObjectPathBuf {
692    #[inline]
693    fn eq(&self, other: &ObjectPath) -> bool {
694        self.deref().eq(other)
695    }
696}
697#[cfg(test)]
698mod tests {
699    use super::{ObjectPath, ObjectPathBuf};
700    use std::borrow::Borrow;
701    use std::collections::hash_map::DefaultHasher;
702    use std::hash::{Hash, Hasher};
703    use std::path::Path;
704
705    fn test_objpaths() -> Vec<&'static ObjectPath> {
706        vec![
707            ObjectPath::from_str("/org/freedesktop/NetworkManager").unwrap(),
708            ObjectPath::from_str("/org/freedesktop/NetworkManager/ActiveConnection").unwrap(),
709        ]
710    }
711    fn test_objpathbufs() -> Vec<ObjectPathBuf> {
712        test_objpaths()
713            .into_iter()
714            .map(|op| op.to_owned())
715            .collect()
716    }
717    // Borrow requires that the Ord trait prodces equivelent values before and after
718    fn compare_ord_borrow<A, T, U>(pre: &[A]) -> Option<(usize, usize)>
719    where
720        T: Ord + Borrow<U> + ?Sized,
721        U: Ord + ?Sized,
722        A: Borrow<T>,
723    {
724        let pre_iter = pre.iter().map(|p| p.borrow());
725        for (i, pre_i) in pre_iter.clone().enumerate() {
726            for (j, pre_j) in pre_iter.clone().enumerate().skip(i) {
727                let pre_ord = pre_i.cmp(pre_j);
728                let post_i = pre_i.borrow();
729                let post_j = pre_j.borrow();
730                let post_ord = post_i.cmp(post_j);
731                if pre_ord != post_ord {
732                    return Some((i, j));
733                }
734            }
735        }
736        None
737    }
738    // Borrow requires that Hash trait produces equivelent values for before and after borrow()
739    // This tests that invariant
740    fn compare_hasher_borrow<A, T, U>(pre: &[A]) -> Option<usize>
741    where
742        T: Hash + Borrow<U> + ?Sized,
743        U: Hash + ?Sized,
744        A: Borrow<T>,
745    {
746        let pre_iter = pre.iter().map(|p| p.borrow());
747        for (i, (pre, post)) in pre_iter
748            .clone()
749            .zip(pre_iter.map(|p| p.borrow()))
750            .enumerate()
751        {
752            let mut pre_borrow_hasher = DefaultHasher::new();
753            let mut post_borrow_hasher = DefaultHasher::new();
754            pre.hash(&mut pre_borrow_hasher);
755            post.hash(&mut post_borrow_hasher);
756            if pre_borrow_hasher.finish() != post_borrow_hasher.finish() {
757                return Some(i);
758            }
759        }
760        None
761    }
762    #[test]
763    fn test_objectpathbuf_borrow_objectpath() {
764        let objpathbufs = test_objpathbufs();
765        if let Some(i) =
766            compare_hasher_borrow::<ObjectPathBuf, ObjectPathBuf, ObjectPath>(&objpathbufs[..])
767        {
768            panic!("Hash didn't match: {}", i);
769        }
770        if let Some((i, j)) =
771            compare_ord_borrow::<ObjectPathBuf, ObjectPathBuf, ObjectPath>(&objpathbufs[..])
772        {
773            panic!("Ord didn't match for: {} {}", i, j);
774        }
775    }
776    #[test]
777    fn test_objectpath_borrow_path() {
778        let objpaths = test_objpaths();
779        if let Some(i) = compare_hasher_borrow::<&ObjectPath, ObjectPath, Path>(&objpaths[..]) {
780            panic!("Hash didn't match: {}", i);
781        }
782        if let Some((i, j)) = compare_ord_borrow::<&ObjectPath, ObjectPath, Path>(&objpaths[..]) {
783            panic!("Ord didn't match for: {} {}", i, j);
784        }
785    }
786    #[test]
787    fn test_push() {
788        let objpath = ObjectPath::from_str("/dbus/test").unwrap();
789        let objpath2 = ObjectPath::from_str("/freedesktop/more").unwrap();
790        let mut objpathbuf = ObjectPathBuf::new();
791        objpathbuf.push(objpath);
792        assert_eq!(objpathbuf, *objpath);
793        objpathbuf.push(objpath2);
794        assert_eq!(
795            &objpathbuf,
796            ObjectPath::from_str("/dbus/test/freedesktop/more").unwrap()
797        );
798        assert!(objpathbuf.starts_with(objpath));
799        assert!(!objpathbuf.starts_with(objpath2));
800        assert_eq!(objpathbuf.strip_prefix(objpath).unwrap(), objpath2);
801    }
802}