| 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 | |
| 8 | use std::{str, fmt, ops, default, hash}; |
| 9 | use std::ffi::{CStr, CString}; |
| 10 | use std::borrow::{Borrow, Cow}; |
| 11 | use std::os::raw::c_char; |
| 12 | |
| 13 | #[cfg (not(feature = "no-string-validation" ))] |
| 14 | use crate::Error; |
| 15 | #[cfg (not(feature = "no-string-validation" ))] |
| 16 | use crate::ffi; |
| 17 | |
| 18 | macro_rules! cstring_wrapper { |
| 19 | ($t: ident, $s: ident) => { |
| 20 | |
| 21 | impl<'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 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. |
| 101 | impl<'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. |
| 106 | impl<'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. |
| 111 | impl<'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. |
| 116 | impl<'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 | |
| 123 | impl<'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. |
| 129 | impl<'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 | |
| 138 | impl<'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 | |
| 144 | impl<'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 | |
| 149 | impl<'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 | /* |
| 156 | As of dbus 0.9, this has been removed to prepare for a potential native implementation. |
| 157 | impl<'m> AsRef<CStr> for $t<'m> { |
| 158 | fn as_ref(&self) -> &CStr { &self.0 } |
| 159 | } |
| 160 | */ |
| 161 | |
| 162 | impl<'m> hash::Hash for $t<'m> { |
| 163 | fn hash<H: hash::Hasher>(&self, state: &mut H) { |
| 164 | self.0.hash(state); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | }} |
| 169 | |
| 170 | /// A wrapper around a string that is guaranteed to be |
| 171 | /// a valid (single) D-Bus type signature. |
| 172 | #[derive (Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] |
| 173 | pub struct Signature<'a>(Cow<'a, str>); |
| 174 | |
| 175 | cstring_wrapper!(Signature, dbus_signature_validate_single); |
| 176 | |
| 177 | impl Signature<'static> { |
| 178 | /// Makes a D-Bus signature that corresponds to A. |
| 179 | pub fn make<A: super::arg::Arg>() -> Signature<'static> { A::signature() } |
| 180 | } |
| 181 | |
| 182 | /// A wrapper around a string that is guaranteed to be |
| 183 | /// a valid D-Bus object path. |
| 184 | #[derive (Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] |
| 185 | pub struct Path<'a>(Cow<'a, str>); |
| 186 | |
| 187 | cstring_wrapper!(Path, dbus_validate_path); |
| 188 | |
| 189 | // This is needed so one can make arrays of paths easily |
| 190 | impl<'a> default::Default for Path<'a> { |
| 191 | fn default() -> Path<'a> { Path(Cow::Borrowed("/ \0" )) } |
| 192 | } |
| 193 | |
| 194 | /// A wrapper around a string that is guaranteed to be |
| 195 | /// a valid D-Bus member, i e, a signal or method name. |
| 196 | #[derive (Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] |
| 197 | pub struct Member<'a>(Cow<'a, str>); |
| 198 | |
| 199 | cstring_wrapper!(Member, dbus_validate_member); |
| 200 | |
| 201 | /// A wrapper around a string that is guaranteed to be |
| 202 | /// a valid D-Bus interface name. |
| 203 | #[derive (Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] |
| 204 | pub struct Interface<'a>(Cow<'a, str>); |
| 205 | |
| 206 | cstring_wrapper!(Interface, dbus_validate_interface); |
| 207 | |
| 208 | /// A wrapper around a string that is guaranteed to be |
| 209 | /// a valid D-Bus bus name. |
| 210 | #[derive (Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] |
| 211 | pub struct BusName<'a>(Cow<'a, str>); |
| 212 | |
| 213 | cstring_wrapper!(BusName, dbus_validate_bus_name); |
| 214 | |
| 215 | /// A wrapper around a string that is guaranteed to be |
| 216 | /// a valid D-Bus error name. |
| 217 | #[derive (Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] |
| 218 | pub struct ErrorName<'a>(Cow<'a, str>); |
| 219 | |
| 220 | cstring_wrapper!(ErrorName, dbus_validate_error_name); |
| 221 | |
| 222 | #[test ] |
| 223 | fn some_path() { |
| 224 | let p1: Path = "/valid" .into(); |
| 225 | let p2 = Path::new("##invalid##" ); |
| 226 | assert_eq!(p1, Path(Cow::Borrowed("/valid \0" ))); |
| 227 | #[cfg (not(feature = "no-string-validation" ))] |
| 228 | assert_eq!(p2, Err("Object path was not valid: '##invalid##'" .into())); |
| 229 | #[cfg (feature = "no-string-validation" )] |
| 230 | assert_eq!(p2, Ok(Path(Cow::Borrowed("##invalid## \0" )))); |
| 231 | } |
| 232 | |
| 233 | #[test ] |
| 234 | fn reborrow_path() { |
| 235 | let p1 = Path::from("/valid" ); |
| 236 | let p2 = p1.clone(); |
| 237 | { |
| 238 | let p2_borrow: &Path = &p2; |
| 239 | let p3 = Path::from(p2_borrow); |
| 240 | // Check path created from borrow |
| 241 | assert_eq!(p2, p3); |
| 242 | } |
| 243 | // Check path that was previously borrowed |
| 244 | assert_eq!(p1, p2); |
| 245 | } |
| 246 | |
| 247 | #[test ] |
| 248 | fn make_sig() { |
| 249 | assert_eq!(&*Signature::make::<(&str, u8)>(), "(sy)" ); |
| 250 | } |
| 251 | |