1 use super::{Connection, Message, MessageItem, Error, TypeSig};
2 use std::collections::BTreeMap;
3 use std::rc::Rc;
4 use std::cell::{Cell, RefCell};
5 use std::borrow::Cow;
6 
7 /// a Method has a list of Arguments.
8 pub struct Argument<'a> {
9     name: &'a str,
10     sig: TypeSig<'a>,
11 }
12 
13 impl<'a> Argument<'a> {
14     /// Create a new Argument.
new<T: Into<Cow<'a, str>>>(name: &'a str, sig: T) -> Argument<'a>15     pub fn new<T: Into<Cow<'a, str>>>(name: &'a str, sig: T) -> Argument<'a> {
16         Argument { name: name, sig: sig.into() }
17     }
18 }
19 
20 struct Annotation {
21     name: String,
22     value: String,
23 }
24 
25 struct ISignal<'a> {
26     args: Vec<Argument<'a>>,
27     anns: Vec<Annotation>,
28 }
29 
30 /// Declares that an Interface can send this signal
31 pub struct Signal<'a> {
32     name: String,
33     i: ISignal<'a>,
34 }
35 
36 impl<'a> Signal<'a> {
37     /// Create a new Signal.
new<N: ToString>(name: N, args: Vec<Argument<'a>>) -> Signal<'a>38     pub fn new<N: ToString>(name: N, args: Vec<Argument<'a>>) -> Signal<'a> {
39         Signal { name: name.to_string(), i: ISignal { args: args, anns: vec![] } }
40     }
41 
42     /// Add an Annotation to the Signal.
annotate<N: ToString, V: ToString>(&mut self, name: N, value: V)43     pub fn annotate<N: ToString, V: ToString>(&mut self, name: N, value: V) {
44         self.i.anns.push(Annotation { name: name.to_string(), value: value.to_string() });
45     }
46 }
47 
48 /// A method returns either a list of MessageItems, or an error - the tuple
49 /// represents the name and message of the Error.
50 pub type MethodResult = Result<Vec<MessageItem>, (&'static str, String)>;
51 /// Contains the retrieved MessageItem or an error tuple containing the
52 /// name and message of the error.
53 pub type PropertyGetResult = Result<MessageItem, (&'static str, String)>;
54 /// Contains () or an error tuple containing the name and message of
55 /// the error.
56 pub type PropertySetResult = Result<(), (&'static str, String)>;
57 
58 /// A boxed closure for dynamic dispatch. It is called when the method is
59 /// called by a remote application.
60 pub type MethodHandler<'a> = Box<FnMut(&mut Message) -> MethodResult + 'a>;
61 
62 struct IMethod<'a> {
63     in_args: Vec<Argument<'a>>,
64     out_args: Vec<Argument<'a>>,
65     cb: Rc<RefCell<MethodHandler<'a>>>,
66     anns: Vec<Annotation>,
67 }
68 
69 /// a method that can be called from another application
70 pub struct Method<'a> {
71     name: String,
72     i: IMethod<'a>
73 }
74 
75 impl<'a> Method<'a> {
76     /// Create a new Method.
new<N: ToString>(name: N, in_args: Vec<Argument<'a>>, out_args: Vec<Argument<'a>>, cb: MethodHandler<'a>) -> Method<'a>77     pub fn new<N: ToString>(name: N, in_args: Vec<Argument<'a>>,
78             out_args: Vec<Argument<'a>>, cb: MethodHandler<'a>) -> Method<'a> {
79         Method { name: name.to_string(), i: IMethod {
80             in_args: in_args, out_args: out_args, cb: Rc::new(RefCell::new(cb)), anns: vec![] }
81         }
82     }
83 
84     /// Add an Annotation to the Method.
annotate<N: ToString, V: ToString>(&mut self, name: N, value: V)85     pub fn annotate<N: ToString, V: ToString>(&mut self, name: N, value: V) {
86         self.i.anns.push(Annotation { name: name.to_string(), value: value.to_string() });
87     }
88 }
89 
90 /// A read/write property handler.
91 pub trait PropertyRWHandler {
92     /// Get a property's value.
get(&self) -> PropertyGetResult93     fn get(&self) -> PropertyGetResult;
94     /// Set a property's value.
set(&self, &MessageItem) -> PropertySetResult95     fn set(&self, &MessageItem) -> PropertySetResult;
96 }
97 
98 /// A read-only property handler.
99 pub trait PropertyROHandler {
100     /// Get a property's value.
get(&self) -> PropertyGetResult101     fn get(&self) -> PropertyGetResult;
102 }
103 
104 /// A write-only property handler.
105 pub trait PropertyWOHandler {
106     /// Set a property's value.
set(&self, &MessageItem) -> PropertySetResult107     fn set(&self, &MessageItem) -> PropertySetResult;
108 }
109 
110 /// Types of access to a Property.
111 pub enum PropertyAccess<'a> {
112     RO(Box<PropertyROHandler+'a>),
113     RW(Box<PropertyRWHandler+'a>),
114     WO(Box<PropertyWOHandler+'a>),
115 }
116 
117 struct IProperty<'a> {
118     sig: TypeSig<'a>,
119     access: PropertyAccess<'a>,
120     anns: Vec<Annotation>,
121 }
122 
123 /// Properties that a remote application can get/set.
124 pub struct Property<'a> {
125     name: String,
126     i: IProperty<'a>
127 }
128 
129 impl<'a> Property<'a> {
new<N: ToString>(name: N, sig: TypeSig<'a>, a: PropertyAccess<'a>) -> Property<'a>130     fn new<N: ToString>(name: N, sig: TypeSig<'a>, a: PropertyAccess<'a>) -> Property<'a> {
131         Property { name: name.to_string(), i: IProperty { sig: sig, access: a, anns: vec![] } }
132     }
133     /// Creates a new read-only Property
new_ro<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyROHandler+'a>) -> Property<'a>134     pub fn new_ro<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyROHandler+'a>) -> Property<'a> {
135         Property::new(name, sig, PropertyAccess::RO(h))
136     }
137     /// Creates a new read-write Property
new_rw<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyRWHandler+'a>) -> Property<'a>138     pub fn new_rw<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyRWHandler+'a>) -> Property<'a> {
139         Property::new(name, sig, PropertyAccess::RW(h))
140     }
141     /// Creates a new write-only Property
new_wo<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyWOHandler+'a>) -> Property<'a>142     pub fn new_wo<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyWOHandler+'a>) -> Property<'a> {
143         Property::new(name, sig, PropertyAccess::WO(h))
144     }
145     /// Add an annotation to the Property
annotate<N: ToString, V: ToString>(&mut self, name: N, value: V)146     pub fn annotate<N: ToString, V: ToString>(&mut self, name: N, value: V) {
147         self.i.anns.push(Annotation { name: name.to_string(), value: value.to_string() })
148     }
149 }
150 
151 /// Interfaces can contain Methods, Properties, and Signals.
152 pub struct Interface<'a> {
153     methods: BTreeMap<String, IMethod<'a>>,
154     properties: BTreeMap<String, IProperty<'a>>,
155     signals: BTreeMap<String, ISignal<'a>>,
156 }
157 
158 impl<'a> Interface<'a> {
159     /// Create a new Interface.
new(m: Vec<Method<'a>>, p: Vec<Property<'a>>, s: Vec<Signal<'a>>) -> Interface<'a>160     pub fn new(m: Vec<Method<'a>>, p: Vec<Property<'a>>, s: Vec<Signal<'a>>) -> Interface<'a> {
161         Interface {
162            methods: m.into_iter().map(|m| (m.name, m.i)).collect(),
163            properties: p.into_iter().map(|p| (p.name, p.i)).collect(),
164            signals: s.into_iter().map(|s| (s.name, s.i)).collect(),
165         }
166     }
167 }
168 
169 struct IObjectPath<'a> {
170     conn: &'a Connection,
171     path: String,
172     registered: Cell<bool>,
173     interfaces: RefCell<BTreeMap<String, Interface<'a>>>,
174 }
175 
176 /// Represents a D-Bus object path, which can in turn contain Interfaces.
177 pub struct ObjectPath<'a> {
178     // We need extra references for the introspector and property handlers, hence this extra boxing
179     i: Rc<IObjectPath<'a>>,
180 }
181 
182 impl<'a> Drop for ObjectPath<'a> {
drop(&mut self)183     fn drop(&mut self) {
184         let _ = self.i.set_registered(false);
185         self.i.interfaces.borrow_mut().clear(); // This should remove all the other references to i
186     }
187 }
188 
introspect_args(args: &Vec<Argument>, indent: &str, dir: &str) -> String189 fn introspect_args(args: &Vec<Argument>, indent: &str, dir: &str) -> String {
190     args.iter().fold("".to_string(), |aa, az| {
191         format!("{}{}<arg name=\"{}\" type=\"{}\"{}/>\n", aa, indent, az.name, az.sig, dir)
192     })
193 }
194 
introspect_anns(anns: &Vec<Annotation>, indent: &str) -> String195 fn introspect_anns(anns: &Vec<Annotation>, indent: &str) -> String {
196     anns.iter().fold("".to_string(), |aa, az| {
197         format!("{}{}<annotation name=\"{}\" value=\"{}\"/>\n", aa, indent, az.name, az.value)
198     })
199 }
200 
introspect_map<T, C: Fn(&T) -> (String, String)> (h: &BTreeMap<String, T>, name: &str, indent: &str, func: C) -> String201 fn introspect_map<T, C: Fn(&T) -> (String, String)>
202     (h: &BTreeMap<String, T>, name: &str, indent: &str, func: C) -> String {
203 
204     h.iter().fold("".to_string(), |a, (k, v)| {
205         let (params, contents) = func(v);
206         format!("{}{}<{} name=\"{}\"{}{}>\n",
207             a, indent, name, k, params, if contents.len() > 0 {
208                 format!(">\n{}{}</{}", contents, indent, name)
209             }
210             else { format!("/") }
211         )
212     })
213 }
214 
215 impl<'a> IObjectPath<'a> {
216 
set_registered(&self, register: bool) -> Result<(), Error>217     fn set_registered(&self, register: bool) -> Result<(), Error> {
218         if register == self.registered.get() { return Ok(()) };
219         if register {
220             try!(self.conn.register_object_path(&self.path));
221         } else {
222             self.conn.unregister_object_path(&self.path);
223         }
224         self.registered.set(register);
225         Ok(())
226     }
227 
introspect(&self, _: &mut Message) -> MethodResult228     fn introspect(&self, _: &mut Message) -> MethodResult {
229         let ifacestr = introspect_map(&self.interfaces.borrow(), "interface", "  ", |iv|
230             (format!(""), format!("{}{}{}",
231                 introspect_map(&iv.methods, "method", "    ", |m| (format!(""), format!("{}{}{}",
232                     introspect_args(&m.in_args, "      ", " direction=\"in\""),
233                     introspect_args(&m.out_args, "      ", " direction=\"out\""),
234                     introspect_anns(&m.anns, "      ")
235                 ))),
236                 introspect_map(&iv.properties, "property", "    ", |p| (
237                     format!(" type=\"{}\" access=\"{}\"", p.sig, match p.access {
238                         PropertyAccess::RO(_) => "read",
239                         PropertyAccess::RW(_) => "readwrite",
240                         PropertyAccess::WO(_) => "write",
241                     }),
242                     introspect_anns(&p.anns, "      ")
243                 )),
244                 introspect_map(&iv.signals, "signal", "    ", |s| (format!(""), format!("{}{}",
245                     introspect_args(&s.args, "      ", ""),
246                     introspect_anns(&s.anns, "      ")
247                 )))
248             ))
249         );
250         let childstr = self.conn.list_registered_object_paths(&self.path).iter().fold("".to_string(), |na, n|
251             format!(r##"{}  <node name="{}"/>
252 "##, na, n)
253         );
254         let nodestr = format!(r##"<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
255 <node name="{}">
256 {}{}</node>"##, self.path, ifacestr, childstr);
257 
258         Ok(vec!(MessageItem::Str(nodestr)))
259     }
260 
property_get(&self, msg: &mut Message) -> MethodResult261     fn property_get(&self, msg: &mut Message) -> MethodResult {
262         let items = msg.get_items();
263         let iface_name = try!(parse_msg_str(items.get(0)));
264         let prop_name = try!(parse_msg_str(items.get(1)));
265 
266         let is = self.interfaces.borrow();
267         let i = try!(is.get(iface_name).ok_or_else(||
268             ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name))));
269         let p = try!(i.properties.get(prop_name).ok_or_else(||
270             ("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", prop_name))));
271         let v = try!(match p.access {
272             PropertyAccess::RO(ref cb) => cb.get(),
273             PropertyAccess::RW(ref cb) => cb.get(),
274             PropertyAccess::WO(_) => {
275                 return Err(("org.freedesktop.DBus.Error.Failed", format!("Property {} is write only", prop_name)))
276             }
277         });
278         Ok(vec!(MessageItem::Variant(Box::new(v))))
279     }
280 
property_getall(&self, msg: &mut Message) -> MethodResult281     fn property_getall(&self, msg: &mut Message) -> MethodResult {
282         let items = msg.get_items();
283         let iface_name = try!(parse_msg_str(items.get(0)));
284 
285         let is = self.interfaces.borrow();
286         let i = try!(is.get(iface_name).ok_or_else(||
287             ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name))));
288         let mut result = Vec::new();
289         result.push(try!(MessageItem::from_dict(i.properties.iter().filter_map(|(pname, pv)| {
290             let v = match pv.access {
291                 PropertyAccess::RO(ref cb) => cb.get(),
292                 PropertyAccess::RW(ref cb) => cb.get(),
293                 PropertyAccess::WO(_) => { return None }
294             };
295             Some(v.map(|vv| (pname.clone(),vv)))
296         }))));
297         Ok(result)
298     }
299 
property_set(&self, msg: &mut Message) -> MethodResult300     fn property_set(&self, msg: &mut Message) -> MethodResult {
301         let items = msg.get_items();
302         let iface_name = try!(parse_msg_str(items.get(0)));
303         let prop_name = try!(parse_msg_str(items.get(1)));
304         let value = try!(parse_msg_variant(items.get(2)));
305 
306         let is = self.interfaces.borrow();
307         let i = try!(is.get(iface_name).ok_or_else(||
308             ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name))));
309         let p = try!(i.properties.get(prop_name).ok_or_else(||
310             ("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", prop_name))));
311         try!(match p.access {
312             PropertyAccess::WO(ref cb) => cb.set(value),
313             PropertyAccess::RW(ref cb) => cb.set(value),
314             PropertyAccess::RO(_) => {
315                 return Err(("org.freedesktop.DBus.Error.PropertyReadOnly", format!("Property {} is read only", prop_name)))
316             }
317         });
318         Ok(vec!())
319     }
320 }
321 
parse_msg_str(a: Option<&MessageItem>) -> Result<&str,(&'static str, String)>322 fn parse_msg_str(a: Option<&MessageItem>) -> Result<&str,(&'static str, String)> {
323     let name = try!(a.ok_or_else(|| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a))));
324     name.inner().map_err(|_| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a)))
325 }
326 
parse_msg_variant(a: Option<&MessageItem>) -> Result<&MessageItem,(&'static str, String)>327 fn parse_msg_variant(a: Option<&MessageItem>) -> Result<&MessageItem,(&'static str, String)> {
328     let name = try!(a.ok_or_else(|| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a))));
329     name.inner().map_err(|_| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a)))
330 }
331 
332 impl PropertyROHandler for MessageItem {
get(&self) -> PropertyGetResult333     fn get(&self) -> PropertyGetResult {
334         Ok(self.clone())
335     }
336 }
337 
338 impl<'a> ObjectPath<'a> {
339     /// Create a new ObjectPath.
new(conn: &'a Connection, path: &str, introspectable: bool) -> ObjectPath<'a>340     pub fn new(conn: &'a Connection, path: &str, introspectable: bool) -> ObjectPath<'a> {
341         let i = IObjectPath {
342             conn: conn,
343             path: path.to_string(),
344             registered: Cell::new(false),
345             interfaces: RefCell::new(BTreeMap::new()),
346         };
347         let mut o = ObjectPath { i: Rc::new(i) };
348 
349         if introspectable {
350             let o_cl = o.i.clone();
351             let i = Interface::new(vec!(
352                 Method::new("Introspect", vec!(), vec!(Argument::new("xml_data", "s")),
353                     Box::new(move |m| { o_cl.introspect(m) }))), vec!(), vec!());
354             o.insert_interface("org.freedesktop.DBus.Introspectable", i);
355         }
356         o
357     }
358 
add_property_handler(&mut self)359     fn add_property_handler(&mut self) {
360         if self.i.interfaces.borrow().contains_key("org.freedesktop.DBus.Properties") { return };
361         let (cl1, cl2, cl3) = (self.i.clone(), self.i.clone(), self.i.clone());
362         let i = Interface::new(vec!(
363             Method::new("Get",
364                 vec!(Argument::new("interface_name", "s"), Argument::new("property_name", "s")),
365                 vec!(Argument::new("value", "v")),
366                 Box::new(move |m| cl1.property_get(m))),
367             Method::new("GetAll",
368                 vec!(Argument::new("interface_name", "s")),
369                 vec!(Argument::new("props", "a{sv}")),
370                 Box::new(move |m| cl2.property_getall(m))),
371             Method::new("Set",
372                 vec!(Argument::new("interface_name", "s"), Argument::new("property_name", "s"),
373                     Argument::new("value", "v")),
374                 vec!(),
375                 Box::new(move |m| cl3.property_set(m)))),
376             vec!(), vec!());
377         self.insert_interface("org.freedesktop.DBus.Properties", i);
378     }
379 
380     /// Add an Interface to this ObjectPath.
insert_interface<N: ToString>(&mut self, name: N, i: Interface<'a>)381     pub fn insert_interface<N: ToString>(&mut self, name: N, i: Interface<'a>) {
382         if !i.properties.is_empty() {
383             self.add_property_handler();
384         }
385         self.i.interfaces.borrow_mut().insert(name.to_string(), i);
386     }
387 
388     /// Returns if the ObjectPath is registered.
is_registered(&self) -> bool389     pub fn is_registered(&self) -> bool {
390         self.i.registered.get()
391     }
392 
393     /// Changes the registration status of the ObjectPath.
set_registered(&mut self, register: bool) -> Result<(), Error>394     pub fn set_registered(&mut self, register: bool) -> Result<(), Error> {
395         self.i.set_registered(register)
396     }
397 
398     /// Handles a method call if the object path matches.
399     /// Return value: None => not handled (no match),
400     /// Some(Err(())) => message reply send failed,
401     /// Some(Ok()) => message reply send ok */
handle_message(&mut self, msg: &mut Message) -> Option<Result<(), ()>>402     pub fn handle_message(&mut self, msg: &mut Message) -> Option<Result<(), ()>> {
403         let (_, path, iface, method) = msg.headers();
404         if path.is_none() || path.unwrap() != self.i.path { return None; }
405         if iface.is_none() { return None; }
406 
407         let method = {
408             // This is because we don't want to hold the refcell lock when we call the
409             // callback - maximum flexibility for clients.
410             if let Some(i) = self.i.interfaces.borrow().get(&iface.unwrap()) {
411                 if let Some(Some(m)) = method.map(|m| i.methods.get(&m)) {
412                     m.cb.clone()
413                 } else {
414                     return Some(self.i.conn.send(Message::new_error(
415                         msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unknown method").unwrap()).map(|_| ()));
416                 }
417             } else {
418                 return Some(self.i.conn.send(Message::new_error(msg,
419                     "org.freedesktop.DBus.Error.UnknownInterface", "Unknown interface").unwrap()).map(|_| ()));
420             }
421         };
422 
423         let r = {
424             // Now call it
425             let mut m = method.borrow_mut();
426             (&mut **m)(msg)
427         };
428 
429         let reply = match r {
430             Ok(r) => {
431                 let mut z = Message::new_method_return(msg).unwrap();
432                 z.append_items(&r);
433                 z
434             },
435             Err((aa,bb)) => Message::new_error(msg, aa, &bb).unwrap(),
436         };
437 
438         Some(self.i.conn.send(reply).map(|_| ()))
439     }
440 }
441 
442 #[cfg(test)]
make_objpath<'a>(c: &'a Connection) -> ObjectPath<'a>443 fn make_objpath<'a>(c: &'a Connection) -> ObjectPath<'a> {
444     let mut o = ObjectPath::new(c, "/echo", true);
445     o.insert_interface("com.example.echo", Interface::new(
446         vec!(Method::new("Echo",
447             vec!(Argument::new("request", "s")),
448             vec!(Argument::new("reply", "s")), Box::new(|_| { Err(("dummy", "dummy".to_string())) } ))),
449         vec!(Property::new_ro("EchoCount", MessageItem::Int32(7).type_sig(), Box::new(MessageItem::Int32(7)))),
450         vec!(Signal::new("Echoed", vec!(Argument::new("data", "s"))))));
451     o
452 }
453 
454 #[test]
test_objpath()455 fn test_objpath() {
456     let c = Connection::get_private(super::BusType::Session).unwrap();
457     let mut o = make_objpath(&c);
458     o.set_registered(true).unwrap();
459     let busname = format!("com.example.objpath.test.test_objpath");
460     assert_eq!(c.register_name(&busname, super::NameFlag::ReplaceExisting as u32).unwrap(), super::RequestNameReply::PrimaryOwner);
461 
462     let thread = ::std::thread::spawn(move || {
463         let c = Connection::get_private(super::BusType::Session).unwrap();
464         let pr = super::Props::new(&c, &*busname, "/echo", "com.example.echo", 5000);
465         assert_eq!(pr.get("EchoCount").unwrap(), 7i32.into());
466         let m = pr.get_all().unwrap();
467         assert_eq!(m.get("EchoCount").unwrap(), &7i32.into());
468     });
469 
470     let mut i = 0;
471     for n in c.iter(1000) {
472         println!("objpath msg {:?}", n);
473         if let super::ConnectionItem::MethodCall(mut m) = n {
474             if let Some(msg) = o.handle_message(&mut m) {
475                 msg.unwrap();
476                 i += 1;
477                 if i >= 2 { break };
478             }
479         }
480     }
481 
482     thread.join().unwrap();
483 }
484 
485 
486 /// Currently commented out because it requires feature(alloc)
487 /*
488 #[test]
489 fn test_refcount() {
490     let c = Connection::get_private(super::BusType::Session).unwrap();
491     let i = {
492         let o = make_objpath(&c);
493         o.i.clone()
494     };
495     assert!(::std::rc::is_unique(&i));
496 }
497 */
498 
499 #[test]
test_introspect()500 fn test_introspect() {
501     let c = Connection::get_private(super::BusType::Session).unwrap();
502     let mut o = make_objpath(&c);
503     o.set_registered(true).unwrap();
504     let mut o2 = ObjectPath::new(&c, "/echo/subpath", true);
505     o2.set_registered(true).unwrap();
506     let mut msg = Message::new_method_call("com.example.echoserver", "/echo", "org.freedesktop.DBus.Introspectable", "Introspect").unwrap();
507     println!("Introspect result: {}", parse_msg_str(o.i.introspect(&mut msg).unwrap().get(0)).unwrap());
508 
509     let result = r##"<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
510 <node name="/echo">
511   <interface name="com.example.echo">
512     <method name="Echo">
513       <arg name="request" type="s" direction="in"/>
514       <arg name="reply" type="s" direction="out"/>
515     </method>
516     <property name="EchoCount" type="i" access="read"/>
517     <signal name="Echoed">
518       <arg name="data" type="s"/>
519     </signal>
520   </interface>
521   <interface name="org.freedesktop.DBus.Introspectable">
522     <method name="Introspect">
523       <arg name="xml_data" type="s" direction="out"/>
524     </method>
525   </interface>
526   <interface name="org.freedesktop.DBus.Properties">
527     <method name="Get">
528       <arg name="interface_name" type="s" direction="in"/>
529       <arg name="property_name" type="s" direction="in"/>
530       <arg name="value" type="v" direction="out"/>
531     </method>
532     <method name="GetAll">
533       <arg name="interface_name" type="s" direction="in"/>
534       <arg name="props" type="a{sv}" direction="out"/>
535     </method>
536     <method name="Set">
537       <arg name="interface_name" type="s" direction="in"/>
538       <arg name="property_name" type="s" direction="in"/>
539       <arg name="value" type="v" direction="in"/>
540     </method>
541   </interface>
542   <node name="subpath"/>
543 </node>"##;
544 
545     assert_eq!(result, parse_msg_str(o.i.introspect(&mut msg).unwrap().get(0)).unwrap());
546 
547 }
548 
549