use dbus::strings::{Path as PathName, Interface as IfaceName, Member as MemberName, Signature};
use dbus::Message;
use std::borrow::Cow;
use std::collections::HashMap;
use std::any::{Any, TypeId};
use std::mem;
use dbus::arg::{Arg, Append, AppendAll, ReadAll, ArgAll, Get, TypeMismatchError, IterAppend};
use std::marker::PhantomData;
use super::MethodErr;
use super::handlers::{Handlers, MakeHandler, DebugMethod, DebugProp, Par};
use super::crossroads::{Crossroads, RegEntry};
use super::path::Path;
fn build_argvec<A: ArgAll>(a: A::strs) -> Vec<Argument<'static>> {
let mut v = vec!();
A::strs_sig(a, |name, sig| {
v.push(Argument { name: name.into(), sig, anns: Default::default() })
});
v
}
pub (super) type Annotations = HashMap<String, String>;
#[derive(Debug, Clone)]
pub struct Argument<'a> {
pub (super) name: Cow<'a, str>,
pub (super) sig: Signature<'a>,
pub (super) anns: Annotations,
}
#[derive(Debug)]
pub struct IfaceInfo<'a, H: Handlers> {
pub (super) name: IfaceName<'a>,
pub (super) methods: Vec<MethodInfo<'a, H>>,
pub (super) props: Vec<PropInfo<'a, H>>,
pub (super) signals: Vec<SignalInfo<'a>>,
pub (super) anns: Annotations,
}
#[derive(Debug)]
pub struct MethodInfo<'a, H: Handlers> {
pub (super) name: MemberName<'a>,
handler: DebugMethod<H>,
pub (super) i_args: Vec<Argument<'a>>,
pub (super) o_args: Vec<Argument<'a>>,
pub (super) anns: Annotations,
}
impl<'a, H: Handlers> MethodInfo<'a, H> {
pub fn name(&self) -> &MemberName<'a> { &self.name }
pub fn handler(&self) -> &H::Method { &self.handler.0 }
pub fn handler_mut(&mut self) -> &mut H::Method { &mut self.handler.0 }
}
#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)]
pub enum EmitsChangedSignal {
True,
Invalidates,
Const,
False,
}
impl<'a> std::convert::TryFrom<&'a Annotations> for EmitsChangedSignal {
type Error = ();
fn try_from(a: &'a Annotations) -> Result<Self, Self::Error> {
match a.get("org.freedesktop.DBus.Property.EmitsChangedSignal").map(|s| &**s) {
None => Err(()),
Some("true") => Ok(EmitsChangedSignal::True),
Some("false") => Ok(EmitsChangedSignal::False),
Some("invalidates") => Ok(EmitsChangedSignal::Invalidates),
Some("const") => Ok(EmitsChangedSignal::Const),
_ => Err(()),
}
}
}
#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)]
pub enum Access {
Read,
ReadWrite,
Write,
}
#[derive(Debug)]
pub struct PropInfo<'a, H: Handlers> {
pub (super) name: String,
pub (super) handlers: DebugProp<H>,
pub (super) anns: Annotations,
pub (super) sig: Signature<'a>,
pub (super) access: Access,
}
#[derive(Debug)]
pub struct SignalInfo<'a> {
pub (super) name: MemberName<'a>,
pub (super) args: Vec<Argument<'a>>,
pub (super) anns: Annotations,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
enum MetSigProp { Method, Signal, Prop }
#[derive(Debug)]
pub struct IfaceInfoBuilder<'a, I: 'static, H: Handlers> {
cr: Option<&'a mut Crossroads<H>>,
entry: RegEntry<H>,
last: Option<MetSigProp>,
_dummy: PhantomData<*const I>,
}
impl<'a, I: 'static, H: Handlers> IfaceInfoBuilder<'a, I, H> {
pub fn new(cr: Option<&'a mut Crossroads<H>>, name: IfaceName<'static>) -> Self {
let entry = RegEntry::new::<I>(name);
IfaceInfoBuilder { cr, _dummy: PhantomData, entry, last: None }
}
pub fn signal<A: ArgAll, N: Into<MemberName<'static>>>(mut self, name: N, args: A::strs) -> Self {
let s = SignalInfo { name: name.into(), args: build_argvec::<A>(args), anns: Default::default() };
self.entry.info.signals.push(s);
self.last = Some(MetSigProp::Signal);
self
}
pub fn annotate<N: Into<String>, V: Into<String>>(mut self, name: N, value: V) -> Self {
let x: &mut Annotations = match self.last {
None => &mut self.entry.info.anns,
Some(MetSigProp::Method) => &mut self.entry.info.methods.last_mut().unwrap().anns,
Some(MetSigProp::Signal) => &mut self.entry.info.signals.last_mut().unwrap().anns,
Some(MetSigProp::Prop) => &mut self.entry.info.props.last_mut().unwrap().anns,
};
x.insert(name.into(), value.into());
self
}
pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") }
pub fn emits_changed(self, e: EmitsChangedSignal) -> Self {
let c = match e {
EmitsChangedSignal::True => "true",
EmitsChangedSignal::False => "false",
EmitsChangedSignal::Invalidates => "invalidates",
EmitsChangedSignal::Const => "const",
};
match self.last {
None | Some(MetSigProp::Prop) => self.annotate("org.freedesktop.DBus.Property.EmitsChangedSignal", c),
_ => panic!("Cannot add EmitsChangedSignal to a method or signal"),
}
}
pub fn access(mut self, a: Access) -> Self {
if self.last != Some(MetSigProp::Prop) { panic!("Cannot modify access on a non property") }
self.entry.info.props.last_mut().unwrap().access = a;
self
}
pub fn method<IA: ReadAll + ArgAll, OA: AppendAll + ArgAll, N, F, D>(self, name: N, in_args: IA::strs, out_args: OA::strs, f: F) -> Self
where N: Into<MemberName<'static>>, F: MakeHandler<<H as Handlers>::Method, ((), IA, OA, I), D> {
self.method_custom::<IA, OA>(name.into(), in_args, out_args, f.make())
}
pub fn method_custom<IA: ArgAll, OA: ArgAll>(mut self, name: MemberName<'static>, in_args: IA::strs, out_args: OA::strs, f: H::Method) -> Self {
let m = MethodInfo { name, handler: DebugMethod(f),
i_args: build_argvec::<IA>(in_args), o_args: build_argvec::<OA>(out_args), anns: Default::default() };
self.entry.info.methods.push(m);
self.last = Some(MetSigProp::Method);
self
}
pub fn prop_custom(mut self, name: String, sig: Signature<'static>, get: Option<H::GetProp>, set: Option<H::SetProp>) -> Self {
let p = PropInfo::new(name, sig, get, set);
self.entry.info.props.push(p);
self.last = Some(MetSigProp::Prop);
self
}
pub fn prop_ro<T: Arg, N: Into<String>, G, D>(self, name: N, getf: G) -> Self
where G: MakeHandler<<H as Handlers>::GetProp, (i64, T, I), D> {
self.prop_custom(name.into(), T::signature(), Some(getf.make()), None)
}
pub fn prop_rw<T: Arg, N: Into<String>, G, S, D>(self, name: N, getf: G, setf: S) -> Self
where G: MakeHandler<<H as Handlers>::GetProp, (i64, T, I), D>,
S: MakeHandler<<H as Handlers>::SetProp, (u64, T, I), D> {
self.prop_custom(name.into(), T::signature(), Some(getf.make()), Some(setf.make()))
}
pub (super) fn on_path_insert<F: Fn(&mut Path<H>, &Crossroads<H>) + 'static + Send + Sync>(mut self, f: F) -> Self {
self.entry.path_insert = Some(Box::new(f));
self
}
}
impl<'a, I: 'static, H: Handlers> Drop for IfaceInfoBuilder<'a, I, H> {
fn drop(&mut self) {
if let Some(ref mut cr) = self.cr {
let n = self.entry.info.name.clone();
let entry = mem::replace(&mut self.entry, RegEntry::new::<()>(n.clone())); cr.reg.insert(n.into_cstring(), entry);
}
}
}
impl<H: Handlers> MethodInfo<'_, H> {
pub fn new(name: MemberName<'static>, f: H::Method) -> Self {
MethodInfo { name: name, handler: DebugMethod(f),
i_args: Default::default(), o_args: Default::default(), anns: Default::default() }
}
}
impl<H: Handlers> PropInfo<'_, H> {
pub fn new(name: String, sig: Signature<'static>, get: Option<H::GetProp>,
set: Option<H::SetProp>) -> Self {
let a = match (&get, &set) {
(Some(_), Some(_)) => Access::ReadWrite,
(Some(_), None) => Access::Read,
(None, Some(_)) => Access::Write,
_ => Access::Read,
};
PropInfo { name, handlers: DebugProp(get, set), sig, access: a, anns: Default::default() }
}
}
impl<'a, H: Handlers> IfaceInfo<'a, H> {
pub (super) fn emits_for_prop(&self, propinfo: &PropInfo<H>) -> EmitsChangedSignal {
propinfo.anns.get("org.freedesktop.DBus.Property.EmitsChangedSignal")
.or_else(|| self.anns.get("org.freedesktop.DBus.Property.EmitsChangedSignal"))
.map(|x| match &**x {
"true" => EmitsChangedSignal::True,
"false" => EmitsChangedSignal::False,
"invalidates" => EmitsChangedSignal::Invalidates,
"const" => EmitsChangedSignal::Const,
n @ _ => panic!("Invalid EmitsChangedSignal value: {}", n),
}).unwrap_or(EmitsChangedSignal::True)
}
pub fn new_empty(name: IfaceName<'static>) -> Self {
IfaceInfo { name, methods: vec!(), props: vec!(), signals: vec!(), anns: Default::default(), }
}
pub fn new<N, M, P, S>(name: N, methods: M, properties: P, signals: S) -> Self where
N: Into<IfaceName<'a>>,
M: IntoIterator<Item=MethodInfo<'a, H>>,
P: IntoIterator<Item=PropInfo<'a, H>>,
S: IntoIterator<Item=SignalInfo<'a>>
{
IfaceInfo {
name: name.into(),
methods: methods.into_iter().collect(),
props: properties.into_iter().collect(),
signals: signals.into_iter().collect(),
anns: Default::default(),
}
}
}