dbus_crossroads/
ifacedesc.rs

1use dbus::blocking::stdintf::org_freedesktop_dbus::EmitsChangedSignal;
2use std::future::Future;
3use std::marker::PhantomData;
4use crate::{Context, PropContext, MethodErr, Crossroads, utils::Dbg};
5use std::collections::{HashMap, HashSet};
6use std::fmt;
7use std::borrow::Cow;
8use dbus::{arg, strings};
9
10#[derive(Default, Debug)]
11pub struct Registry(Vec<IfaceDesc>);
12
13impl Registry {
14    pub fn push(&mut self, x: IfaceDesc) -> usize {
15        self.0.push(x);
16        self.0.len() - 1
17    }
18
19    pub fn find_token(&self, name: Option<&strings::Interface>, tokens: &HashSet<usize>) -> Result<usize, MethodErr> {
20        for &t in tokens.iter() {
21            let desc = &self.0[t];
22            if desc.name.as_ref() == name { return Ok(t) }
23        }
24        Err(name.map(MethodErr::no_interface).unwrap_or_else(|| MethodErr::no_interface("")))
25    }
26
27    pub fn take_method(&mut self, t: usize, name: &strings::Member<'static>) -> Result<Callback, MethodErr> {
28        let mdesc = self.0[t].methods.get_mut(name).ok_or_else(|| MethodErr::no_method(name))?;
29        let cb = mdesc.cb.take();
30        let cb = cb.ok_or_else(|| MethodErr::failed(&format!("Detected recursive call to {}", name)))?;
31        Ok(cb.0)
32    }
33
34    pub fn give_method(&mut self, t: usize, name: &strings::Member<'static>, cb: Callback) {
35        let x = self.0[t].methods.get_mut(name).unwrap();
36        x.cb = Some(CallbackDbg(cb));
37    }
38
39    pub fn prop_names_readable(&self, t: usize) -> impl Iterator<Item=&str> {
40        self.0[t].properties.iter().filter_map(|(k, v)| {
41            if v.get_cb.is_some() { Some(&**k) } else { None }
42        })
43    }
44
45    pub fn take_prop(&mut self, t: usize, name: &str, is_set: bool) -> Result<PropCb, MethodErr> {
46        let pdesc = self.0[t].properties.get_mut(name).ok_or_else(|| MethodErr::no_property(name))?;
47        let cb = if is_set { pdesc.set_cb.take() } else { pdesc.get_cb.take() };
48        let rw = if is_set { "writable" } else { "readable" };
49        let cb = cb.ok_or_else(|| MethodErr::failed(&format!("Property {} is not {}", name, rw)))?;
50        Ok(cb.0)
51    }
52
53    pub fn give_prop(&mut self, t: usize, name: &str, cb: PropCb, is_set: bool) {
54        let x = self.0[t].properties.get_mut(name).unwrap();
55        let cb = Some(Dbg(cb));
56        if is_set { x.set_cb = cb } else { x.get_cb = cb };
57    }
58
59    pub fn has_props(&self, t: usize) -> bool { !self.0[t].properties.is_empty() }
60
61    pub fn find_annotation(&self, t: usize, annotation_name: &str, prop_name: Option<&str>) -> Option<&str> {
62        let desc = &self.0[t];
63        if let Some(prop_name) = prop_name {
64            if let Some(prop) = desc.properties.get(prop_name) {
65                if let Some(value) = prop.annotations.get(annotation_name) {
66                    return Some(value);
67                }
68            }
69        }
70        desc.annotations.get(annotation_name)
71    }
72
73    pub fn introspect(&self, ifaces: &HashSet<usize>) -> String {
74        let mut v: Vec<_> = ifaces.iter().filter_map(|&t| self.0[t].name.as_ref().map(|n| (n, t))).collect();
75        v.sort_unstable();
76        let mut r = String::new();
77        for (n, t) in v.into_iter() {
78            r += &format!("  <interface name=\"{}\">\n", n);
79            let desc = &self.0[t];
80
81            let mut v2: Vec<_> = desc.methods.keys().collect();
82            v2.sort_unstable();
83            for name in v2.into_iter() {
84                r += &format!("    <method name=\"{}\">\n", name);
85                let x = &desc.methods[name];
86                r += &x.input_args.introspect(Some("in"), "      ");
87                r += &x.output_args.introspect(Some("out"), "      ");
88                r += &x.annotations.introspect("      ");
89                r += "    </method>\n";
90            }
91
92            let mut v2: Vec<_> = desc.signals.keys().collect();
93            v2.sort_unstable();
94            for name in v2.into_iter() {
95                r += &format!("    <signal name=\"{}\">\n", name);
96                let x = &desc.signals[name];
97                r += &x.args.introspect(None, "      ");
98                r += &x.annotations.introspect("      ");
99                r += "    </signal>\n";
100            }
101
102            let mut v2: Vec<_> = desc.properties.keys().collect();
103            v2.sort_unstable();
104            for name in v2.into_iter() {
105                let x = &desc.properties[name];
106                let a = match (x.get_cb.is_some(), x.set_cb.is_some()) {
107                    (true, true) => "readwrite",
108                    (true, false) => "read",
109                    (false, true) => "write",
110                    _ => unreachable!(),
111                };
112                r += &format!("    <property name=\"{}\" type=\"{}\" access=\"{}\"", name, x.sig, a);
113                if x.annotations.is_empty() {
114                    r += "/>\n";
115                } else {
116                    r += &format!(">\n{}    </property>\n",  x.annotations.introspect("      "));
117                }
118            }
119            r += &desc.annotations.introspect("    ");
120            r += "  </interface>\n";
121        };
122        r
123    }
124
125    pub fn get_intf_name(&self, t: usize) -> Option<&strings::Interface<'static>> {
126        self.0.get(t)?.name.as_ref()
127    }
128}
129
130pub type Callback = Box<dyn FnMut(Context, &mut Crossroads) -> Option<Context> + Send + 'static>;
131pub type PropCb = Box<dyn FnMut(PropContext, &mut Crossroads) -> Option<PropContext> + Send + 'static>;
132
133struct CallbackDbg(Callback);
134
135impl fmt::Debug for CallbackDbg {
136    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Callback") }
137}
138
139#[derive(Debug, Clone, Default)]
140pub struct Annotations(Option<HashMap<String, String>>);
141
142impl Annotations {
143    pub fn insert<K: Into<String>, V: Into<String>>(&mut self, k: K, v: V) {
144        let mut x = self.0.take().unwrap_or_default();
145        x.insert(k.into(), v.into());
146        self.0 = Some(x);
147    }
148
149    pub fn get(&self, key: &str) -> Option<&str> {
150        self.0.as_ref()?.get(key).map(|x| &**x)
151    }
152
153    fn is_empty(&self) -> bool {
154        self.0.as_ref().map(|s| s.len()).unwrap_or(0) == 0
155    }
156
157    fn introspect(&self, prefix: &str) -> String {
158        let mut r = String::new();
159        if let Some(anns) = &self.0 {
160            for (k, v) in anns.iter() {
161                r += &format!("{}<annotation name=\"{}\" value=\"{}\"/>\n", prefix, k, v);
162            }
163        }
164        r
165    }
166}
167
168#[derive(Debug, Clone)]
169struct Argument {
170    name: Cow<'static, str>,
171    sig: dbus::Signature<'static>,
172    annotations: Annotations,
173}
174
175#[derive(Debug, Clone)]
176pub struct Arguments(Vec<Argument>);
177
178impl Arguments {
179    fn introspect(&self, dir: Option<&str>, prefix: &str) -> String {
180        let mut r = String::new();
181        for a in &self.0 {
182            r += &format!("{}<arg name=\"{}\" type=\"{}\"", prefix, a.name, a.sig);
183            if let Some(dir) = dir { r += &format!(" direction=\"{}\"", dir); }
184            if a.annotations.is_empty() {
185                r += "/>\n";
186            } else {
187                let inner_prefix = format!("{}  ", prefix);
188                r += &format!(">\n{}{}</arg>\n", a.annotations.introspect(&inner_prefix), prefix);
189            }
190        }
191        r
192    }
193}
194
195/// Struct used to describe a method when building an interface.
196#[derive(Debug)]
197pub struct MethodDesc {
198    cb: Option<CallbackDbg>,
199    input_args: Arguments,
200    output_args: Arguments,
201    annotations: Annotations,
202}
203
204impl MethodDesc {
205    pub fn annotate<N: Into<String>, V: Into<String>>(&mut self, name: N, value: V) -> &mut Self {
206        self.annotations.insert(name, value);
207        self
208    }
209    pub fn deprecated(&mut self) -> &mut Self { self.annotate(DEPRECATED, "true") }
210}
211
212
213/// Struct used to describe a signal when building an interface.
214///
215/// For now, this is only used for introspection.
216#[derive(Debug)]
217pub struct SignalDesc {
218    args: Arguments,
219    annotations: Annotations,
220}
221
222/// Struct used to describe a property when building an interface.
223#[derive(Debug)]
224pub struct SignalBuilder<'a, A: 'static> {
225    desc: &'a mut SignalDesc,
226    _dummy: PhantomData<&'static A>,
227    iface_name: Option<&'a strings::Interface<'static>>,
228    name: strings::Member<'static>,
229}
230
231
232impl<A: 'static> SignalBuilder<'_, A> {
233    pub fn annotate<N: Into<String>, V: Into<String>>(&mut self, name: N, value: V) -> &mut Self {
234        self.desc.annotations.insert(name, value);
235        self
236    }
237    pub fn deprecated(&mut self) -> &mut Self { self.annotate(DEPRECATED, "true") }
238}
239
240impl<A: arg::AppendAll + 'static> SignalBuilder<'_, A> {
241    /// Returns a function which, when called, will construct the signal message.
242    pub fn msg_fn(self) -> Box<dyn Fn(&dbus::Path, &A) -> dbus::Message + Send + Sync + 'static> {
243        let SignalBuilder { iface_name, name, .. } = self;
244        let iface_name: strings::Interface<'static> = iface_name.unwrap().clone();
245        Box::new(move |path, args| {
246            let mut msg = dbus::Message::signal(path, &iface_name, &name);
247            let mut ia = arg::IterAppend::new(&mut msg);
248            args.append(&mut ia);
249            msg
250        })
251    }
252}
253
254#[derive(Debug)]
255pub struct PropDesc {
256    annotations: Annotations,
257    sig: dbus::Signature<'static>,
258    get_cb: Option<Dbg<PropCb>>,
259    set_cb: Option<Dbg<PropCb>>,
260}
261
262#[derive(Debug)]
263pub struct IfaceDesc {
264    name: Option<strings::Interface<'static>>,
265    annotations: Annotations,
266    methods: HashMap<strings::Member<'static>, MethodDesc>,
267    signals: HashMap<strings::Member<'static>, SignalDesc>,
268    properties: HashMap<String, PropDesc>,
269}
270
271fn build_argvec<A: arg::ArgAll>(a: A::strs) -> Arguments {
272    let mut v = vec!();
273    A::strs_sig(a, |name, sig| {
274        v.push(Argument { name: name.into(), sig, annotations: Default::default() })
275    });
276    Arguments(v)
277}
278
279/// Struct used to describe a property when building an interface.
280#[derive(Debug)]
281pub struct PropBuilder<'a, T:'static, A: 'static> {
282    desc: &'a mut PropDesc,
283    _dummy: PhantomData<&'static (T, A)>,
284    emits_changed: EmitsChangedSignal,
285    iface_name: Option<&'a strings::Interface<'static>>,
286    prop_name: String,
287}
288
289impl<T, A> Drop for PropBuilder<'_, T, A> {
290    fn drop(&mut self) {
291        // Need to set at least one callback!
292        assert!(self.desc.get_cb.is_some() || self.desc.set_cb.is_some());
293    }
294}
295
296impl<T: Send, A: Send + arg::RefArg + arg::Arg + arg::Append> PropBuilder<'_, T, A> {
297    pub fn get<CB>(self, mut cb: CB) -> Self
298    where CB: FnMut(&mut PropContext, &mut T) -> Result<A, MethodErr> + Send + 'static {
299        self.get_with_cr(move |ctx, cr| {
300            let data = cr.data_mut(ctx.path()).ok_or_else(|| MethodErr::no_path(ctx.path()))?;
301            cb(ctx, data)
302        })
303    }
304
305    pub fn get_with_cr<CB>(self, mut cb: CB) -> Self
306    where CB: FnMut(&mut PropContext, &mut Crossroads) -> Result<A, MethodErr> + Send + 'static {
307        self.get_custom(move |mut ctx, cr| {
308            let r = cb(&mut ctx, cr);
309            ctx.reply(r);
310            Some(ctx)
311        })
312    }
313
314    pub (crate) fn get_custom<CB>(self, mut cb: CB) -> Self
315    where CB: FnMut(PropContext, &mut Crossroads) -> Option<PropContext> + Send + 'static {
316        self.desc.get_cb = Some(Dbg(Box::new(move |ctx, cr| {
317            cb(ctx, cr)
318        })));
319        self
320    }
321
322    pub fn get_with_cr_async<R, CB>(self, mut cb: CB) -> Self
323    where
324        CB: FnMut(PropContext, &mut Crossroads) -> R + Send + 'static,
325        R: Future<Output=PhantomData<A>> + Send + 'static
326    {
327        self.get_custom(move |mut ctx, cr| {
328            cr.run_async_method(|sender, cr| {
329                ctx.set_send_on_drop(sender);
330                let r = cb(ctx, cr);
331                async move { r.await; }
332            });
333            None
334        })
335    }
336
337    pub fn get_async<R, CB>(self, mut cb: CB) -> Self
338    where
339        CB: FnMut(PropContext, &mut T) -> R + Send + 'static,
340        R: Future<Output=PhantomData<A>> + Send + 'static
341    {
342        self.get_with_cr_async(move |ctx, cr| {
343            // It should be safe to unwrap here, the path has already been checked once (when dispatching the method)
344            let data = cr.data_mut(ctx.path()).unwrap();
345            cb(ctx, data)
346        })
347    }
348
349    /// Returns a function which, when called, creates a PropertiesChanged message.
350    ///
351    /// Call the resulting function with the path that the object belongs to,
352    /// and a reference to the new value.
353    /// If a PropertiesChanged message should be sent (this depends on the emits_changed function called),
354    /// such a message will be returned.
355    ///
356    /// # Panics
357    ///
358    /// If emits_changed is set to "const", the function will panic.
359    pub fn changed_msg_fn(self) -> Box<dyn Fn(&dbus::Path, &dyn arg::RefArg) -> Option<dbus::Message> + Send + Sync + 'static> {
360        let prop_name = self.prop_name.clone();
361        let emits_changed = self.emits_changed;
362        assert_ne!(emits_changed, EmitsChangedSignal::Const);
363        let interface_name: String = self.iface_name.map(|x| &**x).unwrap().into();
364        Box::new(move |path, val| {
365            use dbus::blocking::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged as PPC;
366            let mut ppc = PPC {
367                interface_name: interface_name.clone(),
368                invalidated_properties: vec!(),
369                changed_properties: Default::default(),
370            };
371            if ppc.add_prop(&prop_name, emits_changed, || { val.box_clone() }) {
372                use dbus::message::SignalArgs;
373                Some(ppc.to_emit_message(path))
374            } else {
375                None
376            }
377        })
378    }
379}
380
381pub const EMITS_CHANGED: &'static str = "org.freedesktop.DBus.Property.EmitsChangedSignal";
382const DEPRECATED: &'static str = "org.freedesktop.DBus.Deprecated";
383
384impl<T: Send, A: arg::RefArg + Send + for<'x> arg::Get<'x> + arg::Arg + arg::Append> PropBuilder<'_, T, A> {
385    /// Adds a set property handler to this property.
386    ///
387    /// In case an EmitsChangedSignal should be emitted, the callback should return Ok(Some(v)) where
388    /// v is the value to be emitted. If no EmitsChangedSignal should be emitted, return Ok(None).
389    pub fn set<CB>(self, mut cb: CB) -> Self
390    where CB: FnMut(&mut PropContext, &mut T, A) -> Result<Option<A>, MethodErr> + Send + 'static {
391        self.set_with_cr(move |ctx, cr, a| {
392            let data = cr.data_mut(ctx.path()).ok_or_else(|| MethodErr::no_path(ctx.path()))?;
393            cb(ctx, data, a)
394        })
395    }
396
397    /// Adds a set property handler to this property, and allowing the entire tree to be changed.
398    ///
399    /// In case an EmitsChangedSignal should be emitted, the callback should return Ok(Some(v)) where
400    /// v is the value to be emitted. If no EmitsChangedSignal should be emitted, return Ok(None).
401    pub fn set_with_cr<CB>(self, mut cb: CB) -> Self
402    where CB: FnMut(&mut PropContext, &mut Crossroads, A) -> Result<Option<A>, MethodErr> + Send + 'static {
403        self.set_custom(move |mut ctx, cr, a| {
404            match cb(&mut ctx, cr, a) {
405                Ok(None) => { ctx.reply_noemit(Ok(())); }
406                Ok(Some(x)) => { ctx.reply(Ok(x)); }
407                Err(x) => { ctx.reply_noemit(Err(x)); }
408            };
409            Some(ctx)
410        })
411    }
412
413    pub fn set_custom<CB>(self, mut cb: CB) -> Self
414    where CB: FnMut(PropContext, &mut Crossroads, A) -> Option<PropContext> + Send + 'static {
415        self.desc.set_cb = Some(Dbg(Box::new(move |mut ctx, cr| {
416            match ctx.check(|ctx| {
417                let ctx = ctx.unwrap();
418                let mut i = ctx.message().iter_init();
419                i.next(); i.next();
420                let a: arg::Variant<_> = i.read()?;
421                Ok(a)
422            }) {
423                Ok(a) => cb(ctx, cr, a.0),
424                Err(_) => Some(ctx),
425            }
426        })));
427        self
428    }
429
430    pub fn set_with_cr_async<CB, R>(self, mut cb: CB) -> Self
431    where
432        CB: FnMut(PropContext, &mut Crossroads, A) -> R + Send + 'static,
433        R: Future<Output=PhantomData<Option<A>>> + Send + 'static
434    {
435        self.set_custom(move |mut ctx, cr, a| {
436            cr.run_async_method(|sender, cr| {
437                ctx.set_send_on_drop(sender);
438                let r = cb(ctx, cr, a);
439                async move { r.await; }
440            });
441            None
442        })
443    }
444
445    pub fn set_async<CB, R>(self, mut cb: CB) -> Self
446    where
447        CB: FnMut(PropContext, &mut T, A) -> R + Send + 'static,
448        R: Future<Output=PhantomData<Option<A>>> + Send + 'static
449    {
450        self.set_with_cr_async(move |ctx, cr, a| {
451            // It should be safe to unwrap here, the path has already been checked once (when dispatching the method)
452            let data = cr.data_mut(ctx.path()).unwrap();
453            cb(ctx, data, a)
454        })
455    }
456}
457
458impl<T: std::marker::Send, A> PropBuilder<'_, T, A> {
459
460    pub fn annotate<N: Into<String>, V: Into<String>>(self, name: N, value: V) -> Self {
461        self.desc.annotations.insert(name, value);
462        self
463    }
464    pub fn deprecated(self) -> Self { self.annotate(DEPRECATED, "true") }
465    pub fn emits_changed_false(mut self) -> Self {
466        self.emits_changed = EmitsChangedSignal::False;
467        self.annotate(EMITS_CHANGED, "false")
468    }
469    /// This means that the property never changes. Attempts to change it or make an "PropertiesChanged"
470    /// signal will result in a panic.
471    pub fn emits_changed_const(mut self) -> Self {
472        self.emits_changed = EmitsChangedSignal::Const;
473        self.annotate(EMITS_CHANGED, "const")
474    }
475    pub fn emits_changed_invalidates(mut self) -> Self {
476        self.emits_changed = EmitsChangedSignal::Invalidates;
477        self.annotate(EMITS_CHANGED, "invalidates")
478    }
479    pub fn emits_changed_true(mut self) -> Self {
480        self.emits_changed = EmitsChangedSignal::True;
481        self.annotate(EMITS_CHANGED, "true")
482    }
483}
484
485/// Struct used to build an interface.
486///
487/// You get an instance of this struct in the call to Crossroads::register.
488///
489/// Register new methods, properties and signals using the corresponding functions on this struct.
490/// You might find several similar functions, e g `method`, `method_with_cr`, `method_with_cr_async` and
491/// `method_with_cr_custom`. Methods that have "with_cr" will allow you to access the full mutable Crossroads
492/// instance, but beware - trying to recursively handle methods from within a method handler is not allowed
493/// and may cause panics.
494///
495/// Methods that have "_async" will allow you to defer the result of your method. During await points,
496/// other tasks with method calls can run as separate tasks. Remember to call Crossroads::set_async_support
497/// when using async methods.
498///
499#[derive(Debug)]
500pub struct IfaceBuilder<T: Send + 'static>(IfaceDesc, PhantomData<&'static T>);
501
502impl<T: Send + 'static> IfaceBuilder<T> {
503    pub fn property<A: arg::Arg, N: Into<String>>(&mut self, name: N) -> PropBuilder<T, A> {
504        let key = name.into();
505        let prop_name = key.clone();
506        PropBuilder {
507            desc: self.0.properties.entry(key).or_insert(PropDesc {
508                annotations: Default::default(),
509                get_cb: None,
510                set_cb: None,
511                sig: A::signature(),
512            }),
513            _dummy: PhantomData,
514            emits_changed: EmitsChangedSignal::True,
515            prop_name,
516            iface_name: self.0.name.as_ref(),
517        }
518    }
519
520    pub fn method<IA, OA, N, CB>(&mut self, name: N, input_args: IA::strs, output_args: OA::strs, mut cb: CB) -> &mut MethodDesc
521    where IA: arg::ArgAll + arg::ReadAll, OA: arg::ArgAll + arg::AppendAll,
522    N: Into<strings::Member<'static>>,
523    CB: FnMut(&mut Context, &mut T, IA) -> Result<OA, MethodErr> + Send + 'static {
524        self.method_with_cr(name, input_args, output_args, move |ctx, cr, ia| {
525            let data = cr.data_mut(ctx.path()).ok_or_else(|| MethodErr::no_path(ctx.path()))?;
526            cb(ctx, data, ia)
527        })
528    }
529
530    pub fn method_with_cr<IA, OA, N, CB>(&mut self, name: N, input_args: IA::strs, output_args: OA::strs, mut cb: CB) -> &mut MethodDesc
531    where IA: arg::ArgAll + arg::ReadAll, OA: arg::ArgAll + arg::AppendAll,
532    N: Into<strings::Member<'static>>,
533    CB: FnMut(&mut Context, &mut Crossroads, IA) -> Result<OA, MethodErr> + Send + 'static {
534        let boxed = Box::new(move |mut ctx: Context, cr: &mut Crossroads| {
535            let _ = ctx.check(|ctx| {
536                let ia = ctx.message().read_all()?;
537                let oa = cb(ctx, cr, ia)?;
538                ctx.do_reply(|msg| msg.append_all(oa));
539                Ok(())
540            });
541            Some(ctx)
542        });
543        self.0.methods.entry(name.into()).or_insert(MethodDesc {
544            annotations: Default::default(),
545            input_args: build_argvec::<IA>(input_args),
546            output_args: build_argvec::<OA>(output_args),
547            cb: Some(CallbackDbg(boxed)),
548        })
549    }
550
551    pub fn method_with_cr_custom<IA, OA, N, CB>(&mut self, name: N, input_args: IA::strs, output_args: OA::strs, mut cb: CB) -> &mut MethodDesc
552    where IA: arg::ArgAll + arg::ReadAll, OA: arg::ArgAll + arg::AppendAll,
553    N: Into<strings::Member<'static>>,
554    CB: FnMut(Context, &mut Crossroads, IA) -> Option<Context> + Send + 'static {
555        let boxed = Box::new(move |mut ctx: Context, cr: &mut Crossroads| {
556            match ctx.check(|ctx| Ok(ctx.message().read_all()?)) {
557                Ok(ia) => cb(ctx, cr, ia),
558                Err(_) => Some(ctx),
559            }
560        });
561        self.0.methods.entry(name.into()).or_insert(MethodDesc {
562            annotations: Default::default(),
563            input_args: build_argvec::<IA>(input_args),
564            output_args: build_argvec::<OA>(output_args),
565            cb: Some(CallbackDbg(boxed)),
566        })
567    }
568
569    pub fn method_with_cr_async<IA, OA, N, R, CB>(&mut self, name: N, input_args: IA::strs, output_args: OA::strs, mut cb: CB) -> &mut MethodDesc
570    where IA: arg::ArgAll + arg::ReadAll, OA: arg::ArgAll + arg::AppendAll,
571    N: Into<strings::Member<'static>>,
572    CB: FnMut(Context, &mut Crossroads, IA) -> R + Send + 'static,
573    R: Future<Output=PhantomData<OA>> + Send + 'static {
574        self.method_with_cr_custom::<IA, OA, _, _>(name, input_args, output_args, move |mut ctx, cr, ia| {
575            cr.run_async_method(|sender, cr| {
576                ctx.set_send_on_drop(sender);
577                let r = cb(ctx, cr, ia);
578                async move { r.await; }
579            });
580            None
581        })
582    }
583
584
585/*
586    pub fn method_with_cr_async<'x, IA, OA, N, R, CB>(&mut self, name: N, input_args: IA::strs, output_args: OA::strs, mut cb: CB) -> &mut MethodDesc
587    where IA: arg::ArgAll + arg::ReadAll, OA: arg::ArgAll + arg::AppendAll,
588    N: Into<strings::Member<'static>>,
589    CB: FnMut(&'x mut Context, &mut Crossroads, IA) -> R + Send + 'static,
590    R: Future<Output = Result<OA, MethodErr>> + Send + 'x {
591        self.method_with_cr_custom::<IA, OA, _, _>(name, input_args, output_args, move |mut ctx, cr, ia| {
592            cb(&mut ctx, cr, ia);
593            todo!()
594        })
595    }
596*/
597
598    pub fn signal<A, N>(&mut self, name: N, args: A::strs) -> SignalBuilder<A>
599    where A: arg::ArgAll, N: Into<strings::Member<'static>> {
600        let name = name.into();
601        let key = name.clone();
602        SignalBuilder {
603            desc: self.0.signals.entry(key).or_insert(SignalDesc {
604                annotations: Default::default(),
605                args: build_argvec::<A>(args),
606            }),
607            _dummy: PhantomData,
608            name,
609            iface_name: self.0.name.as_ref(),
610        }
611    }
612
613    pub fn annotate<N: Into<String>, V: Into<String>>(&mut self, name: N, value: V) -> &mut Self {
614        self.0.annotations.insert(name, value);
615        self
616    }
617    pub fn deprecated(&mut self) -> &mut Self { self.annotate(DEPRECATED, "true") }
618
619    pub (crate) fn build<F>(name: Option<strings::Interface<'static>>, f: F) -> IfaceDesc
620    where F: FnOnce(&mut IfaceBuilder<T>) {
621        let mut b = IfaceBuilder(IfaceDesc {
622            name,
623            annotations: Default::default(),
624            methods: Default::default(),
625            signals: Default::default(),
626            properties: Default::default(),
627        }, PhantomData);
628        f(&mut b);
629        b.0
630    }
631}