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 | |