use super::{Connection, Message, MessageItem, Error, Path, Interface, BusName}; use std::collections::BTreeMap; /// Client side properties - get and set properties on a remote application. pub struct Props<'a> { name: BusName<'a>, path: Path<'a>, interface: Interface<'a>, timeout_ms: i32, conn: &'a Connection, } impl<'a> Props<'a> { /// Create a new Props. pub fn new(conn: &'a Connection, name: N, path: P, interface: I, timeout_ms: i32) -> Props<'a> where N: Into>, P: Into>, I: Into> { Props { name: name.into(), path: path.into(), interface: interface.into(), timeout_ms: timeout_ms, conn: conn, } } /// Get a single property's value. pub fn get(&self, propname: &str) -> Result { let mut m = Message::method_call(&self.name, &self.path, &"org.freedesktop.DBus.Properties".into(), &"Get".into()); m.append_items(&[self.interface.to_string().into(), propname.to_string().into()]); let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms)); let reply = try!(r.as_result()).get_items(); if reply.len() == 1 { if let &MessageItem::Variant(ref v) = &reply[0] { return Ok((**v).clone()) } } let f = format!("Invalid reply for property get {}: '{:?}'", propname, reply); return Err(Error::new_custom("InvalidReply", &f)); } /// Set a single property's value. pub fn set(&self, propname: &str, value: MessageItem) -> Result<(), Error> { let mut m = Message::method_call(&self.name, &self.path, &"org.freedesktop.DBus.Properties".into(), &"Set".into()); m.append_items(&[self.interface.to_string().into(), propname.to_string().into(), Box::new(value).into()]); let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms)); try!(r.as_result()); Ok(()) } /// Get a map of all the properties' names and their values. pub fn get_all(&self) -> Result, Error> { let mut m = Message::method_call(&self.name, &self.path, &"org.freedesktop.DBus.Properties".into(), &"GetAll".into()); m.append_items(&[self.interface.to_string().into()]); let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms)); let reply = try!(r.as_result()).get_items(); (|| { if reply.len() != 1 { return Err(()) }; let mut t = BTreeMap::new(); let a: &[MessageItem] = try!(reply[0].inner()); for p in a.iter() { let (k, v) = try!(p.inner()); let (k, v): (&String, &MessageItem) = (try!(k.inner()), try!(v.inner())); t.insert(k.clone(), v.clone()); } Ok(t) })().map_err(|_| { let f = format!("Invalid reply for property GetAll: '{:?}'", reply); Error::new_custom("InvalidReply", &f) }) } } /// Wrapper around Props that keeps a map of fetched properties. pub struct PropHandler<'a> { p: Props<'a>, map: BTreeMap, } impl<'a> PropHandler<'a> { /// Create a new PropHandler from a Props. pub fn new(p: Props) -> PropHandler { PropHandler { p: p, map: BTreeMap::new() } } /// Get a map of all the properties' names and their values. pub fn get_all(&mut self) -> Result<(), Error> { self.map = try!(self.p.get_all()); Ok(()) } /// Get a mutable reference to the PropHandler's fetched properties. pub fn map_mut(&mut self) -> &mut BTreeMap { &mut self.map } /// Get a reference to the PropHandler's fetched properties. pub fn map(&self) -> &BTreeMap { &self.map } /// Get a single property's value. pub fn get(&mut self, propname: &str) -> Result<&MessageItem, Error> { let v = try!(self.p.get(propname)); self.map.insert(propname.to_string(), v); Ok(self.map.get(propname).unwrap()) } /// Set a single property's value. pub fn set(&mut self, propname: &str, value: MessageItem) -> Result<(), Error> { try!(self.p.set(propname, value.clone())); self.map.insert(propname.to_string(), value); Ok(()) } } /* Unfortunately org.freedesktop.DBus has no properties we can use for testing, but PolicyKit should be around on most distros. */ #[test] fn test_get_policykit_version() { use super::BusType; let c = Connection::get_private(BusType::System).unwrap(); let p = Props::new(&c, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", "org.freedesktop.PolicyKit1.Authority", 10000); /* Let's use both the get and getall methods and see if we get the same result */ let v = p.get("BackendVersion").unwrap(); let vall = p.get_all().unwrap(); let v2 = vall.get("BackendVersion").unwrap(); assert_eq!(&v, &*v2); match v { MessageItem::Str(ref s) => { println!("Policykit Backend version is {}", s); } _ => { panic!("Invalid Get: {:?}", v); } }; }