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#[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#[derive(Debug)]
217pub struct SignalDesc {
218 args: Arguments,
219 annotations: Annotations,
220}
221
222#[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 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#[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 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 let data = cr.data_mut(ctx.path()).unwrap();
345 cb(ctx, data)
346 })
347 }
348
349 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 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 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 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 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#[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
585pub 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}