dbus/
strings.rs

1//! This module contains strings with a specific format, such as a valid
2//! Interface name, a valid Error name, etc.
3//!
4//! (The internal representation of these strings are `Cow<str>`, but with a \0 byte
5//! at the end to use it libdbus calls without extra allocations. This is usually nothing
6//! you have to worry about.)
7
8use std::{str, fmt, ops, default, hash};
9use std::ffi::{CStr, CString};
10use std::borrow::{Borrow, Cow};
11use std::os::raw::c_char;
12
13#[cfg(not(feature = "no-string-validation"))]
14use crate::Error;
15#[cfg(not(feature = "no-string-validation"))]
16use crate::ffi;
17
18macro_rules! cstring_wrapper {
19    ($t: ident, $s: ident) => {
20
21impl<'m> $t<'m> {
22    #[cfg(feature = "no-string-validation")]
23    fn check_valid(_: *const c_char) -> Result<(), String> { Ok(()) }
24
25    #[cfg(not(feature = "no-string-validation"))]
26    fn check_valid(c: *const c_char) -> Result<(), String> {
27        let mut e = Error::empty();
28        let b = unsafe { ffi::$s(c, e.get_mut()) };
29        if b != 0 { Ok(()) } else { Err(e.message().unwrap().into()) }
30    }
31
32    /// Creates a new instance of this struct.
33    ///
34    /// Note: If the no-string-validation feature is activated, this string
35    /// will not be checked for conformance with the D-Bus specification.
36    pub fn new<S: Into<String>>(s: S) -> Result<$t<'m>, String> {
37        let mut s = s.into();
38        s.push_str("\0");
39        unsafe { $t::check_valid(CStr::from_bytes_with_nul_unchecked(s.as_bytes()).as_ptr() as *const c_char)?; }
40        Ok(Self(Cow::Owned(s)))
41    }
42
43    /// Creates a new instance of this struct. If you end it with \0,
44    /// it can borrow the slice without extra allocation.
45    ///
46    /// Note: If the no-string-validation feature is activated, this string
47    /// will not be checked for conformance with the D-Bus specification.
48    pub fn from_slice(s: &'m str) -> Result<$t<'m>, String> {
49        let ss = s.as_bytes();
50        if ss.len() == 0 || ss[ss.len()-1] != 0 { return $t::new(s) };
51        $t::check_valid(s.as_ptr() as *const c_char).map(|_| {
52            unsafe { Self::from_slice_unchecked(s) }
53        })
54    }
55
56    /// This function creates a new instance of this struct, without checking.
57    /// It's up to you to guarantee that s ends with a \0 and is valid.
58    pub const unsafe fn from_slice_unchecked(s: &'m str) -> $t<'m> {
59        let ss = s.as_bytes();
60        debug_assert!(ss[ss.len()-1] == 0);
61        $t(Cow::Borrowed(s))
62    }
63
64    /// View this struct as a CStr.
65    ///
66    /// Note: As of dbus 0.9, this is made private to be able to make it easier for a potential
67    /// native implementation using "str" instead of "cstr".
68    pub (crate) fn as_cstr(&self) -> &CStr {
69        unsafe {
70            CStr::from_bytes_with_nul_unchecked(self.0.as_bytes())
71        }
72    }
73
74    #[allow(dead_code)]
75    pub (crate) fn as_ptr(&self) -> *const c_char { self.as_cstr().as_ptr() }
76
77    /// Makes sure this string does not contain borrows.
78    pub fn into_static(self) -> $t<'static> {
79        $t(Cow::Owned(self.0.into_owned()))
80    }
81
82    /// Converts this struct to a CString.
83    pub fn into_cstring(self) -> CString {
84        // Change this when https://github.com/rust-lang/rust/issues/73179 is on stable.
85        let mut x: Vec<u8> = self.0.into_owned().into();
86        x.pop();
87        CString::new(x).unwrap()
88    }
89}
90
91/*
92/// #Panics
93///
94/// If given string is not valid.
95/// impl<S: Into<Vec<u8>>> From<S> for $t { fn from(s: S) -> $t { $t::new(s).unwrap() } }
96*/
97
98/// #Panics
99///
100/// If given string is not valid.
101impl<'m> From<String> for $t<'m> { fn from(s: String) -> $t<'m> { $t::new(s).unwrap() } }
102
103/// #Panics
104///
105/// If given string is not valid.
106impl<'m> From<&'m String> for $t<'m> { fn from(s: &'m String) -> $t<'m> { $t::from_slice(s).unwrap() } }
107
108/// #Panics
109///
110/// If given string is not valid.
111impl<'m> From<&'m str> for $t<'m> { fn from(s: &'m str) -> $t<'m> { $t::from_slice(s).unwrap() } }
112
113/// #Panics
114///
115/// If given string is not valid.
116impl<'m> From<&'m CStr> for $t<'m> {
117    fn from(s: &'m CStr) -> $t<'m> {
118        let x = str::from_utf8(s.to_bytes_with_nul()).unwrap();
119        $t::from_slice(x).unwrap()
120    }
121}
122
123impl<'m> From<$t<'m>> for CString { fn from(s: $t<'m>) -> CString { s.into_cstring() } }
124
125
126/// #Panics
127///
128/// If given string is not valid.
129impl<'m> From<Cow<'m, str>> for $t<'m> {
130    fn from(s: Cow<'m, str>) -> $t<'m> {
131        match s {
132            Cow::Borrowed(z) => z.into(),
133            Cow::Owned(z) => z.into(),
134        }
135    }
136}
137
138impl<'inner, 'm: 'inner> From<&'m $t<'inner>> for $t<'m> {
139    fn from(borrow: &'m $t<'inner>) -> $t<'m> {
140        $t(Cow::Borrowed(borrow.0.borrow()))
141    }
142}
143
144impl<'m> ops::Deref for $t<'m> {
145    type Target = str;
146    fn deref(&self) -> &str { self.0.split_at(self.0.len()-1).0 }
147}
148
149impl<'m> fmt::Display for $t<'m> {
150    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151        <str as fmt::Display>::fmt(&*self, f)
152    }
153}
154
155/*
156As of dbus 0.9, this has been removed to prepare for a potential native implementation.
157impl<'m> AsRef<CStr> for $t<'m> {
158    fn as_ref(&self) -> &CStr { &self.0 }
159}
160*/
161
162impl<'m> hash::Hash for $t<'m> {
163    fn hash<H: hash::Hasher>(&self, state: &mut H) {
164        self.0.hash(state);
165    }
166}
167
168impl PartialEq<str> for $t<'_> {
169    fn eq(&self, other: &str) -> bool {
170        &**self == other
171    }
172}
173
174impl PartialEq<&'_ str> for $t<'_> {
175    fn eq(&self, other: &&str) -> bool {
176        &**self == *other
177    }
178}
179
180}}
181
182/// A wrapper around a string that is guaranteed to be
183/// a valid (single) D-Bus type signature.
184#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
185pub struct Signature<'a>(Cow<'a, str>);
186
187cstring_wrapper!(Signature, dbus_signature_validate_single);
188
189impl Signature<'static> {
190    /// Makes a D-Bus signature that corresponds to A.
191    pub fn make<A: super::arg::Arg>() -> Signature<'static> { A::signature() }
192}
193
194/// A wrapper around a string that is guaranteed to be
195/// a valid D-Bus object path.
196#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
197pub struct Path<'a>(Cow<'a, str>);
198
199cstring_wrapper!(Path, dbus_validate_path);
200
201// This is needed so one can make arrays of paths easily
202impl<'a> default::Default for Path<'a> {
203    fn default() -> Path<'a> { Path(Cow::Borrowed("/\0")) }
204}
205
206/// A wrapper around a string that is guaranteed to be
207/// a valid D-Bus member, i e, a signal or method name.
208#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
209pub struct Member<'a>(Cow<'a, str>);
210
211cstring_wrapper!(Member, dbus_validate_member);
212
213/// A wrapper around a string that is guaranteed to be
214/// a valid D-Bus interface name.
215#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
216pub struct Interface<'a>(Cow<'a, str>);
217
218cstring_wrapper!(Interface, dbus_validate_interface);
219
220/// A wrapper around a string that is guaranteed to be
221/// a valid D-Bus bus name.
222#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
223pub struct BusName<'a>(Cow<'a, str>);
224
225cstring_wrapper!(BusName, dbus_validate_bus_name);
226
227/// A wrapper around a string that is guaranteed to be
228/// a valid D-Bus error name.
229#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
230pub struct ErrorName<'a>(Cow<'a, str>);
231
232cstring_wrapper!(ErrorName, dbus_validate_error_name);
233
234#[test]
235fn some_path() {
236    let p1: Path = "/valid".into();
237    let p2 = Path::new("##invalid##");
238    assert_eq!(p1, Path(Cow::Borrowed("/valid\0")));
239    #[cfg(not(feature = "no-string-validation"))]
240    assert_eq!(p2, Err("Object path was not valid: '##invalid##'".into()));
241    #[cfg(feature = "no-string-validation")]
242    assert_eq!(p2, Ok(Path(Cow::Borrowed("##invalid##\0"))));
243}
244
245#[test]
246fn reborrow_path() {
247    let p1 = Path::from("/valid");
248    let p2 = p1.clone();
249    {
250        let p2_borrow: &Path = &p2;
251        let p3 = Path::from(p2_borrow);
252        // Check path created from borrow
253        assert_eq!(p2, p3);
254    }
255    // Check path that was previously borrowed
256    assert_eq!(p1, p2);
257}
258
259#[test]
260fn make_sig() {
261    assert_eq!(&*Signature::make::<(&str, u8)>(), "(sy)");
262}
263
264#[test]
265fn partial_eq_str() {
266    assert_eq!(Signature::new("s").unwrap(), "s");
267    assert!(Signature::new("s").unwrap() != "s\0");
268    assert_eq!(Path::new("/hello").unwrap(), "/hello");
269}