1use super::utils::{ArcMap, Iter, IterE, Annotations, Introspect};
2use super::{Factory, MethodType, MethodInfo, MethodResult, MethodErr, DataType, Property, Method, Signal, methodtype};
3use std::sync::{Arc, Mutex};
4use dbus::{Message, MessageType, Error, arg, message, channel};
5use dbus::strings::{Member, Path, Signature, Interface as IfaceName};
6use dbus::ffidisp::{ConnectionItem, MsgHandler, Connection, MsgHandlerType, MsgHandlerResult};
7use std::fmt;
8use super::leaves::prop_append_dict;
9use dbus::channel::Channel;
10use std::time::Duration;
11
12fn introspect_map<I: fmt::Display, T: Introspect>
13 (h: &ArcMap<I, T>, indent: &str) -> String {
14
15 h.iter().fold("".into(), |a, (k, v)| {
16 let (name, params, contents) = (v.xml_name(), v.xml_params(), v.xml_contents());
17 format!("{}{}<{} name=\"{}\"{}{}>\n",
18 a, indent, name, &*k, params, if !contents.is_empty() {
19 format!(">\n{}{}</{}", contents, indent, name)
20 }
21 else { "/".to_string() }
22 )
23 })
24}
25
26#[derive(Debug)]
27pub struct Interface<M: MethodType<D>, D: DataType> {
29 name: Arc<IfaceName<'static>>,
30 methods: ArcMap<Member<'static>, Method<M, D>>,
31 signals: ArcMap<Member<'static>, Signal<D>>,
32 properties: ArcMap<String, Property<M, D>>,
33 anns: Annotations,
34 data: D::Interface,
35}
36
37impl<M: MethodType<D>, D: DataType> Interface<M, D> {
38 pub fn add_m<I: Into<Arc<Method<M, D>>>>(mut self, m: I) -> Self {
40 let m = m.into();
41 self.methods.insert(m.get_name().clone(), m);
42 self
43 }
44
45 pub fn add_s<I: Into<Arc<Signal<D>>>>(mut self, s: I) -> Self {
47 let m = s.into();
48 self.signals.insert(m.get_name().clone(), m);
49 self
50 }
51
52 pub fn add_p<I: Into<Arc<Property<M, D>>>>(mut self, p: I) -> Self {
54 let m = p.into();
55 self.properties.insert(m.get_name().to_owned(), m);
56 self
57 }
58
59 pub fn annotate<N: Into<String>, V: Into<String>>(mut self, name: N, value: V) -> Self {
61 self.anns.insert(name, value); self
62 }
63
64 pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") }
66
67 pub fn get_name(&self) -> &IfaceName<'static> { &self.name }
69
70 pub fn get_data(&self) -> &D::Interface { &self.data }
72
73 pub fn iter_m<'a>(&'a self) -> Iter<'a, Method<M, D>> { IterE::Member(self.methods.values()).into() }
75
76 pub fn iter_s<'a>(&'a self) -> Iter<'a, Signal<D>> { IterE::Member(self.signals.values()).into() }
78
79 pub fn iter_p<'a>(&'a self) -> Iter<'a, Property<M, D>> { IterE::String(self.properties.values()).into() }
81}
82
83impl<M: MethodType<D>, D: DataType> Introspect for Interface<M, D> {
84 fn xml_name(&self) -> &'static str { "interface" }
85 fn xml_params(&self) -> String { String::new() }
86 fn xml_contents(&self) -> String {
87 format!("{}{}{}{}",
88 introspect_map(&self.methods, " "),
89 introspect_map(&self.properties, " "),
90 introspect_map(&self.signals, " "),
91 self.anns.introspect(" "))
92 }
93}
94
95
96pub fn new_interface<M: MethodType<D>, D: DataType>(t: IfaceName<'static>, d: D::Interface) -> Interface<M, D> {
97 Interface { name: Arc::new(t), methods: ArcMap::new(), signals: ArcMap::new(),
98 properties: ArcMap::new(), anns: Annotations::new(), data: d
99 }
100}
101
102
103#[derive(Debug)]
104pub struct IfaceCache<M: MethodType<D>, D: DataType>(Mutex<ArcMap<IfaceName<'static>, Interface<M, D>>>);
106
107impl<M: MethodType<D>, D: DataType> IfaceCache<M, D>
108where D::Interface: Default {
109 pub fn get<S: Into<IfaceName<'static>> + Clone, F>(&self, s: S, f: F) -> Arc<Interface<M, D>>
110 where F: FnOnce(Interface<M, D>) -> Interface<M, D> {
111 let s2 = s.clone().into();
112 let mut m = self.0.lock().unwrap();
113 m.entry(s2).or_insert_with(|| {
114 let i = new_interface(s.into(), Default::default());
115 Arc::new(f(i))
116 }).clone()
117 }
118}
119
120impl<M: MethodType<D>, D: DataType> IfaceCache<M, D> {
121 pub fn get_factory<S: Into<IfaceName<'static>> + Clone, F>(&self, s: S, f: F) -> Arc<Interface<M, D>>
122 where F: FnOnce() -> Interface<M, D> {
123 let s2 = s.clone().into();
124 let mut m = self.0.lock().unwrap();
125 m.entry(s2).or_insert_with(|| {
126 Arc::new(f())
127 }).clone()
128 }
129
130
131 pub fn new() -> Arc<Self> { Arc::new(IfaceCache(Mutex::new(ArcMap::new()))) }
132}
133
134#[derive(Debug)]
135pub struct ObjectPath<M: MethodType<D>, D: DataType> {
137 name: Arc<Path<'static>>,
138 default_iface: Option<IfaceName<'static>>,
139 ifaces: ArcMap<Arc<IfaceName<'static>>, Interface<M, D>>,
140 ifacecache: Arc<IfaceCache<M, D>>,
141 data: D::ObjectPath,
142}
143
144impl<M: MethodType<D>, D: DataType> ObjectPath<M, D> {
145
146 pub fn get_name(&self) -> &Path<'static> { &self.name }
148
149 pub fn get_data(&self) -> &D::ObjectPath { &self.data }
151
152 pub fn iter<'a>(&'a self) -> Iter<'a, Interface<M, D>> { IterE::Iface(self.ifaces.values()).into() }
154
155 pub(super) fn introspect(&self, tree: &Tree<M, D>) -> String {
156 let ifacestr = introspect_map(&self.ifaces, " ");
157 let olen = if &**self.name == "/" { 1 } else { self.name.len()+1 };
158 let childstr = tree.children(self, true).iter().fold("".to_string(), |na, n|
159 format!("{} <node name=\"{}\"/>\n", na, &n.name[olen..])
160 );
161
162 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">
163<node name="{}">
164{}{}</node>"##, self.name, ifacestr, childstr);
165 nodestr
166 }
167
168 fn get_iface<'a>(&'a self, iface_name: &'a str) -> Result<&Arc<Interface<M, D>>, MethodErr> {
169 let j = IfaceName::from_slice(iface_name).map_err(|e| MethodErr::invalid_arg(&e))?;
170 self.ifaces.get(&j).ok_or_else(|| MethodErr::no_interface(&j))
171 }
172
173 fn prop_get(&self, m: &MethodInfo<M, D>) -> MethodResult {
174 let (iname, prop_name): (&str, &str) = m.msg.read2()?;
175 let iface = self.get_iface(iname)?;
176 let prop: &Property<M, D> = iface.properties.get(&String::from(prop_name))
177 .ok_or_else(|| MethodErr::no_property(&prop_name))?;
178 prop.can_get()?;
179 let mut mret = m.msg.method_return();
180 {
181 let mut iter = arg::IterAppend::new(&mut mret);
182 let pinfo = m.to_prop_info(iface, prop);
183 prop.get_as_variant(&mut iter, &pinfo)?;
184 }
185 Ok(vec!(mret))
186 }
187
188 fn prop_get_all(&self, m: &MethodInfo<M, D>) -> MethodResult {
189 let iface = self.get_iface(m.msg.read1()?)?;
190 let mut mret = m.msg.method_return();
191 prop_append_dict(&mut arg::IterAppend::new(&mut mret),
192 iface.properties.values().map(|v| &**v), m)?;
193 Ok(vec!(mret))
194 }
195
196
197 fn prop_set(&self, m: &MethodInfo<M, D>) -> MethodResult {
198 let (iname, prop_name): (&str, &str) = m.msg.read2()?;
199 let iface = self.get_iface(iname)?;
200 let prop: &Property<M, D> = iface.properties.get(&String::from(prop_name))
201 .ok_or_else(|| MethodErr::no_property(&prop_name))?;
202
203 let mut iter = arg::Iter::new(m.msg);
204 iter.next(); iter.next();
205 let mut iter2 = iter;
206 prop.can_set(Some(iter))?;
207
208 let pinfo = m.to_prop_info(iface, prop);
209 let mut r: Vec<Message> = prop.set_as_variant(&mut iter2, &pinfo)?.into_iter().collect();
210 r.push(m.msg.method_return());
211 Ok(r)
212
213 }
214
215 fn get_managed_objects(&self, m: &MethodInfo<M, D>) -> MethodResult {
216 use dbus::arg::{Dict, Variant};
217 let paths = m.tree.children(&self, false);
218 let mut result = Ok(());
219 let mut r = m.msg.method_return();
220 {
221 let mut i = arg::IterAppend::new(&mut r);
222 i.append_dict(&Signature::make::<Path>(), &Signature::make::<Dict<&str,Dict<&str,Variant<()>,()>,()>>(), |ii| {
223 for p in paths {
224 ii.append_dict_entry(|pi| {
225 pi.append(&*p.name);
226 pi.append_dict(&Signature::make::<&str>(), &Signature::make::<Dict<&str,Variant<()>,()>>(), |pii| {
227 for ifaces in p.ifaces.values() {
228 let m2 = MethodInfo { msg: m.msg, path: p, iface: ifaces, tree: m.tree, method: m.method };
229 pii.append_dict_entry(|ppii| {
230 ppii.append(&**ifaces.name);
231 result = prop_append_dict(ppii, ifaces.properties.values().map(|v| &**v), &m2);
232 });
233 if result.is_err() { break; }
234 }
235 });
236 });
237 if result.is_err() { break; }
238 }
239 });
240 }
241 result?;
242 Ok(vec!(r))
243 }
244
245 fn handle(&self, m: &Message, t: &Tree<M, D>) -> MethodResult {
246 let iname = m.interface().or_else(|| { self.default_iface.clone() });
247 let i = iname.and_then(|i| self.ifaces.get(&i)).ok_or_else(|| MethodErr::no_interface(&""))?;
248 let me = m.member().and_then(|me| i.methods.get(&me)).ok_or_else(|| MethodErr::no_method(&""))?;
249 let minfo = MethodInfo { msg: m, tree: t, path: self, iface: i, method: me };
250 me.call(&minfo)
251 }
252
253}
254
255impl<M: MethodType<D>, D: DataType> ObjectPath<M, D>
256where <D as DataType>::Interface: Default,
257 <D as DataType>::Method: Default,
258 <D as DataType>::Signal: Default
259{
260 pub fn introspectable(self) -> Self {
262 let z = self.ifacecache.get_factory("org.freedesktop.DBus.Introspectable", || {
263 let f = Factory::from(self.ifacecache.clone());
264 methodtype::org_freedesktop_dbus_introspectable_server(&f, Default::default())
265 });
266 self.add(z)
267 }
268
269 pub fn add<I: Into<Arc<Interface<M, D>>>>(mut self, s: I) -> Self {
271 let m = s.into();
272 if !m.properties.is_empty() { self.add_property_handler(); }
273 self.ifaces.insert(m.name.clone(), m);
274 self
275 }
276
277 pub fn default_interface(mut self, i: IfaceName<'static>) -> Self {
280 self.default_iface = Some(i);
281 self
282 }
283
284 pub fn object_manager(mut self) -> Self {
289 use dbus::arg::{Variant, Dict};
290 let ifname = IfaceName::from("org.freedesktop.DBus.ObjectManager");
291 if self.ifaces.contains_key(&ifname) { return self };
292 let z = self.ifacecache.get(ifname, |i| {
293 i.add_m(super::leaves::new_method("GetManagedObjects".into(), Default::default(),
294 M::make_method(|m| m.path.get_managed_objects(m)))
295 .outarg::<Dict<Path,Dict<&str,Dict<&str,Variant<()>,()>,()>,()>,_>("objpath_interfaces_and_properties"))
296 });
297 self.ifaces.insert(z.name.clone(), z);
298 self
299 }
300
301 fn add_property_handler(&mut self) {
302 use dbus::arg::{Variant, Dict};
303 let ifname = IfaceName::from("org.freedesktop.DBus.Properties");
304 if self.ifaces.contains_key(&ifname) { return };
305 let z = self.ifacecache.get(ifname, |i| {
306 i.add_m(super::leaves::new_method("Get".into(), Default::default(),
307 M::make_method(|m| m.path.prop_get(m)))
308 .inarg::<&str,_>("interface_name")
309 .inarg::<&str,_>("property_name")
310 .outarg::<Variant<()>,_>("value"))
311 .add_m(super::leaves::new_method("GetAll".into(), Default::default(),
312 M::make_method(|m| m.path.prop_get_all(m)))
313 .inarg::<&str,_>("interface_name")
314 .outarg::<Dict<&str, Variant<()>, ()>,_>("props"))
315 .add_m(super::leaves::new_method("Set".into(), Default::default(),
316 M::make_method(|m| m.path.prop_set(m)))
317 .inarg::<&str,_>("interface_name")
318 .inarg::<&str,_>("property_name")
319 .inarg::<Variant<bool>,_>("value"))
320 .add_s(super::leaves::new_signal("PropertiesChanged".into(), Default::default())
321 .sarg::<&str, _>("interface_name")
322 .sarg::<Dict<&str, Variant<()>, ()>, _>("changed_properties")
323 .sarg::<Vec<&str>, _>("invalidated_properties"))
324 });
325 self.ifaces.insert(z.name.clone(), z);
326 }
327}
328
329pub fn new_objectpath<M: MethodType<D>, D: DataType>(n: Path<'static>, d: D::ObjectPath, cache: Arc<IfaceCache<M, D>>)
330 -> ObjectPath<M, D> {
331 ObjectPath { name: Arc::new(n), data: d, ifaces: ArcMap::new(), ifacecache: cache, default_iface: None }
332}
333
334
335#[derive(Debug, Default)]
337pub struct Tree<M: MethodType<D>, D: DataType> {
338 paths: ArcMap<Arc<Path<'static>>, ObjectPath<M, D>>,
339 data: D::Tree,
340}
341
342impl<M: MethodType<D>, D: DataType> Tree<M, D> {
343 pub fn add<I: Into<Arc<ObjectPath<M, D>>>>(mut self, s: I) -> Self {
345 self.insert(s);
346 self
347 }
348
349 pub fn get(&self, p: &Path<'static>) -> Option<&Arc<ObjectPath<M, D>>> {
351 self.paths.get(p)
352 }
353
354 pub fn iter<'a>(&'a self) -> Iter<'a, ObjectPath<M, D>> { IterE::Path(self.paths.values()).into() }
356
357 pub fn insert<I: Into<Arc<ObjectPath<M, D>>>>(&mut self, s: I) {
359 let m = s.into();
360 self.paths.insert(m.name.clone(), m);
361 }
362
363
364 pub fn remove(&mut self, p: &Path<'static>) -> Option<Arc<ObjectPath<M, D>>> {
366 self.paths.remove(p)
369 }
370
371 pub fn set_registered(&self, c: &Connection, b: bool) -> Result<(), Error> {
373 let mut regd_paths = Vec::new();
374 for p in self.paths.keys() {
375 if b {
376 match c.register_object_path(p) {
377 Ok(()) => regd_paths.push(p.clone()),
378 Err(e) => {
379 while let Some(rp) = regd_paths.pop() {
380 c.unregister_object_path(&rp);
381 }
382 return Err(e)
383 }
384 }
385 } else {
386 c.unregister_object_path(p);
387 }
388 }
389 Ok(())
390 }
391
392 pub fn run<'a, I: Iterator<Item=ConnectionItem>>(&'a self, c: &'a Connection, i: I) -> TreeServer<'a, I, M, D> {
395 TreeServer { iter: i, tree: &self, conn: c }
396 }
397
398 pub fn handle(&self, m: &Message) -> Option<Vec<Message>> {
403 if m.msg_type() != MessageType::MethodCall { None }
404 else { m.path().and_then(|p| self.paths.get(&p).map(|s| s.handle(m, &self)
405 .unwrap_or_else(|e| vec!(e.to_message(m))))) }
406 }
407
408 pub fn process_channel(&self, channel: &Channel, timeout: Duration) -> Result<bool, Error> {
411 if let Some(msg) = channel.blocking_pop_message(timeout)? {
412 if let Some(replies) = self.handle(&msg) {
413 for r in replies {
414 let _ = channel.send(r);
415 }
416 } else if let Some(reply) = dbus::channel::default_reply(&msg) {
417 let _ = channel.send(reply);
418 }
419
420 Ok(true)
421 } else {
422 Ok(false)
423 }
424 }
425
426
427 fn children(&self, o: &ObjectPath<M, D>, direct_only: bool) -> Vec<&ObjectPath<M, D>> {
428 let parent: &str = &o.name;
429 let plen = if parent == "/" { 1 } else { parent.len()+1 };
430 let mut r: Vec<&ObjectPath<M, D>> = self.paths.values().filter_map(|v| {
431 let k: &str = &v.name;
432 if !k.starts_with(parent) || k.len() <= plen || &k[plen-1..plen] != "/" {None} else {
433 Some(&**v)
434 }
435 }).collect();
436 if direct_only {
437 r.sort_by_key(|v| &**v.name);
438 let mut prev: Option<&ObjectPath<M, D>> = None;
440 r.retain(|v| {
441 let a = prev.map(|prev|
442 !(v.name.starts_with(&**prev.name) && v.name.as_bytes().get(prev.name.len()) == Some(&b'/'))
443 ).unwrap_or(true);
444 if a { prev = Some(v); }
445 a
446 });
447 }
448 r
449 }
450
451 pub fn get_data(&self) -> &D::Tree { &self.data }
453}
454
455impl<M: MethodType<D> + 'static, D: DataType + 'static> Tree<M, D> {
456 pub fn start_receive_sync<C>(self, connection: &C)
460 where
461 C: channel::MatchingReceiver<F=Box<dyn FnMut(Message, &C) -> bool + Send + Sync>> + channel::Sender,
462 D::Tree: Send + Sync, D::ObjectPath: Send + Sync, D::Interface: Send + Sync,
463 D::Property: Send + Sync, D::Method: Send + Sync, D::Signal: Send + Sync,
464 M::Method: Send + Sync, M::GetProp: Send + Sync, M::SetProp: Send + Sync,
465 {
466 connection.start_receive(message::MatchRule::new_method_call(), Box::new(move |msg, c| {
467 if let Some(replies) = self.handle(&msg) {
468 for r in replies { let _ = c.send(r); }
469 }
470 true
471 }));
472 }
473
474 pub fn start_receive_send<C>(self, connection: &C)
478 where
479 C: channel::MatchingReceiver<F=Box<dyn FnMut(Message, &C) -> bool + Send>> + channel::Sender,
480 D::Tree: Send + Sync, D::ObjectPath: Send + Sync, D::Interface: Send + Sync,
481 D::Property: Send + Sync, D::Method: Send + Sync, D::Signal: Send + Sync,
482 M::Method: Send + Sync, M::GetProp: Send + Sync, M::SetProp: Send + Sync,
483 {
484 connection.start_receive(message::MatchRule::new_method_call(), Box::new(move |msg, c| {
485 if let Some(replies) = self.handle(&msg) {
486 for r in replies { let _ = c.send(r); }
487 }
488 true
489 }));
490 }
491
492
493 pub fn start_receive<C>(self, connection: &C)
495 where
496 C: channel::MatchingReceiver<F=Box<dyn FnMut(Message, &C) -> bool>> + channel::Sender
497 {
498 connection.start_receive(message::MatchRule::new_method_call(), Box::new(move |msg, c| {
499 if let Some(replies) = self.handle(&msg) {
500 for r in replies { let _ = c.send(r); }
501 }
502 true
503 }));
504 }
505
506}
507
508pub fn new_tree<M: MethodType<D>, D: DataType>(d: D::Tree) -> Tree<M, D> {
509 Tree { paths: ArcMap::new(), data: d }
510}
511
512impl<M: MethodType<D>, D: DataType> MsgHandler for Tree<M, D> {
513 fn handle_msg(&mut self, msg: &Message) -> Option<MsgHandlerResult> {
514 self.handle(msg).map(|v| MsgHandlerResult { handled: true, done: false, reply: v })
515 }
516 fn handler_type(&self) -> MsgHandlerType { MsgHandlerType::MsgType(MessageType::MethodCall) }
517}
518pub struct TreeServer<'a, I, M: MethodType<D> + 'a, D: DataType + 'a> {
531 iter: I,
532 conn: &'a Connection,
533 tree: &'a Tree<M, D>,
534}
535
536impl<'a, I: Iterator<Item=ConnectionItem>, M: 'a + MethodType<D>, D: DataType + 'a> Iterator for TreeServer<'a, I, M, D> {
537 type Item = ConnectionItem;
538
539 fn next(&mut self) -> Option<ConnectionItem> {
540 loop {
541 let n = self.iter.next();
542 if let Some(ConnectionItem::MethodCall(ref msg)) = n {
543 if let Some(v) = self.tree.handle(&msg) {
544 for m in v { let _ = self.conn.send(m); };
547 continue;
548 }
549 }
550 return n;
551 }
552 }
553}
554
555
556#[test]
557fn test_iter() {
558 let f = super::Factory::new_fn::<()>();
559 let t = f.tree(())
560 .add(f.object_path("/echo", ()).introspectable()
561 .add(f.interface("com.example.echo", ())
562 .add_m(f.method("Echo", (), |_| unimplemented!()).in_arg(("request", "s")).out_arg(("reply", "s")))
563 .add_p(f.property::<i32,_>("EchoCount", ()))
564 .add_s(f.signal("Echoed", ()).arg(("data", "s")).deprecated()
565 )
566 )).add(f.object_path("/echo/subpath", ()));
567
568 let paths: Vec<_> = t.iter().collect();
569 assert_eq!(paths.len(), 2);
570}
571
572#[test]
573fn test_set_default_interface() {
574 let iface_name: IfaceName<'_> = "com.example.echo".into();
575 let f = super::Factory::new_fn::<()>();
576 let t = f.object_path("/echo", ()).default_interface(iface_name.clone());
577 assert_eq!(t.default_iface, Some(iface_name));
578}
579
580
581#[test]
582fn test_introspection() {
583 let f = super::Factory::new_fn::<()>();
584 let t = f.object_path("/echo", ()).introspectable()
585 .add(f.interface("com.example.echo", ())
586 .add_m(f.method("Echo", (), |_| unimplemented!()).in_arg(("request", "s")).out_arg(("reply", "s")))
587 .add_p(f.property::<i32,_>("EchoCount", ()))
588 .add_s(f.signal("Echoed", ()).arg(("data", "s")).deprecated())
589 );
590
591 let actual_result = t.introspect(&f.tree(()).add(f.object_path("/echo/subpath2", ())).add(f.object_path("/echo/subpath", ())));
592 println!("\n=== Introspection XML start ===\n{}\n=== Introspection XML end ===", actual_result);
593
594 let expected_result = r##"<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
595<node name="/echo">
596 <interface name="com.example.echo">
597 <method name="Echo">
598 <arg name="request" type="s" direction="in"/>
599 <arg name="reply" type="s" direction="out"/>
600 </method>
601 <property name="EchoCount" type="i" access="read"/>
602 <signal name="Echoed">
603 <arg name="data" type="s"/>
604 <annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
605 </signal>
606 </interface>
607 <interface name="org.freedesktop.DBus.Introspectable">
608 <method name="Introspect">
609 <arg name="xml_data" type="s" direction="out"/>
610 </method>
611 </interface>
612 <interface name="org.freedesktop.DBus.Properties">
613 <method name="Get">
614 <arg name="interface_name" type="s" direction="in"/>
615 <arg name="property_name" type="s" direction="in"/>
616 <arg name="value" type="v" direction="out"/>
617 </method>
618 <method name="GetAll">
619 <arg name="interface_name" type="s" direction="in"/>
620 <arg name="props" type="a{sv}" direction="out"/>
621 </method>
622 <method name="Set">
623 <arg name="interface_name" type="s" direction="in"/>
624 <arg name="property_name" type="s" direction="in"/>
625 <arg name="value" type="v" direction="in"/>
626 </method>
627 <signal name="PropertiesChanged">
628 <arg name="interface_name" type="s"/>
629 <arg name="changed_properties" type="a{sv}"/>
630 <arg name="invalidated_properties" type="as"/>
631 </signal>
632 </interface>
633 <node name="subpath"/>
634 <node name="subpath2"/>
635</node>"##;
636
637 assert_eq!(expected_result, actual_result);
638}
639
640#[test]
641fn test_introspection_dynamic() {
642 let f = super::Factory::new_fn::<()>();
643 let tree = f
644 .tree(())
645 .add(f.object_path("/", ()).introspectable())
646 .add(f.object_path("/foo/bar", ()).introspectable())
647 .add(f.object_path("/foo/bar/item1", ()).introspectable());
648
649 let o = f.object_path("/", ()).introspectable();
651 let actual_result = o.introspect(&tree);
652 println!("\n=== Introspection XML start ===\n{}\n=== Introspection XML end ===", actual_result);
653
654 let expected_result = r##"<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
655<node name="/">
656 <interface name="org.freedesktop.DBus.Introspectable">
657 <method name="Introspect">
658 <arg name="xml_data" type="s" direction="out"/>
659 </method>
660 </interface>
661 <node name="foo/bar"/>
662</node>"##;
663
664 assert_eq!(expected_result, actual_result);
665
666 let o = f.object_path("/foo/bar", ()).introspectable();
668 let actual_result = o.introspect(&tree);
669 println!("\n=== Introspection XML start ===\n{}\n=== Introspection XML end ===", actual_result);
670
671 let expected_result = r##"<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
672<node name="/foo/bar">
673 <interface name="org.freedesktop.DBus.Introspectable">
674 <method name="Introspect">
675 <arg name="xml_data" type="s" direction="out"/>
676 </method>
677 </interface>
678 <node name="item1"/>
679</node>"##;
680
681 assert_eq!(expected_result, actual_result);
682
683 let tree = tree.add(f.object_path("/foo/bar/item2", ()).introspectable());
685
686 let o = f.object_path("/", ()).introspectable();
688 let actual_result = o.introspect(&tree);
689 println!("\n=== Introspection XML start ===\n{}\n=== Introspection XML end ===", actual_result);
690
691 let expected_result = r##"<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
692<node name="/">
693 <interface name="org.freedesktop.DBus.Introspectable">
694 <method name="Introspect">
695 <arg name="xml_data" type="s" direction="out"/>
696 </method>
697 </interface>
698 <node name="foo/bar"/>
699</node>"##;
700
701 assert_eq!(expected_result, actual_result);
702
703 let o = f.object_path("/foo/bar", ()).introspectable();
705 let actual_result = o.introspect(&tree);
706 println!("\n=== Introspection XML start ===\n{}\n=== Introspection XML end ===", actual_result);
707
708 let expected_result = r##"<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
709<node name="/foo/bar">
710 <interface name="org.freedesktop.DBus.Introspectable">
711 <method name="Introspect">
712 <arg name="xml_data" type="s" direction="out"/>
713 </method>
714 </interface>
715 <node name="item1"/>
716 <node name="item2"/>
717</node>"##;
718
719 assert_eq!(expected_result, actual_result);
720}