1 use super::{Connection, Message, MessageItem, Error, Path, Interface, BusName};
2 use std::collections::BTreeMap;
3
4 /// Client side properties - get and set properties on a remote application.
5 pub struct Props<'a> {
6 name: BusName<'a>,
7 path: Path<'a>,
8 interface: Interface<'a>,
9 timeout_ms: i32,
10 conn: &'a Connection,
11 }
12
13 impl<'a> Props<'a> {
14 /// Create a new Props.
new<N, P, I>(conn: &'a Connection, name: N, path: P, interface: I, timeout_ms: i32) -> Props<'a> where N: Into<BusName<'a>>, P: Into<Path<'a>>, I: Into<Interface<'a>>15 pub fn new<N, P, I>(conn: &'a Connection, name: N, path: P, interface: I, timeout_ms: i32) -> Props<'a>
16 where N: Into<BusName<'a>>, P: Into<Path<'a>>, I: Into<Interface<'a>> {
17 Props {
18 name: name.into(),
19 path: path.into(),
20 interface: interface.into(),
21 timeout_ms: timeout_ms,
22 conn: conn,
23 }
24 }
25
26 /// Get a single property's value.
get(&self, propname: &str) -> Result<MessageItem, Error>27 pub fn get(&self, propname: &str) -> Result<MessageItem, Error> {
28 let mut m = Message::method_call(&self.name, &self.path,
29 &"org.freedesktop.DBus.Properties".into(), &"Get".into());
30 m.append_items(&[self.interface.to_string().into(), propname.to_string().into()]);
31 let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms));
32 let reply = try!(r.as_result()).get_items();
33 if reply.len() == 1 {
34 if let &MessageItem::Variant(ref v) = &reply[0] {
35 return Ok((**v).clone())
36 }
37 }
38 let f = format!("Invalid reply for property get {}: '{:?}'", propname, reply);
39 return Err(Error::new_custom("InvalidReply", &f));
40 }
41
42 /// Set a single property's value.
set(&self, propname: &str, value: MessageItem) -> Result<(), Error>43 pub fn set(&self, propname: &str, value: MessageItem) -> Result<(), Error> {
44 let mut m = Message::method_call(&self.name, &self.path,
45 &"org.freedesktop.DBus.Properties".into(), &"Set".into());
46 m.append_items(&[self.interface.to_string().into(), propname.to_string().into(), Box::new(value).into()]);
47 let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms));
48 try!(r.as_result());
49 Ok(())
50 }
51
52 /// Get a map of all the properties' names and their values.
get_all(&self) -> Result<BTreeMap<String, MessageItem>, Error>53 pub fn get_all(&self) -> Result<BTreeMap<String, MessageItem>, Error> {
54 let mut m = Message::method_call(&self.name, &self.path,
55 &"org.freedesktop.DBus.Properties".into(), &"GetAll".into());
56 m.append_items(&[self.interface.to_string().into()]);
57 let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms));
58 let reply = try!(r.as_result()).get_items();
59
60 (|| {
61 if reply.len() != 1 { return Err(()) };
62 let mut t = BTreeMap::new();
63 let a: &[MessageItem] = try!(reply[0].inner());
64 for p in a.iter() {
65 let (k, v) = try!(p.inner());
66 let (k, v): (&String, &MessageItem) = (try!(k.inner()), try!(v.inner()));
67 t.insert(k.clone(), v.clone());
68 }
69 Ok(t)
70 })().map_err(|_| {
71 let f = format!("Invalid reply for property GetAll: '{:?}'", reply);
72 Error::new_custom("InvalidReply", &f)
73 })
74 }
75 }
76
77 /// Wrapper around Props that keeps a map of fetched properties.
78 pub struct PropHandler<'a> {
79 p: Props<'a>,
80 map: BTreeMap<String, MessageItem>,
81 }
82
83 impl<'a> PropHandler<'a> {
84 /// Create a new PropHandler from a Props.
new(p: Props) -> PropHandler85 pub fn new(p: Props) -> PropHandler {
86 PropHandler { p: p, map: BTreeMap::new() }
87 }
88
89 /// Get a map of all the properties' names and their values.
get_all(&mut self) -> Result<(), Error>90 pub fn get_all(&mut self) -> Result<(), Error> {
91 self.map = try!(self.p.get_all());
92 Ok(())
93 }
94
95 /// Get a mutable reference to the PropHandler's fetched properties.
map_mut(&mut self) -> &mut BTreeMap<String, MessageItem>96 pub fn map_mut(&mut self) -> &mut BTreeMap<String, MessageItem> { &mut self.map }
97
98 /// Get a reference to the PropHandler's fetched properties.
map(&self) -> &BTreeMap<String, MessageItem>99 pub fn map(&self) -> &BTreeMap<String, MessageItem> { &self.map }
100
101 /// Get a single property's value.
get(&mut self, propname: &str) -> Result<&MessageItem, Error>102 pub fn get(&mut self, propname: &str) -> Result<&MessageItem, Error> {
103 let v = try!(self.p.get(propname));
104 self.map.insert(propname.to_string(), v);
105 Ok(self.map.get(propname).unwrap())
106 }
107
108 /// Set a single property's value.
set(&mut self, propname: &str, value: MessageItem) -> Result<(), Error>109 pub fn set(&mut self, propname: &str, value: MessageItem) -> Result<(), Error> {
110 try!(self.p.set(propname, value.clone()));
111 self.map.insert(propname.to_string(), value);
112 Ok(())
113 }
114 }
115
116
117 /* Unfortunately org.freedesktop.DBus has no properties we can use for testing, but PolicyKit should be around on most distros. */
118 #[test]
test_get_policykit_version()119 fn test_get_policykit_version() {
120 use super::BusType;
121 let c = Connection::get_private(BusType::System).unwrap();
122 let p = Props::new(&c, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority",
123 "org.freedesktop.PolicyKit1.Authority", 10000);
124
125 /* Let's use both the get and getall methods and see if we get the same result */
126 let v = p.get("BackendVersion").unwrap();
127 let vall = p.get_all().unwrap();
128 let v2 = vall.get("BackendVersion").unwrap();
129
130 assert_eq!(&v, &*v2);
131 match v {
132 MessageItem::Str(ref s) => { println!("Policykit Backend version is {}", s); }
133 _ => { panic!("Invalid Get: {:?}", v); }
134 };
135 }
136
137