1//! Types and traits for easily getting a message's arguments, or appening a message with arguments.
2//!
3//! Also see the argument's guide (in the examples directory) for details about which Rust
4//! types correspond to which D-Bus types.
5//!
6//! A message has `read1`, `read2` etc, and `append1`, `append2` etc, which is one
7//! starting point into this module's types.
8
9
10mod msgarg;
11mod basic_impl;
12mod variantstruct_impl;
13mod array_impl;
14
15pub mod messageitem;
16
17pub use self::msgarg::{Arg, FixedArray, Get, DictKey, Append, RefArg, AppendAll, ReadAll, ArgAll,
18 cast, cast_mut, prop_cast, PropMap};
19pub use self::array_impl::{Array, Dict};
20pub use self::variantstruct_impl::Variant;
21
22use std::{fmt, mem, ptr, error};
23use crate::{ffi, Message, Signature, Path};
24use std::ffi::{CStr, CString};
25use std::os::raw::{c_void, c_int};
26#[cfg(unix)]
27use std::os::unix::io::{AsRawFd, FromRawFd};
28use std::collections::VecDeque;
29
30fn check(f: &str, i: u32) { if i == 0 { panic!("D-Bus error: '{}' failed", f) }}
31
32fn ffi_iter() -> ffi::DBusMessageIter {
33 // Safe because DBusMessageIter contains only fields that are allowed to be zeroed (i e no references or similar)
34 unsafe { mem::zeroed() }
35}
36
37#[cfg(feature = "stdfd")]
38pub use std::os::unix::io::OwnedFd;
39
40/// An RAII wrapper around Fd to ensure that file descriptor is closed
41/// when the scope ends. Enable the `stdfd` feature to use std's OwnedFd instead.
42#[cfg(not(feature = "stdfd"))]
43#[derive(Debug, PartialEq, PartialOrd)]
44pub struct OwnedFd {
45 #[cfg(unix)]
46 fd: std::os::unix::io::RawFd
47}
48
49#[cfg(all(unix,not(feature = "stdfd")))]
50mod owned_fd_impl {
51 use super::OwnedFd;
52 use std::os::unix::io::{RawFd, AsRawFd, FromRawFd, IntoRawFd};
53
54 impl OwnedFd {
55 /// Create a new OwnedFd from a RawFd.
56 ///
57 /// This function is unsafe, because you could potentially send in an invalid file descriptor,
58 /// or close it during the lifetime of this struct. This could potentially be unsound.
59 pub unsafe fn new(fd: RawFd) -> OwnedFd {
60 OwnedFd { fd: fd }
61 }
62
63 /// Convert an OwnedFD back into a RawFd.
64 pub fn into_fd(self) -> RawFd {
65 let s = self.fd;
66 ::std::mem::forget(self);
67 s
68 }
69
70 /// Tries to clone the fd.
71 pub fn try_clone(&self) -> Result<Self, &'static str> {
72 let x = unsafe { libc::dup(self.fd) };
73 if x == -1 { Err("Duplicating file descriptor failed") }
74 else { Ok(unsafe { OwnedFd::new(x) }) }
75 }
76 }
77
78 impl Drop for OwnedFd {
79 fn drop(&mut self) {
80 unsafe { libc::close(self.fd); }
81 }
82 }
83
84 impl AsRawFd for OwnedFd {
85 fn as_raw_fd(&self) -> RawFd {
86 self.fd
87 }
88 }
89
90 impl IntoRawFd for OwnedFd {
91 fn into_raw_fd(self) -> RawFd {
92 self.into_fd()
93 }
94 }
95
96 impl FromRawFd for OwnedFd {
97 unsafe fn from_raw_fd(fd: RawFd) -> Self { OwnedFd::new(fd) }
98 }
99}
100
101#[cfg(not(feature = "stdfd"))]
102impl Clone for OwnedFd {
103 #[cfg(unix)]
104 fn clone(&self) -> OwnedFd {
105 self.try_clone().unwrap()
106 }
107
108 #[cfg(windows)]
109 fn clone(&self) -> OwnedFd {
110 OwnedFd {}
111 }
112}
113
114
115#[derive(Clone, Copy)]
116/// Helper struct for appending one or more arguments to a Message.
117pub struct IterAppend<'a>(ffi::DBusMessageIter, &'a Message);
118
119impl<'a> IterAppend<'a> {
120 /// Creates a new IterAppend struct.
121 pub fn new(m: &'a mut Message) -> IterAppend<'a> {
122 let mut i = ffi_iter();
123 unsafe { ffi::dbus_message_iter_init_append(m.ptr(), &mut i) };
124 IterAppend(i, m)
125 }
126
127 /// Appends the argument.
128 pub fn append<T: Append>(&mut self, a: T) { a.append(self) }
129
130 fn append_container<F: FnOnce(&mut IterAppend<'a>)>(&mut self, arg_type: ArgType, sig: Option<&CStr>, f: F) {
131 let mut s = IterAppend(ffi_iter(), self.1);
132 let p = sig.map(|s| s.as_ptr()).unwrap_or(ptr::null());
133 check("dbus_message_iter_open_container",
134 unsafe { ffi::dbus_message_iter_open_container(&mut self.0, arg_type as c_int, p, &mut s.0) });
135 f(&mut s);
136 check("dbus_message_iter_close_container",
137 unsafe { ffi::dbus_message_iter_close_container(&mut self.0, &mut s.0) });
138 }
139
140 /// Low-level function to append a variant.
141 ///
142 /// Use in case the `Variant` struct is not flexible enough -
143 /// the easier way is to just call e g "append1" on a message and supply a `Variant` parameter.
144 ///
145 /// In order not to get D-Bus errors: during the call to "f" you need to call "append" on
146 /// the supplied `IterAppend` exactly once,
147 /// and with a value which has the same signature as inner_sig.
148 pub fn append_variant<F: FnOnce(&mut IterAppend<'a>)>(&mut self, inner_sig: &Signature, f: F) {
149 self.append_container(ArgType::Variant, Some(inner_sig.as_cstr()), f)
150 }
151
152 /// Low-level function to append an array.
153 ///
154 /// Use in case the `Array` struct is not flexible enough -
155 /// the easier way is to just call e g "append1" on a message and supply an `Array` parameter.
156 ///
157 /// In order not to get D-Bus errors: during the call to "f", you should only call "append" on
158 /// the supplied `IterAppend` with values which has the same signature as inner_sig.
159 pub fn append_array<F: FnOnce(&mut IterAppend<'a>)>(&mut self, inner_sig: &Signature, f: F) {
160 self.append_container(ArgType::Array, Some(inner_sig.as_cstr()), f)
161 }
162
163 /// Low-level function to append a struct.
164 ///
165 /// Use in case tuples are not flexible enough -
166 /// the easier way is to just call e g "append1" on a message and supply a tuple parameter.
167 pub fn append_struct<F: FnOnce(&mut IterAppend<'a>)>(&mut self, f: F) {
168 self.append_container(ArgType::Struct, None, f)
169 }
170
171 /// Low-level function to append a dict entry.
172 ///
173 /// Use in case the `Dict` struct is not flexible enough -
174 /// the easier way is to just call e g "append1" on a message and supply a `Dict` parameter.
175 ///
176 /// In order not to get D-Bus errors: during the call to "f", you should call "append" once
177 /// for the key, then once for the value. You should only call this function for a subiterator
178 /// you got from calling "append_dict", and signatures need to match what you specified in "append_dict".
179 pub fn append_dict_entry<F: FnOnce(&mut IterAppend<'a>)>(&mut self, f: F) {
180 self.append_container(ArgType::DictEntry, None, f)
181 }
182
183 /// Low-level function to append a dict.
184 ///
185 /// Use in case the `Dict` struct is not flexible enough -
186 /// the easier way is to just call e g "append1" on a message and supply a `Dict` parameter.
187 ///
188 /// In order not to get D-Bus errors: during the call to "f", you should only call "append_dict_entry"
189 /// for the subiterator - do this as many times as the number of dict entries.
190 pub fn append_dict<F: FnOnce(&mut IterAppend<'a>)>(&mut self, key_sig: &Signature, value_sig: &Signature, f: F) {
191 let sig = format!("{{{}{}}}", key_sig, value_sig);
192 self.append_container(Array::<bool,()>::ARG_TYPE, Some(&CString::new(sig).unwrap()), f);
193 }
194}
195
196
197
198#[derive(Clone, Copy)]
199/// Helper struct for retrieve one or more arguments from a Message.
200pub struct Iter<'a>(ffi::DBusMessageIter, &'a Message, u32);
201
202impl<'a> Iter<'a> {
203 /// Creates a new struct for iterating over the arguments of a message, starting with the first argument.
204 pub fn new(m: &'a Message) -> Iter<'a> {
205 let mut i = ffi_iter();
206 unsafe { ffi::dbus_message_iter_init(m.ptr(), &mut i) };
207 Iter(i, m, 0)
208 }
209
210 /// Returns the current argument, if T is the argument type. Otherwise returns None.
211 pub fn get<T: Get<'a>>(&mut self) -> Option<T> {
212 T::get(self)
213 }
214
215 /// Returns the current argument as a trait object.
216 ///
217 /// See the argument guide's reference part for information on what types hide inside the RefArg.
218 pub fn get_refarg(&mut self) -> Option<Box<dyn RefArg + 'static>> {
219 Some(match self.arg_type() {
220 ArgType::Array => array_impl::get_array_refarg(self),
221 ArgType::Variant => Box::new(Variant::new_refarg(self).unwrap()),
222 ArgType::Boolean => Box::new(self.get::<bool>().unwrap()),
223 ArgType::Invalid => return None,
224 ArgType::String => Box::new(self.get::<String>().unwrap()),
225 ArgType::DictEntry => unimplemented!(),
226 ArgType::Byte => Box::new(self.get::<u8>().unwrap()),
227 ArgType::Int16 => Box::new(self.get::<i16>().unwrap()),
228 ArgType::UInt16 => Box::new(self.get::<u16>().unwrap()),
229 ArgType::Int32 => Box::new(self.get::<i32>().unwrap()),
230 ArgType::UInt32 => Box::new(self.get::<u32>().unwrap()),
231 ArgType::Int64 => Box::new(self.get::<i64>().unwrap()),
232 ArgType::UInt64 => Box::new(self.get::<u64>().unwrap()),
233 ArgType::Double => Box::new(self.get::<f64>().unwrap()),
234 ArgType::UnixFd => Box::new(self.get::<std::fs::File>().unwrap()),
235 ArgType::Struct => Box::new(self.recurse(ArgType::Struct).unwrap().collect::<VecDeque<_>>()),
236 ArgType::ObjectPath => Box::new(self.get::<Path>().unwrap().into_static()),
237 ArgType::Signature => Box::new(self.get::<Signature>().unwrap().into_static()),
238 })
239 }
240
241 /// Returns the type signature for the current argument.
242 pub fn signature(&mut self) -> Signature<'static> {
243 unsafe {
244 let c = ffi::dbus_message_iter_get_signature(&mut self.0);
245 assert!(c != ptr::null_mut());
246 let cc = CStr::from_ptr(c);
247 let r = Signature::new(std::str::from_utf8(cc.to_bytes()).unwrap());
248 ffi::dbus_free(c as *mut c_void);
249 r.unwrap()
250 }
251 }
252
253 /// The raw arg_type for the current item.
254 ///
255 /// Unlike Arg::arg_type, this requires access to self and is not a static method.
256 /// You can match this against Arg::arg_type for different types to understand what type the current item is.
257 /// In case you're past the last argument, this function will return 0.
258 pub fn arg_type(&mut self) -> ArgType {
259 let s = unsafe { ffi::dbus_message_iter_get_arg_type(&mut self.0) };
260 ArgType::from_i32(s as i32).unwrap()
261 }
262
263 /// Returns false if there are no more items.
264 pub fn next(&mut self) -> bool {
265 self.2 += 1;
266 unsafe { ffi::dbus_message_iter_next(&mut self.0) != 0 }
267 }
268
269 /// Wrapper around `get` and `next`. Calls `get`, and then `next` if `get` succeeded.
270 ///
271 /// Also returns a `Result` rather than an `Option` to give an error if successful.
272 ///
273 /// # Example
274 /// ```ignore
275 /// struct ServiceBrowserItemNew {
276 /// interface: i32,
277 /// protocol: i32,
278 /// name: String,
279 /// item_type: String,
280 /// domain: String,
281 /// flags: u32,
282 /// }
283 ///
284 /// fn service_browser_item_new_msg(m: &Message) -> Result<ServiceBrowserItemNew, TypeMismatchError> {
285 /// let mut iter = m.iter_init();
286 /// Ok(ServiceBrowserItemNew {
287 /// interface: iter.read()?,
288 /// protocol: iter.read()?,
289 /// name: iter.read()?,
290 /// item_type: iter.read()?,
291 /// domain: iter.read()?,
292 /// flags: iter.read()?,
293 /// })
294 /// }
295 /// ```
296 pub fn read<T: Arg + Get<'a>>(&mut self) -> Result<T, TypeMismatchError> {
297 let r = self.get().ok_or_else(||
298 TypeMismatchError { expected: T::ARG_TYPE, found: self.arg_type(), position: self.2 })?;
299 self.next();
300 Ok(r)
301 }
302
303 /// If the current argument is a container of the specified arg_type, then a new
304 /// Iter is returned which is for iterating over the contents inside the container.
305 ///
306 /// Primarily for internal use (the "get" function is more ergonomic), but could be
307 /// useful for recursing into containers with unknown types.
308 pub fn recurse(&mut self, arg_type: ArgType) -> Option<Iter<'a>> {
309 let containers = [ArgType::Array, ArgType::DictEntry, ArgType::Struct, ArgType::Variant];
310 if !containers.iter().any(|&t| t == arg_type) { return None; }
311
312 let mut subiter = ffi_iter();
313 unsafe {
314 if ffi::dbus_message_iter_get_arg_type(&mut self.0) != arg_type as c_int { return None };
315 ffi::dbus_message_iter_recurse(&mut self.0, &mut subiter)
316 }
317 Some(Iter(subiter, self.1, 0))
318 }
319}
320
321impl<'a> fmt::Debug for Iter<'a> {
322 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
323 let mut z = self.clone();
324 let mut t = f.debug_tuple("Iter");
325 loop {
326 t.field(&z.arg_type());
327 if !z.next() { break }
328 }
329 t.finish()
330 }
331}
332
333impl<'a> Iterator for Iter<'a> {
334 type Item = Box<dyn RefArg + 'static>;
335 fn next(&mut self) -> Option<Self::Item> {
336 let r = self.get_refarg();
337 if r.is_some() { self.next(); }
338 r
339 }
340}
341
342/// Type of Argument
343///
344/// use this to figure out, e g, which type of argument is at the current position of Iter.
345#[repr(u8)]
346#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
347pub enum ArgType {
348 /// Dicts are Arrays of dict entries, so Dict types will have Array as ArgType.
349 Array = ffi::DBUS_TYPE_ARRAY as u8,
350 /// Variant
351 Variant = ffi::DBUS_TYPE_VARIANT as u8,
352 /// bool
353 Boolean = ffi::DBUS_TYPE_BOOLEAN as u8,
354 /// Invalid arg type - this is also the ArgType returned when there are no more arguments available.
355 Invalid = ffi::DBUS_TYPE_INVALID as u8,
356 /// String
357 String = ffi::DBUS_TYPE_STRING as u8,
358 /// Dict entry; you'll usually not encounter this one as dicts are arrays of dict entries.
359 DictEntry = ffi::DBUS_TYPE_DICT_ENTRY as u8,
360 /// u8
361 Byte = ffi::DBUS_TYPE_BYTE as u8,
362 /// i16
363 Int16 = ffi::DBUS_TYPE_INT16 as u8,
364 /// u16
365 UInt16 = ffi::DBUS_TYPE_UINT16 as u8,
366 /// i32
367 Int32 = ffi::DBUS_TYPE_INT32 as u8,
368 /// u32
369 UInt32 = ffi::DBUS_TYPE_UINT32 as u8,
370 /// i64
371 Int64 = ffi::DBUS_TYPE_INT64 as u8,
372 /// u64
373 UInt64 = ffi::DBUS_TYPE_UINT64 as u8,
374 /// f64
375 Double = ffi::DBUS_TYPE_DOUBLE as u8,
376 /// File
377 UnixFd = ffi::DBUS_TYPE_UNIX_FD as u8,
378 /// Use tuples or Vec<Box<dyn RefArg>> to read/write structs.
379 Struct = ffi::DBUS_TYPE_STRUCT as u8,
380 /// Path
381 ObjectPath = ffi::DBUS_TYPE_OBJECT_PATH as u8,
382 /// Signature
383 Signature = ffi::DBUS_TYPE_SIGNATURE as u8,
384}
385
386const ALL_ARG_TYPES: [(ArgType, &str); 18] =
387 [(ArgType::Variant, "Variant"),
388 (ArgType::Array, "Array/Dict"),
389 (ArgType::Struct, "Struct"),
390 (ArgType::String, "String"),
391 (ArgType::DictEntry, "Dict entry"),
392 (ArgType::ObjectPath, "Path"),
393 (ArgType::Signature, "Signature"),
394 (ArgType::UnixFd, "File"),
395 (ArgType::Boolean, "bool"),
396 (ArgType::Byte, "u8"),
397 (ArgType::Int16, "i16"),
398 (ArgType::Int32, "i32"),
399 (ArgType::Int64, "i64"),
400 (ArgType::UInt16, "u16"),
401 (ArgType::UInt32, "u32"),
402 (ArgType::UInt64, "u64"),
403 (ArgType::Double, "f64"),
404 (ArgType::Invalid, "nothing")];
405
406impl ArgType {
407 /// A str corresponding to the name of a Rust type.
408 pub fn as_str(self) -> &'static str {
409 ALL_ARG_TYPES.iter().skip_while(|a| a.0 != self).next().unwrap().1
410 }
411
412 /// Returns a Vec of all possible argtypes.
413 pub fn all() -> Vec<Self> {
414 ALL_ARG_TYPES.iter().map(|x| x.0).collect()
415 }
416
417 /// Converts an i32 to an ArgType (or an error).
418 pub fn from_i32(i: i32) -> Result<ArgType, String> {
419 for &(a: ArgType, _) in &ALL_ARG_TYPES {
420 if a as i32 == i { return Ok(a); }
421 }
422 Err(format!("Invalid ArgType {} ({})", i, i as u8 as char))
423 }
424}
425
426
427/// Error struct to indicate a D-Bus argument type mismatch.
428///
429/// Might be returned from `iter::read()`.
430#[derive(Clone, Copy, Debug, PartialEq, Eq)]
431pub struct TypeMismatchError {
432 expected: ArgType,
433 found: ArgType,
434 position: u32,
435}
436
437impl TypeMismatchError {
438 /// The ArgType we were trying to read, but failed
439 pub fn expected_arg_type(&self) -> ArgType { self.expected }
440
441 /// The ArgType we should have been trying to read, if we wanted the read to succeed
442 pub fn found_arg_type(&self) -> ArgType { self.found }
443
444 /// At what argument was the error found?
445 ///
446 /// Returns 0 for first argument, 1 for second argument, etc.
447 pub fn pos(&self) -> u32 { self.position }
448}
449
450impl error::Error for TypeMismatchError {
451 fn description(&self) -> &str { "D-Bus argument type mismatch" }
452 fn cause(&self) -> Option<&dyn error::Error> { None }
453}
454
455impl fmt::Display for TypeMismatchError {
456 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
457 write!(f, "D-Bus argument type mismatch at position {}: expected {}, found {}",
458 self.position, self.expected.as_str(),
459 if self.expected == self.found { "same but still different somehow" } else { self.found.as_str() }
460 )
461 }
462}
463
464
465#[allow(dead_code)]
466fn test_compile() {
467 let mut msg = Message::new_signal(path:"/", iface:"a.b", name:"C").unwrap();
468 let mut q: IterAppend<'_> = IterAppend::new(&mut msg);
469
470 q.append(5u8);
471 q.append(Array::new(&[5u8, 6, 7]));
472 q.append((8u8, &[9u8, 6, 7][..]));
473 q.append(Variant((6u8, 7u8)));
474}
475