summaryrefslogtreecommitdiffstats
path: root/third_party/rust/dbus/src/arg/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/dbus/src/arg/mod.rs')
-rw-r--r--third_party/rust/dbus/src/arg/mod.rs440
1 files changed, 440 insertions, 0 deletions
diff --git a/third_party/rust/dbus/src/arg/mod.rs b/third_party/rust/dbus/src/arg/mod.rs
new file mode 100644
index 0000000000..737960b4b8
--- /dev/null
+++ b/third_party/rust/dbus/src/arg/mod.rs
@@ -0,0 +1,440 @@
+//! Types and traits for easily getting a message's arguments, or appening a message with arguments.
+//!
+//! Also see the arguments guide (in the examples directory).
+//!
+//! A message has `read1`, `read2` etc, and `append1`, `append2` etc, which is your
+//! starting point into this module's types.
+//!
+//! **Append a**:
+//!
+//! `bool, u8, u16, u32, u64, i16, i32, i64, f64` - the corresponding D-Bus basic type
+//!
+//! `&str` - a D-Bus string. D-Bus strings do not allow null characters, so
+//! if the string contains null characters, it will be cropped
+//! to only include the data before the null character. (Tip: This allows for skipping an
+//! allocation by writing a string literal which ends with a null character.)
+//!
+//! `&[T] where T: Append` - a D-Bus array. Note: can use an efficient fast-path in case of
+//! T being an FixedArray type.
+//!
+//! `Array<T, I> where T: Append, I: Iterator<Item=T>` - a D-Bus array, maximum flexibility.
+//!
+//! `Variant<T> where T: Append` - a D-Bus variant.
+//!
+//! `(T1, T2) where T1: Append, T2: Append` - tuples are D-Bus structs. Implemented up to 12.
+//!
+//! `Dict<K, V, I> where K: Append + DictKey, V: Append, I: Iterator<Item=(&K, &V)>` - A D-Bus dict (array of dict entries).
+//!
+//! `Path` - a D-Bus object path.
+//!
+//! `Signature` - a D-Bus signature.
+//!
+//! `OwnedFd` - shares the file descriptor with the remote side.
+//!
+//! **Get / read a**:
+//!
+//! `bool, u8, u16, u32, u64, i16, i32, i64, f64` - the corresponding D-Bus basic type
+//!
+//! `&str`, `&CStr` - a D-Bus string. D-Bus strings are always UTF-8 and do not contain null characters.
+//!
+//! `&[T] where T: FixedArray` - a D-Bus array of integers or f64.
+//!
+//! `Array<T, Iter> where T: Get` - a D-Bus array, maximum flexibility. Implements Iterator so you can easily
+//! collect it into, e g, a `Vec`.
+//!
+//! `Variant<T> where T: Get` - a D-Bus variant. Use this type of Variant if you know the inner type.
+//!
+//! `Variant<Iter>` - a D-Bus variant. This type of Variant allows you to examine the inner type.
+//!
+//! `(T1, T2) where T1: Get, T2: Get` - tuples are D-Bus structs. Implemented up to 12.
+//!
+//! `Dict<K, V, Iter> where K: Get + DictKey, V: Get` - A D-Bus dict (array of dict entries). Implements Iterator so you can easily
+//! collect it into, e g, a `HashMap`.
+//!
+//! `Path` - a D-Bus object path.
+//!
+//! `Signature` - a D-Bus signature.
+//!
+//! `OwnedFd` - a file descriptor sent from the remote side.
+//!
+
+mod msgarg;
+mod basic_impl;
+mod variantstruct_impl;
+mod array_impl;
+
+pub use self::msgarg::{Arg, FixedArray, Get, DictKey, Append, RefArg, AppendAll, ReadAll, cast, cast_mut};
+pub use self::array_impl::{Array, Dict};
+pub use self::variantstruct_impl::Variant;
+
+use std::{fmt, mem, ptr, error};
+use {ffi, Message, Signature, Path, OwnedFd};
+use std::ffi::{CStr, CString};
+use std::os::raw::{c_void, c_int};
+
+
+fn check(f: &str, i: u32) { if i == 0 { panic!("D-Bus error: '{}' failed", f) }}
+
+fn ffi_iter() -> ffi::DBusMessageIter { unsafe { mem::zeroed() }}
+
+#[derive(Clone, Copy)]
+/// Helper struct for appending one or more arguments to a Message.
+pub struct IterAppend<'a>(ffi::DBusMessageIter, &'a Message);
+
+impl<'a> IterAppend<'a> {
+ /// Creates a new IterAppend struct.
+ pub fn new(m: &'a mut Message) -> IterAppend<'a> {
+ let mut i = ffi_iter();
+ unsafe { ffi::dbus_message_iter_init_append(m.ptr(), &mut i) };
+ IterAppend(i, m)
+ }
+
+ /// Appends the argument.
+ pub fn append<T: Append>(&mut self, a: T) { a.append(self) }
+
+ fn append_container<F: FnOnce(&mut IterAppend<'a>)>(&mut self, arg_type: ArgType, sig: Option<&CStr>, f: F) {
+ let mut s = IterAppend(ffi_iter(), self.1);
+ let p = sig.map(|s| s.as_ptr()).unwrap_or(ptr::null());
+ check("dbus_message_iter_open_container",
+ unsafe { ffi::dbus_message_iter_open_container(&mut self.0, arg_type as c_int, p, &mut s.0) });
+ f(&mut s);
+ check("dbus_message_iter_close_container",
+ unsafe { ffi::dbus_message_iter_close_container(&mut self.0, &mut s.0) });
+ }
+
+ /// Low-level function to append a variant.
+ ///
+ /// Use in case the `Variant` struct is not flexible enough -
+ /// the easier way is to just call e g "append1" on a message and supply a `Variant` parameter.
+ ///
+ /// In order not to get D-Bus errors: during the call to "f" you need to call "append" on
+ /// the supplied `IterAppend` exactly once,
+ /// and with a value which has the same signature as inner_sig.
+ pub fn append_variant<F: FnOnce(&mut IterAppend<'a>)>(&mut self, inner_sig: &Signature, f: F) {
+ self.append_container(ArgType::Variant, Some(inner_sig.as_cstr()), f)
+ }
+
+ /// Low-level function to append an array.
+ ///
+ /// Use in case the `Array` struct is not flexible enough -
+ /// the easier way is to just call e g "append1" on a message and supply an `Array` parameter.
+ ///
+ /// In order not to get D-Bus errors: during the call to "f", you should only call "append" on
+ /// the supplied `IterAppend` with values which has the same signature as inner_sig.
+ pub fn append_array<F: FnOnce(&mut IterAppend<'a>)>(&mut self, inner_sig: &Signature, f: F) {
+ self.append_container(ArgType::Array, Some(inner_sig.as_cstr()), f)
+ }
+
+ /// Low-level function to append a struct.
+ ///
+ /// Use in case tuples are not flexible enough -
+ /// the easier way is to just call e g "append1" on a message and supply a tuple parameter.
+ pub fn append_struct<F: FnOnce(&mut IterAppend<'a>)>(&mut self, f: F) {
+ self.append_container(ArgType::Struct, None, f)
+ }
+
+ /// Low-level function to append a dict entry.
+ ///
+ /// Use in case the `Dict` struct is not flexible enough -
+ /// the easier way is to just call e g "append1" on a message and supply a `Dict` parameter.
+ ///
+ /// In order not to get D-Bus errors: during the call to "f", you should call "append" once
+ /// for the key, then once for the value. You should only call this function for a subiterator
+ /// you got from calling "append_dict", and signatures need to match what you specified in "append_dict".
+ pub fn append_dict_entry<F: FnOnce(&mut IterAppend<'a>)>(&mut self, f: F) {
+ self.append_container(ArgType::DictEntry, None, f)
+ }
+
+ /// Low-level function to append a dict.
+ ///
+ /// Use in case the `Dict` struct is not flexible enough -
+ /// the easier way is to just call e g "append1" on a message and supply a `Dict` parameter.
+ ///
+ /// In order not to get D-Bus errors: during the call to "f", you should only call "append_dict_entry"
+ /// for the subiterator - do this as many times as the number of dict entries.
+ pub fn append_dict<F: FnOnce(&mut IterAppend<'a>)>(&mut self, key_sig: &Signature, value_sig: &Signature, f: F) {
+ let sig = format!("{{{}{}}}", key_sig, value_sig);
+ self.append_container(Array::<bool,()>::ARG_TYPE, Some(&CString::new(sig).unwrap()), f);
+ }
+}
+
+
+
+#[derive(Clone, Copy)]
+/// Helper struct for retrieve one or more arguments from a Message.
+pub struct Iter<'a>(ffi::DBusMessageIter, &'a Message, u32);
+
+impl<'a> Iter<'a> {
+ /// Creates a new struct for iterating over the arguments of a message, starting with the first argument.
+ pub fn new(m: &'a Message) -> Iter<'a> {
+ let mut i = ffi_iter();
+ unsafe { ffi::dbus_message_iter_init(m.ptr(), &mut i) };
+ Iter(i, m, 0)
+ }
+
+ /// Returns the current argument, if T is the argument type. Otherwise returns None.
+ pub fn get<T: Get<'a>>(&mut self) -> Option<T> {
+ T::get(self)
+ }
+
+ /// Returns the current argument as a trait object (experimental).
+ ///
+ /// Note: For the more complex arguments (arrays / dicts / structs, and especially
+ /// combinations thereof), their internal representations are still a bit in flux.
+ /// Instead, use as_iter() to read the values of those.
+ ///
+ /// The rest are unlikely to change - Variants are `Variant<Box<RefArg>>`, strings are `String`,
+ /// paths are `Path<'static>`, signatures are `Signature<'static>`, Int32 are `i32s` and so on.
+ pub fn get_refarg(&mut self) -> Option<Box<RefArg + 'static>> {
+ Some(match self.arg_type() {
+ ArgType::Array => array_impl::get_array_refarg(self),
+ ArgType::Variant => Box::new(Variant::new_refarg(self).unwrap()),
+ ArgType::Boolean => Box::new(self.get::<bool>().unwrap()),
+ ArgType::Invalid => return None,
+ ArgType::String => Box::new(self.get::<String>().unwrap()),
+ ArgType::DictEntry => unimplemented!(),
+ ArgType::Byte => Box::new(self.get::<u8>().unwrap()),
+ ArgType::Int16 => Box::new(self.get::<i16>().unwrap()),
+ ArgType::UInt16 => Box::new(self.get::<u16>().unwrap()),
+ ArgType::Int32 => Box::new(self.get::<i32>().unwrap()),
+ ArgType::UInt32 => Box::new(self.get::<u32>().unwrap()),
+ ArgType::Int64 => Box::new(self.get::<i64>().unwrap()),
+ ArgType::UInt64 => Box::new(self.get::<u64>().unwrap()),
+ ArgType::Double => Box::new(self.get::<f64>().unwrap()),
+ ArgType::UnixFd => Box::new(self.get::<OwnedFd>().unwrap()),
+ ArgType::Struct => Box::new(self.recurse(ArgType::Struct).unwrap().collect::<Vec<_>>()),
+ ArgType::ObjectPath => Box::new(self.get::<Path>().unwrap().into_static()),
+ ArgType::Signature => Box::new(self.get::<Signature>().unwrap().into_static()),
+ })
+ }
+
+ /// Returns the type signature for the current argument.
+ pub fn signature(&mut self) -> Signature<'static> {
+ unsafe {
+ let c = ffi::dbus_message_iter_get_signature(&mut self.0);
+ assert!(c != ptr::null_mut());
+ let cc = CStr::from_ptr(c);
+ let r = Signature::new(cc.to_bytes());
+ ffi::dbus_free(c as *mut c_void);
+ r.unwrap()
+ }
+ }
+
+ /// The raw arg_type for the current item.
+ ///
+ /// Unlike Arg::arg_type, this requires access to self and is not a static method.
+ /// You can match this against Arg::arg_type for different types to understand what type the current item is.
+ /// In case you're past the last argument, this function will return 0.
+ pub fn arg_type(&mut self) -> ArgType {
+ let s = unsafe { ffi::dbus_message_iter_get_arg_type(&mut self.0) };
+ ArgType::from_i32(s as i32).unwrap()
+ }
+
+ /// Returns false if there are no more items.
+ pub fn next(&mut self) -> bool {
+ self.2 += 1;
+ unsafe { ffi::dbus_message_iter_next(&mut self.0) != 0 }
+ }
+
+ /// Wrapper around `get` and `next`. Calls `get`, and then `next` if `get` succeeded.
+ ///
+ /// Also returns a `Result` rather than an `Option` to give an error if successful.
+ ///
+ /// # Example
+ /// ```ignore
+ /// struct ServiceBrowserItemNew {
+ /// interface: i32,
+ /// protocol: i32,
+ /// name: String,
+ /// item_type: String,
+ /// domain: String,
+ /// flags: u32,
+ /// }
+ ///
+ /// fn service_browser_item_new_msg(m: &Message) -> Result<ServiceBrowserItemNew, TypeMismatchError> {
+ /// let mut iter = m.iter_init();
+ /// Ok(ServiceBrowserItemNew {
+ /// interface: iter.read()?,
+ /// protocol: iter.read()?,
+ /// name: iter.read()?,
+ /// item_type: iter.read()?,
+ /// domain: iter.read()?,
+ /// flags: iter.read()?,
+ /// })
+ /// }
+ /// ```
+ pub fn read<T: Arg + Get<'a>>(&mut self) -> Result<T, TypeMismatchError> {
+ let r = try!(self.get().ok_or_else(||
+ TypeMismatchError { expected: T::ARG_TYPE, found: self.arg_type(), position: self.2 }));
+ self.next();
+ Ok(r)
+ }
+
+ /// If the current argument is a container of the specified arg_type, then a new
+ /// Iter is returned which is for iterating over the contents inside the container.
+ ///
+ /// Primarily for internal use (the "get" function is more ergonomic), but could be
+ /// useful for recursing into containers with unknown types.
+ pub fn recurse(&mut self, arg_type: ArgType) -> Option<Iter<'a>> {
+ let containers = [ArgType::Array, ArgType::DictEntry, ArgType::Struct, ArgType::Variant];
+ if !containers.iter().any(|&t| t == arg_type) { return None; }
+
+ let mut subiter = ffi_iter();
+ unsafe {
+ if ffi::dbus_message_iter_get_arg_type(&mut self.0) != arg_type as c_int { return None };
+ ffi::dbus_message_iter_recurse(&mut self.0, &mut subiter)
+ }
+ Some(Iter(subiter, self.1, 0))
+ }
+}
+
+impl<'a> fmt::Debug for Iter<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut z = self.clone();
+ let mut t = f.debug_tuple("Iter");
+ loop {
+ t.field(&z.arg_type());
+ if !z.next() { break }
+ }
+ t.finish()
+ }
+}
+
+impl<'a> Iterator for Iter<'a> {
+ type Item = Box<RefArg + 'static>;
+ fn next(&mut self) -> Option<Self::Item> {
+ let r = self.get_refarg();
+ if r.is_some() { self.next(); }
+ r
+ }
+}
+
+/// Type of Argument
+///
+/// use this to figure out, e g, which type of argument is at the current position of Iter.
+#[repr(u8)]
+#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
+pub enum ArgType {
+ /// Dicts are Arrays of dict entries, so Dict types will have Array as ArgType.
+ Array = ffi::DBUS_TYPE_ARRAY as u8,
+ /// Variant
+ Variant = ffi::DBUS_TYPE_VARIANT as u8,
+ /// bool
+ Boolean = ffi::DBUS_TYPE_BOOLEAN as u8,
+ /// Invalid arg type - this is also the ArgType returned when there are no more arguments available.
+ Invalid = ffi::DBUS_TYPE_INVALID as u8,
+ /// String
+ String = ffi::DBUS_TYPE_STRING as u8,
+ /// Dict entry; you'll usually not encounter this one as dicts are arrays of dict entries.
+ DictEntry = ffi::DBUS_TYPE_DICT_ENTRY as u8,
+ /// u8
+ Byte = ffi::DBUS_TYPE_BYTE as u8,
+ /// i16
+ Int16 = ffi::DBUS_TYPE_INT16 as u8,
+ /// u16
+ UInt16 = ffi::DBUS_TYPE_UINT16 as u8,
+ /// i32
+ Int32 = ffi::DBUS_TYPE_INT32 as u8,
+ /// u32
+ UInt32 = ffi::DBUS_TYPE_UINT32 as u8,
+ /// i64
+ Int64 = ffi::DBUS_TYPE_INT64 as u8,
+ /// u64
+ UInt64 = ffi::DBUS_TYPE_UINT64 as u8,
+ /// f64
+ Double = ffi::DBUS_TYPE_DOUBLE as u8,
+ /// OwnedFd
+ UnixFd = ffi::DBUS_TYPE_UNIX_FD as u8,
+ /// Use tuples or Vec<Box<RefArg>> to read/write structs.
+ Struct = ffi::DBUS_TYPE_STRUCT as u8,
+ /// Path
+ ObjectPath = ffi::DBUS_TYPE_OBJECT_PATH as u8,
+ /// Signature
+ Signature = ffi::DBUS_TYPE_SIGNATURE as u8,
+}
+
+const ALL_ARG_TYPES: [(ArgType, &'static str); 18] =
+ [(ArgType::Variant, "Variant"),
+ (ArgType::Array, "Array/Dict"),
+ (ArgType::Struct, "Struct"),
+ (ArgType::String, "String"),
+ (ArgType::DictEntry, "Dict entry"),
+ (ArgType::ObjectPath, "Path"),
+ (ArgType::Signature, "Signature"),
+ (ArgType::UnixFd, "OwnedFd"),
+ (ArgType::Boolean, "bool"),
+ (ArgType::Byte, "u8"),
+ (ArgType::Int16, "i16"),
+ (ArgType::Int32, "i32"),
+ (ArgType::Int64, "i64"),
+ (ArgType::UInt16, "u16"),
+ (ArgType::UInt32, "u32"),
+ (ArgType::UInt64, "u64"),
+ (ArgType::Double, "f64"),
+ (ArgType::Invalid, "nothing")];
+
+impl ArgType {
+ /// A str corresponding to the name of a Rust type.
+ pub fn as_str(self) -> &'static str {
+ ALL_ARG_TYPES.iter().skip_while(|a| a.0 != self).next().unwrap().1
+ }
+
+ /// Converts an i32 to an ArgType (or an error).
+ pub fn from_i32(i: i32) -> Result<ArgType, String> {
+ for &(a, _) in &ALL_ARG_TYPES {
+ if a as i32 == i { return Ok(a); }
+ }
+ Err(format!("Invalid ArgType {} ({})", i, i as u8 as char))
+ }
+}
+
+
+/// Error struct to indicate a D-Bus argument type mismatch.
+///
+/// Might be returned from `iter::read()`.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub struct TypeMismatchError {
+ expected: ArgType,
+ found: ArgType,
+ position: u32,
+}
+
+impl TypeMismatchError {
+ /// The ArgType we were trying to read, but failed
+ pub fn expected_arg_type(&self) -> ArgType { self.expected }
+
+ /// The ArgType we should have been trying to read, if we wanted the read to succeed
+ pub fn found_arg_type(&self) -> ArgType { self.found }
+
+ /// At what argument was the error found?
+ ///
+ /// Returns 0 for first argument, 1 for second argument, etc.
+ pub fn pos(&self) -> u32 { self.position }
+}
+
+impl error::Error for TypeMismatchError {
+ fn description(&self) -> &str { "D-Bus argument type mismatch" }
+ fn cause(&self) -> Option<&error::Error> { None }
+}
+
+impl fmt::Display for TypeMismatchError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{} at position {}: expected {}, found {}",
+ (self as &error::Error).description(),
+ self.position, self.expected.as_str(),
+ if self.expected == self.found { "same but still different somehow" } else { self.found.as_str() }
+ )
+ }
+}
+
+
+#[allow(dead_code)]
+fn test_compile() {
+ let mut q = IterAppend::new(unsafe { mem::transmute(0usize) });
+
+ q.append(5u8);
+ q.append(Array::new(&[5u8, 6, 7]));
+ q.append((8u8, &[9u8, 6, 7][..]));
+ q.append(Variant((6u8, 7u8)));
+}
+