mod msgarg;
mod basic_impl;
mod variantstruct_impl;
mod array_impl;
pub mod messageitem;
pub use self::msgarg::{Arg, FixedArray, Get, DictKey, Append, RefArg, AppendAll, ReadAll, ArgAll, cast, cast_mut};
pub use self::array_impl::{Array, Dict};
pub use self::variantstruct_impl::Variant;
use std::{fmt, mem, ptr, error};
use crate::{ffi, Message, Signature, Path};
use std::ffi::{CStr, CString};
use std::os::raw::{c_void, c_int};
use std::os::unix::io::{RawFd, AsRawFd};
fn check(f: &str, i: u32) { if i == 0 { panic!("D-Bus error: '{}' failed", f) }}
fn ffi_iter() -> ffi::DBusMessageIter { unsafe { mem::zeroed() }}
#[derive(Debug, PartialEq, PartialOrd)]
pub struct OwnedFd {
fd: RawFd
}
impl OwnedFd {
pub fn new(fd: RawFd) -> OwnedFd {
OwnedFd { fd: fd }
}
pub fn into_fd(self) -> RawFd {
let s = self.fd;
::std::mem::forget(self);
s
}
}
impl Drop for OwnedFd {
fn drop(&mut self) {
unsafe { libc::close(self.fd); }
}
}
impl Clone for OwnedFd {
fn clone(&self) -> OwnedFd {
let x = unsafe { libc::dup(self.fd) };
if x == -1 { panic!("Duplicating file descriptor failed") }
OwnedFd::new(x)
}
}
impl AsRawFd for OwnedFd {
fn as_raw_fd(&self) -> RawFd {
self.fd
}
}
#[derive(Clone, Copy)]
pub struct IterAppend<'a>(ffi::DBusMessageIter, &'a Message);
impl<'a> IterAppend<'a> {
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)
}
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) });
}
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)
}
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)
}
pub fn append_struct<F: FnOnce(&mut IterAppend<'a>)>(&mut self, f: F) {
self.append_container(ArgType::Struct, None, f)
}
pub fn append_dict_entry<F: FnOnce(&mut IterAppend<'a>)>(&mut self, f: F) {
self.append_container(ArgType::DictEntry, None, f)
}
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)]
pub struct Iter<'a>(ffi::DBusMessageIter, &'a Message, u32);
impl<'a> Iter<'a> {
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)
}
pub fn get<T: Get<'a>>(&mut self) -> Option<T> {
T::get(self)
}
pub fn get_refarg(&mut self) -> Option<Box<dyn 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()),
})
}
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()
}
}
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()
}
pub fn next(&mut self) -> bool {
self.2 += 1;
unsafe { ffi::dbus_message_iter_next(&mut self.0) != 0 }
}
pub fn read<T: Arg + Get<'a>>(&mut self) -> Result<T, TypeMismatchError> {
let r = self.get().ok_or_else(||
TypeMismatchError { expected: T::ARG_TYPE, found: self.arg_type(), position: self.2 })?;
self.next();
Ok(r)
}
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<dyn RefArg + 'static>;
fn next(&mut self) -> Option<Self::Item> {
let r = self.get_refarg();
if r.is_some() { self.next(); }
r
}
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub enum ArgType {
Array = ffi::DBUS_TYPE_ARRAY as u8,
Variant = ffi::DBUS_TYPE_VARIANT as u8,
Boolean = ffi::DBUS_TYPE_BOOLEAN as u8,
Invalid = ffi::DBUS_TYPE_INVALID as u8,
String = ffi::DBUS_TYPE_STRING as u8,
DictEntry = ffi::DBUS_TYPE_DICT_ENTRY as u8,
Byte = ffi::DBUS_TYPE_BYTE as u8,
Int16 = ffi::DBUS_TYPE_INT16 as u8,
UInt16 = ffi::DBUS_TYPE_UINT16 as u8,
Int32 = ffi::DBUS_TYPE_INT32 as u8,
UInt32 = ffi::DBUS_TYPE_UINT32 as u8,
Int64 = ffi::DBUS_TYPE_INT64 as u8,
UInt64 = ffi::DBUS_TYPE_UINT64 as u8,
Double = ffi::DBUS_TYPE_DOUBLE as u8,
UnixFd = ffi::DBUS_TYPE_UNIX_FD as u8,
Struct = ffi::DBUS_TYPE_STRUCT as u8,
ObjectPath = ffi::DBUS_TYPE_OBJECT_PATH as u8,
Signature = ffi::DBUS_TYPE_SIGNATURE as u8,
}
const ALL_ARG_TYPES: [(ArgType, &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 {
pub fn as_str(self) -> &'static str {
ALL_ARG_TYPES.iter().skip_while(|a| a.0 != self).next().unwrap().1
}
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))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TypeMismatchError {
expected: ArgType,
found: ArgType,
position: u32,
}
impl TypeMismatchError {
pub fn expected_arg_type(&self) -> ArgType { self.expected }
pub fn found_arg_type(&self) -> ArgType { self.found }
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<&dyn error::Error> { None }
}
impl fmt::Display for TypeMismatchError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} at position {}: expected {}, found {}",
error::Error::description(self),
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)));
}