diff options
Diffstat (limited to 'third_party/rust/dbus/src/tree')
-rw-r--r-- | third_party/rust/dbus/src/tree/factory.rs | 137 | ||||
-rw-r--r-- | third_party/rust/dbus/src/tree/leaves.rs | 653 | ||||
-rw-r--r-- | third_party/rust/dbus/src/tree/methodtype.rs | 275 | ||||
-rw-r--r-- | third_party/rust/dbus/src/tree/mod.rs | 35 | ||||
-rw-r--r-- | third_party/rust/dbus/src/tree/objectpath.rs | 553 | ||||
-rw-r--r-- | third_party/rust/dbus/src/tree/utils.rs | 100 |
6 files changed, 1753 insertions, 0 deletions
diff --git a/third_party/rust/dbus/src/tree/factory.rs b/third_party/rust/dbus/src/tree/factory.rs new file mode 100644 index 0000000000..2157fc4110 --- /dev/null +++ b/third_party/rust/dbus/src/tree/factory.rs @@ -0,0 +1,137 @@ +use super::{MethodType, DataType, MTFn, MTFnMut, MTSync, MethodResult, MethodInfo}; +use super::{Tree, ObjectPath, Interface, Property, Signal, Method}; +use super::objectpath::IfaceCache; +use std::sync::Arc; +use Interface as IfaceName; +use {Member, Path, arg}; +use std::cell::RefCell; + +/// The factory is used to create object paths, interfaces, methods etc. +/// +/// There are three factories: +/// +/// **MTFn** - all methods are `Fn()`. +/// +/// **MTFnMut** - all methods are `FnMut()`. This means they can mutate their environment, +/// which has the side effect that if you call it recursively, it will RefCell panic. +/// +/// **MTSync** - all methods are `Fn() + Send + Sync + 'static`. This means that the methods +/// can be called from different threads in parallel. +/// +#[derive(Debug, Clone)] +pub struct Factory<M: MethodType<D>, D: DataType=()>(Arc<IfaceCache<M, D>>); + +impl<M: MethodType<D>, D: DataType> From<Arc<IfaceCache<M, D>>> for Factory<M, D> { + fn from(f: Arc<IfaceCache<M, D>>) -> Self { Factory(f) } +} + +impl Factory<MTFn<()>, ()> { + /// Creates a new factory for single-thread use. + pub fn new_fn<D: DataType>() -> Factory<MTFn<D>, D> { Factory(IfaceCache::new()) } + + /// Creates a new factory for single-thread use, where callbacks can mutate their environment. + pub fn new_fnmut<D: DataType>() -> Factory<MTFnMut<D>, D> { Factory(IfaceCache::new()) } + + /// Creates a new factory for multi-thread use. + pub fn new_sync<D: DataType>() -> Factory<MTSync<D>, D> { Factory(IfaceCache::new()) } + +} + +impl<D: DataType> Factory<MTFn<D>, D> { + /// Creates a new method for single-thread use. + pub fn method<H, T>(&self, t: T, data: D::Method, handler: H) -> Method<MTFn<D>, D> + where H: 'static + Fn(&MethodInfo<MTFn<D>, D>) -> MethodResult, T: Into<Member<'static>> { + super::leaves::new_method(t.into(), data, Box::new(handler) as Box<_>) + } +} + +impl<D: DataType> Factory<MTFnMut<D>, D> { + /// Creates a new method for single-thread use. + pub fn method<H, T>(&self, t: T, data: D::Method, handler: H) -> Method<MTFnMut<D>, D> + where H: 'static + FnMut(&MethodInfo<MTFnMut<D>, D>) -> MethodResult, T: Into<Member<'static>> { + super::leaves::new_method(t.into(), data, Box::new(RefCell::new(handler)) as Box<_>) + } +} + +impl<D: DataType> Factory<MTSync<D>, D> { + /// Creates a new method for multi-thread use. + pub fn method<H, T>(&self, t: T, data: D::Method, handler: H) -> Method<MTSync<D>, D> + where H: Fn(&MethodInfo<MTSync<D>, D>) -> MethodResult + Send + Sync + 'static, T: Into<Member<'static>> { + super::leaves::new_method(t.into(), data, Box::new(handler) as Box<_>) + } +} + + +impl<M: MethodType<D>, D: DataType> Factory<M, D> { + + /// Creates a new property. + /// + /// `A` is used to calculate the type signature of the property. + pub fn property<A: arg::Arg, T: Into<String>>(&self, name: T, data: D::Property) -> Property<M, D> { + let sig = A::signature(); + super::leaves::new_property(name.into(), sig, data) + } + + /// Creates a new signal. + pub fn signal<T: Into<Member<'static>>>(&self, name: T, data: D::Signal) -> Signal<D> { + super::leaves::new_signal(name.into(), data) + } + + /// Creates a new interface. + pub fn interface<T: Into<IfaceName<'static>>>(&self, name: T, data: D::Interface) -> Interface<M, D> { + super::objectpath::new_interface(name.into(), data) + } + + /// Creates a new object path. + pub fn object_path<T: Into<Path<'static>>>(&self, name: T, data: D::ObjectPath) -> ObjectPath<M, D> { + super::objectpath::new_objectpath(name.into(), data, self.0.clone()) + } + + /// Creates a new tree. + pub fn tree(&self, data: D::Tree) -> Tree<M, D> { + super::objectpath::new_tree(data) + } + + /// Creates a new method - usually you'll use "method" instead. + /// + /// This is useful for being able to create methods in code which is generic over methodtype. + pub fn method_sync<H, T>(&self, t: T, data: D::Method, handler: H) -> Method<M, D> + where H: Fn(&MethodInfo<M, D>) -> MethodResult + Send + Sync + 'static, T: Into<Member<'static>> { + super::leaves::new_method(t.into(), data, M::make_method(handler)) + } +} + + +#[test] +fn create_fnmut() { + let f = Factory::new_fnmut::<()>(); + let mut move_me = 5u32; + let m = f.method("test", (), move |m| { + move_me += 1; + Ok(vec!(m.msg.method_return().append1(&move_me))) + }); + assert_eq!(&**m.get_name(), "test"); +} + + +#[test] +fn fn_customdata() { + #[derive(Default)] + struct Custom; + impl DataType for Custom { + type Tree = (); + type ObjectPath = Arc<u8>; + type Interface = (); + type Property = (); + type Method = i32; + type Signal = (); + } + + let f = Factory::new_fn::<Custom>(); + + let m = f.method("test", 789, |_| unimplemented!()); + assert_eq!(*m.get_data(), 789); + + let o = f.object_path("/test/test", Arc::new(7)); + assert_eq!(**o.get_data(), 7); +} diff --git a/third_party/rust/dbus/src/tree/leaves.rs b/third_party/rust/dbus/src/tree/leaves.rs new file mode 100644 index 0000000000..3d358f7e0c --- /dev/null +++ b/third_party/rust/dbus/src/tree/leaves.rs @@ -0,0 +1,653 @@ +// Methods, signals, properties, and interfaces. +use super::utils::{Argument, Annotations, Introspect, introspect_args}; +use super::{MethodType, MethodInfo, MethodResult, MethodErr, DataType, PropInfo, MTFn, MTFnMut, MTSync}; +use {Member, Signature, Message, Path, MessageItem}; +use Interface as IfaceName; +use arg; +use std::fmt; +use std::cell::RefCell; +use stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged; + + +// Workaround for https://github.com/rust-lang/rust/issues/31518 +struct DebugMethod<M: MethodType<D>, D: DataType>(Box<M::Method>); +impl<M: MethodType<D>, D: DataType> fmt::Debug for DebugMethod<M, D> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "<Method>") } +} +struct DebugGetProp<M: MethodType<D>, D: DataType>(Box<M::GetProp>); +impl<M: MethodType<D>, D: DataType> fmt::Debug for DebugGetProp<M, D> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "<GetProp>") } +} +struct DebugSetProp<M: MethodType<D>, D: DataType>(Box<M::SetProp>); +impl<M: MethodType<D>, D: DataType> fmt::Debug for DebugSetProp<M, D> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "<SetProp>") } +} + + +#[derive(Debug)] +/// A D-Bus Method. +pub struct Method<M: MethodType<D>, D: DataType> { + cb: DebugMethod<M, D>, + data: D::Method, + name: Member<'static>, + i_args: Vec<Argument>, + o_args: Vec<Argument>, + anns: Annotations, +} + +impl<M: MethodType<D>, D: DataType> Method<M, D> { + + /// Builder method that adds an "in" Argument to this Method. + pub fn in_arg<A: Into<Argument>>(mut self, a: A) -> Self { self.i_args.push(a.into()); self } + /// Builder method that adds an "in" Argument to this Method. + pub fn inarg<A: arg::Arg, S: Into<String>>(mut self, s: S) -> Self { self.i_args.push((s.into(), A::signature()).into()); self } + /// Builder method that adds multiple "in" Arguments to this Method. + pub fn in_args<Z: Into<Argument>, A: IntoIterator<Item=Z>>(mut self, a: A) -> Self { + self.i_args.extend(a.into_iter().map(|b| b.into())); self + } + + /// Builder method that adds an "out" Argument to this Method. + pub fn out_arg<A: Into<Argument>>(mut self, a: A) -> Self { self.o_args.push(a.into()); self } + /// Builder method that adds an "out" Argument to this Method. + pub fn outarg<A: arg::Arg, S: Into<String>>(mut self, s: S) -> Self { self.o_args.push((s.into(), A::signature()).into()); self } + /// Builder method that adds multiple "out" Arguments to this Method. + pub fn out_args<Z: Into<Argument>, A: IntoIterator<Item=Z>>(mut self, a: A) -> Self { + self.o_args.extend(a.into_iter().map(|b| b.into())); self + } + + /// Builder method that adds an annotation to the method. + pub fn annotate<N: Into<String>, V: Into<String>>(mut self, name: N, value: V) -> Self { + self.anns.insert(name, value); self + } + /// Builder method that adds an annotation that this entity is deprecated. + pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } + + /// Call the Method + pub fn call(&self, minfo: &MethodInfo<M, D>) -> MethodResult { M::call_method(&self.cb.0, minfo) } + + /// Get method name + pub fn get_name(&self) -> &Member<'static> { &self.name } + + /// Get associated data + pub fn get_data(&self) -> &D::Method { &self.data } + +} + +impl<M: MethodType<D>, D: DataType> Introspect for Method<M, D> { + fn xml_name(&self) -> &'static str { "method" } + fn xml_params(&self) -> String { String::new() } + fn xml_contents(&self) -> String { + format!("{}{}{}", + introspect_args(&self.i_args, " ", " direction=\"in\""), + introspect_args(&self.o_args, " ", " direction=\"out\""), + self.anns.introspect(" ")) + } +} + +pub fn new_method<M: MethodType<D>, D: DataType>(n: Member<'static>, data: D::Method, cb: Box<M::Method>) -> Method<M, D> { + Method { name: n, i_args: vec!(), o_args: vec!(), anns: Annotations::new(), cb: DebugMethod(cb), data: data } +} + + + +#[derive(Debug)] +/// A D-Bus Signal. +pub struct Signal<D: DataType> { + name: Member<'static>, + data: D::Signal, + arguments: Vec<Argument>, + anns: Annotations, +} + +impl<D: DataType> Signal<D> { + + /// Builder method that adds an Argument to the Signal. + pub fn arg<A: Into<Argument>>(mut self, a: A) -> Self { self.arguments.push(a.into()); self } + + /// Builder method that adds an Argument to the Signal. + pub fn sarg<A: arg::Arg, S: Into<String>>(mut self, s: S) -> Self { self.arguments.push((s.into(), A::signature()).into()); self } + + /// Builder method that adds multiple Arguments to the Signal. + pub fn args<Z: Into<Argument>, A: IntoIterator<Item=Z>>(mut self, a: A) -> Self { + self.arguments.extend(a.into_iter().map(|b| b.into())); self + } + + /// Add an annotation to this Signal. + pub fn annotate<N: Into<String>, V: Into<String>>(mut self, name: N, value: V) -> Self { + self.anns.insert(name, value); self + } + /// Add an annotation that this entity is deprecated. + pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } + + /// Get signal name + pub fn get_name(&self) -> &Member<'static> { &self.name } + + /// Get associated data + pub fn get_data(&self) -> &D::Signal { &self.data } + + /// Returns a message which emits the signal when sent. + /// + /// Same as "msg" but also takes a "MessageItem" argument. + pub fn emit(&self, p: &Path<'static>, i: &IfaceName<'static>, items: &[MessageItem]) -> Message { + let mut m = self.msg(p, i); + m.append_items(items); + m + } + + /// Returns a message which emits the signal when sent. + /// + /// Same as "emit" but does not take a "MessageItem" argument. + pub fn msg(&self, p: &Path<'static>, i: &IfaceName<'static>) -> Message { + Message::signal(p, i, &self.name) + } + +} + +impl<D: DataType> Introspect for Signal<D> { + fn xml_name(&self) -> &'static str { "signal" } + fn xml_params(&self) -> String { String::new() } + fn xml_contents(&self) -> String { + format!("{}{}", + introspect_args(&self.arguments, " ", ""), + self.anns.introspect(" ")) + } +} + +pub fn new_signal<D: DataType>(n: Member<'static>, data: D::Signal) -> Signal<D> { + Signal { name: n, arguments: vec!(), anns: Annotations::new(), data: data } +} + +#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)] +/// Enumerates the different signaling behaviors a Property can have +/// to being changed. +pub enum EmitsChangedSignal { + /// The Property emits a signal that includes the new value. + True, + /// The Property emits a signal that does not include the new value. + Invalidates, + /// The Property cannot be changed. + Const, + /// The Property does not emit a signal when changed. + False, +} + +#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)] +/// The possible access characteristics a Property can have. +pub enum Access { + /// The Property can only be read (Get). + Read, + /// The Property can be read or written. + ReadWrite, + /// The Property can only be written (Set). + Write, +} + +impl Access { + fn introspect(&self) -> &'static str { + match self { + &Access::Read => "read", + &Access::ReadWrite => "readwrite", + &Access::Write => "write", + } + } +} + + +pub fn prop_append_dict<'v, M: MethodType<D> + 'v, D: DataType + 'v, I: Iterator<Item=&'v Property<M, D>>> + (iter: &mut arg::IterAppend, mut props: I, minfo: &MethodInfo<M, D>) -> Result<(), MethodErr> { + + let mut result = Ok(()); + iter.append_dict(&Signature::make::<&str>(), &Signature::make::<arg::Variant<bool>>(), |subiter| loop { + let p = if let Some(p) = props.next() { p } else { return }; + if p.can_get().is_err() { continue; } + let pinfo = minfo.to_prop_info(minfo.iface, p); + subiter.append_dict_entry(|mut entryiter| { + entryiter.append(&*p.get_name()); + result = p.get_as_variant(&mut entryiter, &pinfo); + }); + if result.is_err() { return }; + }); + result +} + + +#[derive(Debug)] +/// A D-Bus Property. +pub struct Property<M: MethodType<D>, D: DataType> { + name: String, + data: D::Property, + sig: Signature<'static>, + emits: EmitsChangedSignal, + auto_emit: bool, + rw: Access, + get_cb: Option<DebugGetProp<M, D>>, + set_cb: Option<DebugSetProp<M, D>>, + anns: Annotations, +} + +impl<M: MethodType<D>, D: DataType> Property<M, D> { + + /// Builder method that allows setting the Property's signal + /// behavior when changed. + /// + /// Note: if e is set to const, the property will be read only. + pub fn emits_changed(mut self, e: EmitsChangedSignal) -> Self { + self.emits = e; + if self.emits == EmitsChangedSignal::Const { self.rw = Access::Read }; + self + } + + /// Builder method that determines whether or not setting this property + /// will result in an PropertiesChanged signal. Defaults to true. + /// + /// When set to true (the default), the behaviour is determined by "emits_changed". + /// When set to false, no PropertiesChanged signal will be emitted (but the signal + /// still shows up in introspection data). + /// You can still emit the signal manually by, e g, calling `add_propertieschanged` + /// and send the resulting message(s). + pub fn auto_emit_on_set(mut self, b: bool) -> Self { + self.auto_emit = b; + self + } + + /// Builder method that allows setting the Property as readable, + /// writable, or both. + /// + /// Note: might modify emits_changed as well, if property is changed to non-readonly and emit is set to "Const". + pub fn access(mut self, e: Access) -> Self { + self.rw = e; + if self.rw != Access::Read && self.emits == EmitsChangedSignal::Const { + self.emits = EmitsChangedSignal::False + }; + self + } + + /// Builder method that adds an annotation to the method. + pub fn annotate<N: Into<String>, V: Into<String>>(mut self, name: N, value: V) -> Self { + self.anns.insert(name, value); self + } + + /// Builder method that adds an annotation that this entity is deprecated. + pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } + + /// Get property name + pub fn get_name(&self) -> &str { &self.name } + + /// Get associated data + pub fn get_data(&self) -> &D::Property { &self.data } + + /// Returns Ok if the property is gettable + pub fn can_get(&self) -> Result<(), MethodErr> { + if self.rw == Access::Write || self.get_cb.is_none() { + Err(MethodErr::failed(&format!("Property {} is write only", &self.name))) + } else { Ok(()) } + } + + /// Calls the on_get function and appends the result as a variant. + /// + /// Note: Will panic if get_cb is not set. + pub fn get_as_variant(&self, i: &mut arg::IterAppend, pinfo: &PropInfo<M, D>) -> Result<(), MethodErr> { + let mut r = Ok(()); + i.append_variant(&self.sig, |subi| { + r = M::call_getprop(&*self.get_cb.as_ref().unwrap().0, subi, pinfo); + }); + r + } + + /// Returns Ok if the property is settable. + /// + /// Will verify signature in case iter is not None; iter is supposed to point at the Variant with the item inside. + pub fn can_set(&self, i: Option<arg::Iter>) -> Result<(), MethodErr> { + use arg::Arg; + if self.rw == Access::Read || self.set_cb.is_none() || self.emits == EmitsChangedSignal::Const { + return Err(MethodErr::ro_property(&self.name)) + } + if let Some(mut i) = i { + let mut subiter = try!(i.recurse(arg::Variant::<bool>::ARG_TYPE).ok_or_else(|| MethodErr::invalid_arg(&2))); + if &*subiter.signature() != &*self.sig { + return Err(MethodErr::failed(&format!("Property {} cannot change type", &self.name))) + } + } + Ok(()) + } + + /// Calls the on_set function, which reads from i. + /// + /// The return value might contain an extra message containing the EmitsChanged signal. + /// Note: Will panic if set_cb is not set. + pub fn set_as_variant(&self, i: &mut arg::Iter, pinfo: &PropInfo<M, D>) -> Result<Option<Message>, MethodErr> { + use arg::Arg; + let mut subiter = try!(i.recurse(arg::Variant::<bool>::ARG_TYPE).ok_or_else(|| MethodErr::invalid_arg(&2))); + try!(M::call_setprop(&*self.set_cb.as_ref().unwrap().0, &mut subiter, pinfo)); + self.get_emits_changed_signal(pinfo) + } + + /// Gets the signal (if any) associated with the Property. + fn get_signal(&self, p: &PropInfo<M, D>) -> Message { + Message::signal(p.path.get_name(), &"org.freedesktop.DBus.Properties".into(), &"PropertiesChanged".into()) + .append1(&**p.iface.get_name()) + } + + /// Adds this property to a list of PropertiesChanged signals. + /// + /// "v" is updated with the signal for this property. "new_value" is only called if self.emits is "true", + /// it should return the value of the property. + /// If no PropertiesChanged signal should be emitted for this property, "v" is left unchanged. + pub fn add_propertieschanged<F: FnOnce() -> Box<arg::RefArg>>(&self, v: &mut Vec<PropertiesPropertiesChanged>, iface: &IfaceName, new_value: F) { + + // Impl note: It is a bit silly that this function cannot be used from e g get_emits_changed_signal below, + // but it is due to the fact that we cannot create a RefArg out of an IterAppend; which is what the 'on_get' + // handler currently receives. + + if self.emits == EmitsChangedSignal::Const || self.emits == EmitsChangedSignal::False { return; } + let vpos = v.iter().position(|vv| &*vv.interface_name == &**iface); + let vpos = vpos.unwrap_or_else(|| { + let mut z: PropertiesPropertiesChanged = Default::default(); + z.interface_name = (&**iface).into(); + v.push(z); + v.len()-1 + }); + + let vv = &mut v[vpos]; + if self.emits == EmitsChangedSignal::Invalidates { + vv.invalidated_properties.push(self.name.clone()); + } else { + vv.changed_properties.insert(self.name.clone(), arg::Variant(new_value())); + } + } + + fn get_emits_changed_signal(&self, m: &PropInfo<M, D>) -> Result<Option<Message>, MethodErr> { + if !self.auto_emit { return Ok(None) } + match self.emits { + EmitsChangedSignal::False => Ok(None), + EmitsChangedSignal::Const => Err(MethodErr::ro_property(&self.name)), + EmitsChangedSignal::True => Ok(Some({ + let mut s = self.get_signal(m); + { + let mut iter = arg::IterAppend::new(&mut s); + try!(prop_append_dict(&mut iter, Some(self).into_iter(), &m.to_method_info())); + iter.append(arg::Array::<&str, _>::new(vec!())); + } + s + })), + EmitsChangedSignal::Invalidates => Ok(Some(self.get_signal(m).append2( + arg::Dict::<&str, arg::Variant<bool>, _>::new(vec!()), + arg::Array::new(Some(&*self.name).into_iter()) + ))), + } + } +} + +impl<'a, D: DataType> Property<MTFn<D>, D> { + /// Sets the callback for getting a property. + /// + /// For single-thread use. + pub fn on_get<H>(mut self, handler: H) -> Property<MTFn<D>, D> + where H: 'static + Fn(&mut arg::IterAppend, &PropInfo<MTFn<D>, D>) -> Result<(), MethodErr> { + self.get_cb = Some(DebugGetProp(Box::new(handler) as Box<_>)); + self + } + + /// Sets the callback for setting a property. + /// + /// For single-thread use. + pub fn on_set<H>(mut self, handler: H) -> Property<MTFn<D>, D> + where H: 'static + Fn(&mut arg::Iter, &PropInfo<MTFn<D>, D>) -> Result<(), MethodErr> { + self.set_cb = Some(DebugSetProp(Box::new(handler) as Box<_>)); + self + } +} + + +impl<'a, D: DataType> Property<MTFnMut<D>, D> { + /// Sets the callback for getting a property. + /// + /// For single-thread use. + pub fn on_get<H>(mut self, handler: H) -> Property<MTFnMut<D>, D> + where H: 'static + Fn(&mut arg::IterAppend, &PropInfo<MTFnMut<D>, D>) -> Result<(), MethodErr> { + self.get_cb = Some(DebugGetProp(Box::new(RefCell::new(handler)) as Box<_>)); + self + } + + /// Sets the callback for setting a property. + /// + /// For single-thread use. + pub fn on_set<H>(mut self, handler: H) -> Property<MTFnMut<D>, D> + where H: 'static + Fn(&mut arg::Iter, &PropInfo<MTFnMut<D>, D>) -> Result<(), MethodErr> { + self.set_cb = Some(DebugSetProp(Box::new(RefCell::new(handler)) as Box<_>)); + self + } +} + +impl<D: DataType> Property<MTSync<D>, D> { + /// Sets the callback for getting a property. + /// + /// For multi-thread use. + pub fn on_get<H>(mut self, handler: H) -> Property<MTSync<D>, D> + where H: Fn(&mut arg::IterAppend, &PropInfo<MTSync<D>, D>) -> Result<(), MethodErr> + Send + Sync + 'static { + self.get_cb = Some(DebugGetProp(Box::new(handler) as Box<_>)); + self + } + + /// Sets the callback for setting a property. + /// + /// For single-thread use. + pub fn on_set<H>(mut self, handler: H) -> Property<MTSync<D>, D> + where H: Fn(&mut arg::Iter, &PropInfo<MTSync<D>, D>) -> Result<(), MethodErr> + Send + Sync + 'static { + self.set_cb = Some(DebugSetProp(Box::new(handler) as Box<_>)); + self + } +} + + +impl<M: MethodType<D>, D: DataType> Property<M, D> where D::Property: arg::Append + Clone { + /// Adds a "standard" get handler. + pub fn default_get(mut self) -> Self { + let g = |i: &mut arg::IterAppend, p: &PropInfo<M, D>| { i.append(p.prop.get_data()); Ok(()) }; + self.get_cb = Some(DebugGetProp(M::make_getprop(g))); + self + } +} + + +impl<M: MethodType<D>, D: DataType> Property<M, D> where D::Property: arg::RefArg { + /// Adds a "standard" get handler (for RefArgs). + pub fn default_get_refarg(mut self) -> Self { + let g = |i: &mut arg::IterAppend, p: &PropInfo<M, D>| { (p.prop.get_data() as &arg::RefArg).append(i); Ok(()) }; + self.get_cb = Some(DebugGetProp(M::make_getprop(g))); + self + } +} + +impl<M: MethodType<D>, D: DataType> Introspect for Property<M, D> { + fn xml_name(&self) -> &'static str { "property" } + fn xml_params(&self) -> String { format!(" type=\"{}\" access=\"{}\"", self.sig, self.rw.introspect()) } + fn xml_contents(&self) -> String { + let s = match self.emits { + EmitsChangedSignal::True => return self.anns.introspect(" "), + EmitsChangedSignal::False => "false", + EmitsChangedSignal::Const => "const", + EmitsChangedSignal::Invalidates => "invalidates", + }; + let mut tempanns = self.anns.clone(); + tempanns.insert("org.freedesktop.DBus.Property.EmitsChangedSignal", s); + tempanns.introspect(" ") + } +} + +pub fn new_property<M: MethodType<D>, D: DataType> + (n: String, sig: Signature<'static>, data: D::Property) -> Property<M, D> { + Property { + name: n, emits: EmitsChangedSignal::True, auto_emit: true, rw: Access::Read, + sig: sig, anns: Annotations::new(), set_cb: None, get_cb: None, data: data + } +} + +#[test] +fn test_prop_handlers() { + use tree::Factory; + use std::collections::BTreeMap; + use arg::{Dict, Variant}; + + #[derive(Default, Debug)] + struct Custom; + impl DataType for Custom { + type Tree = (); + type ObjectPath = (); + type Interface = (); + type Property = i32; + type Method = (); + type Signal = (); + } + + let f = Factory::new_fn::<Custom>(); + let tree = f.tree(()).add(f.object_path("/test", ()).introspectable().object_manager() + .add(f.interface("com.example.test", ()) + .add_p(f.property::<i32,_>("Value1", 5i32).default_get()) + .add_p(f.property::<i32,_>("Value2", 9i32).default_get()) + ) + ); + + let mut msg = Message::new_method_call("com.example.test", "/test", "org.freedesktop.DBus.Properties", "Get").unwrap() + .append2("com.example.test", "Value1"); + ::message::message_set_serial(&mut msg, 4); + let res = tree.handle(&msg).unwrap(); + assert_eq!(res[0].get1(), Some(arg::Variant(5i32))); + + let mut msg = Message::new_method_call("com.example.test", "/test", "org.freedesktop.DBus.Properties", "Set").unwrap() + .append3("com.example.test", "Value1", arg::Variant(3i32)); + ::message::message_set_serial(&mut msg, 4); + let mut res = tree.handle(&msg).unwrap(); + assert!(res[0].as_result().is_err()); + + let mut msg = Message::new_method_call("com.example.test", "/test", "org.freedesktop.DBus.Properties", "GetAll").unwrap() + .append1("com.example.test"); + ::message::message_set_serial(&mut msg, 4); + let res = tree.handle(&msg).unwrap(); + let d: Dict<&str, Variant<i32>, _> = res[0].get1().unwrap(); + let z2: BTreeMap<_, _> = d.collect(); + assert_eq!(z2.get("Value1"), Some(&arg::Variant(5i32))); + assert_eq!(z2.get("Value2"), Some(&arg::Variant(9i32))); + assert_eq!(z2.get("Mooh"), None); + + let mut msg = Message::new_method_call("com.example.test", "/test", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects").unwrap(); + ::message::message_set_serial(&mut msg, 4); + let res = tree.handle(&msg).unwrap(); + let pdict: arg::Dict<Path, Dict<&str, Dict<&str, Variant<i32>, _>, _>, _> = res[0].get1().unwrap(); + let pmap: BTreeMap<_, _> = pdict.collect(); + let idict = pmap.get(&Path::from("/test")).unwrap(); + let imap: BTreeMap<_, _> = idict.collect(); + let propdict = imap.get("com.example.test").unwrap(); + let propmap: BTreeMap<_, _> = propdict.collect(); + assert_eq!(propmap.get("Value1"), Some(&arg::Variant(5i32))); + assert_eq!(propmap.get("Value2"), Some(&arg::Variant(9i32))); + assert_eq!(propmap.get("Mooh"), None); +} + +#[test] +fn test_set_prop() { + use tree::{Factory, Access}; + use std::cell::{Cell, RefCell}; + use std::collections::BTreeMap; + use std::rc::Rc; + + let changes = Rc::new(Cell::new(0i32)); + let (changes1, changes2) = (changes.clone(), changes.clone()); + let setme = Rc::new(RefCell::new("I have not been set yet!".to_owned())); + let (setme1, setme2) = (setme.clone(), setme.clone()); + + let f = Factory::new_fn::<()>(); + let tree = f.tree(()).add(f.object_path("/example", ()).introspectable() + .add(f.interface("com.example.dbus.rs", ()) + .add_p(f.property::<i32,_>("changes", ()) + .on_get(move |i, _| { i.append(changes1.get()); Ok(()) })) + .add_p(f.property::<String,_>("setme", ()) + .access(Access::ReadWrite) + .on_get(move |i, _| { i.append(&*setme1.borrow()); Ok(()) }) + .on_set(move |i, _| { + *setme2.borrow_mut() = i.get().unwrap(); + changes2.set(changes2.get() + 1); + Ok(()) + })) + ) + ); + + // Read-only + let mut msg = Message::new_method_call("com.example.dbus.rs", "/example", "org.freedesktop.DBus.Properties", "Set").unwrap() + .append3("com.example.dbus.rs", "changes", arg::Variant(5i32)); + ::message::message_set_serial(&mut msg, 20); + let mut r = tree.handle(&msg).unwrap(); + assert!(r.get_mut(0).unwrap().as_result().is_err()); + + // Wrong type + let mut msg = Message::new_method_call("com.example.dbus.rs", "/example", "org.freedesktop.DBus.Properties", "Set").unwrap() + .append3("com.example.dbus.rs", "setme", arg::Variant(8i32)); + ::message::message_set_serial(&mut msg, 30); + let mut r = tree.handle(&msg).unwrap(); + assert!(r.get_mut(0).unwrap().as_result().is_err()); + + // Correct! + let mut msg = Message::new_method_call("com.example.dbus.rs", "/example", "org.freedesktop.DBus.Properties", "Set").unwrap() + .append3("com.example.dbus.rs", "setme", arg::Variant("Correct")); + ::message::message_set_serial(&mut msg, 30); + let r = tree.handle(&msg).unwrap(); + + assert_eq!(changes.get(), 1); + assert_eq!(&**setme.borrow(), "Correct"); + + println!("{:?}", r); + assert_eq!(r.len(), 2); + assert_eq!(&*r[0].member().unwrap(), "PropertiesChanged"); + let (s, d): (Option<&str>, Option<arg::Dict<&str, arg::Variant<_>, _>>) = r[0].get2(); + assert_eq!(s, Some("com.example.dbus.rs")); + let z2: BTreeMap<_, _> = d.unwrap().collect(); + assert_eq!(z2.get("setme"), Some(&arg::Variant("Correct"))); + +} + + +#[test] +fn test_sync_prop() { + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::sync::Arc; + use tree::{Factory, Access, EmitsChangedSignal}; + + let f = Factory::new_sync::<()>(); + + let count = Arc::new(AtomicUsize::new(3)); + let (cget, cset) = (count.clone(), count.clone()); + + let tree1 = Arc::new(f.tree(()).add(f.object_path("/syncprop", ()).introspectable() + .add(f.interface("com.example.syncprop", ()) + .add_p(f.property::<u32,_>("syncprop", ()) + .access(Access::ReadWrite) + .emits_changed(EmitsChangedSignal::False) + .on_get(move |i,_| { i.append(cget.load(Ordering::SeqCst) as u32); Ok(()) }) + .on_set(move |i,_| { cset.store(i.get::<u32>().unwrap() as usize, Ordering::SeqCst); Ok(()) }) + ) + ) + )); + + let tree2 = tree1.clone(); + println!("{:#?}", tree2); + + ::std::thread::spawn(move || { + let mut msg = Message::new_method_call("com.example.syncprop", "/syncprop", "org.freedesktop.DBus.Properties", "Set").unwrap() + .append3("com.example.syncprop", "syncprop", arg::Variant(5u32)); + ::message::message_set_serial(&mut msg, 30); + let mut r = tree2.handle(&msg).unwrap(); + assert!(r[0].as_result().is_ok()); + }); + + loop { + let mut msg = Message::new_method_call("com.example.echoserver", "/syncprop", "org.freedesktop.DBus.Properties", "Get").unwrap() + .append("com.example.syncprop").append1("syncprop"); + ::message::message_set_serial(&mut msg, 4); + let mut r = tree1.handle(&msg).unwrap(); + let r = r[0].as_result().unwrap(); + let z: arg::Variant<u32> = r.get1().unwrap(); + if z.0 == 5 { break; } + assert_eq!(z.0, 3); + } + assert_eq!(count.load(Ordering::SeqCst), 5); +} diff --git a/third_party/rust/dbus/src/tree/methodtype.rs b/third_party/rust/dbus/src/tree/methodtype.rs new file mode 100644 index 0000000000..38b0669d77 --- /dev/null +++ b/third_party/rust/dbus/src/tree/methodtype.rs @@ -0,0 +1,275 @@ +// Methods and method types. Glue to make stuff generic over MFn, MFnMut and MSync + +use std::fmt; +use {ErrorName, Message, stdintf}; +use arg::{Iter, IterAppend, TypeMismatchError}; +use std::marker::PhantomData; +use super::{Method, Interface, Property, ObjectPath, Tree}; +use std::cell::RefCell; +use std::ffi::CString; +use super::super::Error as dbusError; + +#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] +/// A D-Bus Method Error, containing an error name and a description. +pub struct MethodErr(ErrorName<'static>, String); + +impl MethodErr { + /// Create an Invalid Args MethodErr. + pub fn invalid_arg<T: fmt::Debug>(a: &T) -> MethodErr { + ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a)).into() + } + /// Create a MethodErr that there are not enough arguments given. + pub fn no_arg() -> MethodErr { + ("org.freedesktop.DBus.Error.InvalidArgs", "Not enough arguments").into() + } + /// Create a MethodErr that the method failed in the way specified. + pub fn failed<T: fmt::Display>(a: &T) -> MethodErr { + ("org.freedesktop.DBus.Error.Failed", a.to_string()).into() + } + /// Create a MethodErr that the Interface was unknown. + pub fn no_interface<T: fmt::Display>(a: &T) -> MethodErr { + ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", a)).into() + } + /// Create a MethodErr that the Method was unknown. + pub fn no_method<T: fmt::Display>(a: &T) -> MethodErr { + ("org.freedesktop.DBus.Error.UnknownMethod", format!("Unknown method {}", a)).into() + } + /// Create a MethodErr that the Property was unknown. + pub fn no_property<T: fmt::Display>(a: &T) -> MethodErr { + ("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", a)).into() + } + /// Create a MethodErr that the Property was read-only. + pub fn ro_property<T: fmt::Display>(a: &T) -> MethodErr { + ("org.freedesktop.DBus.Error.PropertyReadOnly", format!("Property {} is read only", a)).into() + } + + /// Error name accessor + pub fn errorname(&self) -> &ErrorName<'static> { &self.0 } + /// Description accessor + pub fn description(&self) -> &str { &self.1 } + + /// Creates an error reply from a method call message. + /// + /// Note: You normally don't need to use this function, + /// as it is called internally from Tree::handle. + pub fn to_message(&self, msg: &Message) -> Message { + msg.error(&self.0, &CString::new(&*self.1).unwrap()) + } +} + +impl From<TypeMismatchError> for MethodErr { + fn from(t: TypeMismatchError) -> MethodErr { ("org.freedesktop.DBus.Error.Failed", format!("{}", t)).into() } +} + +impl<T: Into<ErrorName<'static>>, M: Into<String>> From<(T, M)> for MethodErr { + fn from((t, m): (T, M)) -> MethodErr { MethodErr(t.into(), m.into()) } +} + +impl From<dbusError> for MethodErr { + fn from(t: dbusError) -> MethodErr { + let n = t.name().unwrap_or("org.freedesktop.DBus.Error.Failed"); + let m = t.message().unwrap_or("Unknown error"); + MethodErr(String::from(n).into(), m.into()) + } +} + + +/// Result containing the Messages returned from the Method, or a MethodErr. +pub type MethodResult = Result<Vec<Message>, MethodErr>; + +/// Associated data for different objects in a tree. +/// +/// These currently require a debug bound, due to https://github.com/rust-lang/rust/issues/31518 +pub trait DataType: Sized + Default { + /// Type of associated data on the Tree. + type Tree: fmt::Debug; + /// Type of associated data on every ObjectPath. + type ObjectPath: fmt::Debug; + /// Type of associated data on every Property. + type Property: fmt::Debug; + /// Type of associated data on every Interface. + type Interface: fmt::Debug; + /// Type of associated data on every Method. + type Method: fmt::Debug; + /// Type of associated data on every Signal. + type Signal: fmt::Debug; +} + +/// No associated data for the tree. +impl DataType for () { + type Tree = (); + type ObjectPath = (); + type Interface = (); + type Property = (); + type Method = (); + type Signal = (); +} + +/// A helper trait used internally to make the tree generic over MTFn, MTFnMut and MTSync. +/// +/// You should not need to call these methods directly, it's primarily for internal use. +pub trait MethodType<D: DataType>: Sized + Default { + /// For internal use. + type Method: ?Sized; + /// For internal use. + type GetProp: ?Sized; + /// For internal use. + type SetProp: ?Sized; + + /// For internal use. + fn call_getprop(&Self::GetProp, &mut IterAppend, &PropInfo<Self, D>) -> Result<(), MethodErr>; + /// For internal use. + fn call_setprop(&Self::SetProp, &mut Iter, &PropInfo<Self, D>) -> Result<(), MethodErr>; + /// For internal use. + fn call_method(&Self::Method, &MethodInfo<Self, D>) -> MethodResult; + + /// For internal use. + fn make_getprop<H>(h: H) -> Box<Self::GetProp> + where H: Fn(&mut IterAppend, &PropInfo<Self,D>) -> Result<(), MethodErr> + Send + Sync + 'static; + /// For internal use. + fn make_method<H>(h: H) -> Box<Self::Method> + where H: Fn(&MethodInfo<Self,D>) -> MethodResult + Send + Sync + 'static; +} + + +/// An abstract type to represent Fn functions. +#[derive(Default, Debug, Copy, Clone)] +pub struct MTFn<D=()>(PhantomData<*const D>); + +impl<D: DataType> MethodType<D> for MTFn<D> { + type GetProp = Fn(&mut IterAppend, &PropInfo<Self, D>) -> Result<(), MethodErr>; + type SetProp = Fn(&mut Iter, &PropInfo<Self, D>) -> Result<(), MethodErr>; + type Method = Fn(&MethodInfo<Self, D>) -> MethodResult; + + fn call_getprop(p: &Self::GetProp, i: &mut IterAppend, pinfo: &PropInfo<Self, D>) + -> Result<(), MethodErr> { p(i, pinfo) } + fn call_setprop(p: &Self::SetProp, i: &mut Iter, pinfo: &PropInfo<Self, D>) + -> Result<(), MethodErr> { p(i, pinfo) } + fn call_method(p: &Self::Method, minfo: &MethodInfo<Self, D>) + -> MethodResult { p(minfo) } + + fn make_getprop<H>(h: H) -> Box<Self::GetProp> + where H: Fn(&mut IterAppend, &PropInfo<Self,D>) -> Result<(), MethodErr> + Send + Sync + 'static { Box::new(h) } + fn make_method<H>(h: H) -> Box<Self::Method> + where H: Fn(&MethodInfo<Self,D>) -> MethodResult + Send + Sync + 'static { Box::new(h) } +} + +/// An abstract type to represent FnMut functions. +#[derive(Default, Debug, Copy, Clone)] +pub struct MTFnMut<D=()>(PhantomData<*const D>); + +impl<D: DataType> MethodType<D> for MTFnMut<D> { + type GetProp = RefCell<FnMut(&mut IterAppend, &PropInfo<Self, D>) -> Result<(), MethodErr>>; + type SetProp = RefCell<FnMut(&mut Iter, &PropInfo<Self, D>) -> Result<(), MethodErr>>; + type Method = RefCell<FnMut(&MethodInfo<Self, D>) -> MethodResult>; + + fn call_getprop(p: &Self::GetProp, i: &mut IterAppend, pinfo: &PropInfo<Self, D>) + -> Result<(), MethodErr> { (&mut *p.borrow_mut())(i, pinfo) } + fn call_setprop(p: &Self::SetProp, i: &mut Iter, pinfo: &PropInfo<Self, D>) + -> Result<(), MethodErr> { (&mut *p.borrow_mut())(i, pinfo) } + fn call_method(p: &Self::Method, minfo: &MethodInfo<Self, D>) + -> MethodResult { (&mut *p.borrow_mut())(minfo) } + + fn make_getprop<H>(h: H) -> Box<Self::GetProp> + where H: Fn(&mut IterAppend, &PropInfo<Self,D>) -> Result<(), MethodErr> + Send + Sync + 'static { Box::new(RefCell::new(h)) } + fn make_method<H>(h: H) -> Box<Self::Method> + where H: Fn(&MethodInfo<Self,D>) -> MethodResult + Send + Sync + 'static { Box::new(RefCell::new(h)) } + +} + +/// An abstract type to represent Fn + Send + Sync functions (that can be called from several threads in parallel). +#[derive(Default, Debug, Copy, Clone)] +pub struct MTSync<D=()>(PhantomData<*const D>); + +impl<D: DataType> MethodType<D> for MTSync<D> { + type GetProp = Fn(&mut IterAppend, &PropInfo<Self, D>) -> Result<(), MethodErr> + Send + Sync + 'static; + type SetProp = Fn(&mut Iter, &PropInfo<Self, D>) -> Result<(), MethodErr> + Send + Sync + 'static; + type Method = Fn(&MethodInfo<Self, D>) -> MethodResult + Send + Sync + 'static; + + fn call_getprop(p: &Self::GetProp, i: &mut IterAppend, pinfo: &PropInfo<Self, D>) + -> Result<(), MethodErr> { p(i, pinfo) } + fn call_setprop(p: &Self::SetProp, i: &mut Iter, pinfo: &PropInfo<Self, D>) + -> Result<(), MethodErr> { p(i, pinfo) } + fn call_method(p: &Self::Method, minfo: &MethodInfo<Self, D>) + -> MethodResult { p(minfo) } + + fn make_getprop<H>(h: H) -> Box<Self::GetProp> + where H: Fn(&mut IterAppend, &PropInfo<Self,D>) -> Result<(), MethodErr> + Send + Sync + 'static { Box::new(h) } + fn make_method<H>(h: H) -> Box<Self::Method> + where H: Fn(&MethodInfo<Self,D>) -> MethodResult + Send + Sync + 'static { Box::new(h) } +} + + + +#[derive(Debug, Copy, Clone)] +/// Contains information about the incoming method call. +pub struct MethodInfo<'a, M: 'a + MethodType<D>, D: 'a + DataType> { + /// Message + pub msg: &'a Message, + /// The method to be called + pub method: &'a Method<M, D>, + /// Interface + pub iface: &'a Interface<M, D>, + /// Object path + pub path: &'a ObjectPath<M, D>, + /// Tree + pub tree: &'a Tree<M, D>, +} + +impl<'a, M: 'a + MethodType<D>, D: 'a + DataType> MethodInfo<'a, M, D> { + /// MethodInfo to PropInfo conversion + pub fn to_prop_info(&self, iface: &'a Interface<M, D>, prop: &'a Property<M, D>) -> PropInfo<'a, M, D> { + PropInfo { msg: self.msg, method: self.method, iface: iface, prop: prop, path: self.path, tree: self.tree } + } +} + + +impl<'a, M: 'a + MethodType<D>, D: 'a + DataType> stdintf::OrgFreedesktopDBusIntrospectable for MethodInfo<'a, M, D> { + type Err = MethodErr; + fn introspect(&self) -> Result<String, Self::Err> { Ok(self.path.introspect(self.tree)) } +} + +// Mostly autogenerated by dbus-codegen +pub fn org_freedesktop_dbus_introspectable_server<M, D>(factory: &super::Factory<M, D>, data: D::Interface) -> super::Interface<M, D> +where + D: super::DataType, + D::Method: Default, + M: MethodType<D>, +{ + let i = factory.interface("org.freedesktop.DBus.Introspectable", data); + let h = move |minfo: &super::MethodInfo<M, D>| { + let d: &stdintf::OrgFreedesktopDBusIntrospectable<Err=super::MethodErr> = minfo; + let arg0 = try!(d.introspect()); + let rm = minfo.msg.method_return(); + let rm = rm.append1(arg0); + Ok(vec!(rm)) + }; + let m = factory.method_sync("Introspect", Default::default(), h); + let m = m.out_arg(("xml_data", "s")); + let i = i.add_m(m); + i +} + +#[derive(Debug, Copy, Clone)] +/// Contains information about the incoming property get/set request. +pub struct PropInfo<'a, M: 'a + MethodType<D>, D: 'a + DataType> { + /// Message + pub msg: &'a Message, + /// Get, Set or GetAll + pub method: &'a Method<M, D>, + /// The property to be set/get + pub prop: &'a Property<M, D>, + /// The interface the property belongs to + pub iface: &'a Interface<M, D>, + /// Object path + pub path: &'a ObjectPath<M, D>, + /// Tree + pub tree: &'a Tree<M, D>, +} + +impl<'a, M: 'a + MethodType<D>, D: 'a + DataType> PropInfo<'a, M, D> { + /// PropInfo to MethodInfo conversion. + pub fn to_method_info(&self) -> MethodInfo<'a, M, D> { + MethodInfo { msg: self.msg, method: self.method, iface: self.iface, path: self.path, tree: self.tree } + } +} diff --git a/third_party/rust/dbus/src/tree/mod.rs b/third_party/rust/dbus/src/tree/mod.rs new file mode 100644 index 0000000000..00257235fb --- /dev/null +++ b/third_party/rust/dbus/src/tree/mod.rs @@ -0,0 +1,35 @@ +//! Contains functionality for dispatching methods on a D-Bus "server". +//! +//! # Example +//! ```rust,no_run +//! use dbus::{tree, Connection, BusType}; +//! let f = tree::Factory::new_fn::<()>(); +//! /* Add a method returning "Thanks!" on interface "com.example.dbus.rs" +//! on object path "/example". */ +//! let t = f.tree(()).add(f.object_path("/example", ()).introspectable() +//! .add(f.interface("com.example.dbus.rs", ()) +//! .add_m(f.method("CallMe", (), |m| { +//! Ok(vec!(m.msg.method_return().append("Thanks!"))) } +//! ).out_arg("s")) +//! )); +//! +//! let c = Connection::get_private(BusType::Session).unwrap(); +//! t.set_registered(&c, true).unwrap(); +//! c.add_handler(t); +//! /* Run forever */ +//! loop { c.incoming(1000).next(); } +//! ``` +//! +//! See `examples/server.rs` and `examples/adv_server.rs` for more thorough examples. + +mod utils; +mod methodtype; +mod leaves; +mod objectpath; +mod factory; + +pub use self::utils::{Argument, Iter}; +pub use self::methodtype::{MethodErr, MethodInfo, PropInfo, MethodResult, MethodType, DataType, MTFn, MTFnMut, MTSync}; +pub use self::leaves::{Method, Signal, Property, Access, EmitsChangedSignal}; +pub use self::objectpath::{Interface, ObjectPath, Tree, TreeServer}; +pub use self::factory::Factory; diff --git a/third_party/rust/dbus/src/tree/objectpath.rs b/third_party/rust/dbus/src/tree/objectpath.rs new file mode 100644 index 0000000000..cc2fb0ba05 --- /dev/null +++ b/third_party/rust/dbus/src/tree/objectpath.rs @@ -0,0 +1,553 @@ +use super::utils::{ArcMap, Iter, IterE, Annotations, Introspect}; +use super::{Factory, MethodType, MethodInfo, MethodResult, MethodErr, DataType, Property, Method, Signal, methodtype}; +use std::sync::{Arc, Mutex}; +use {Member, Message, Path, Signature, MessageType, Connection, ConnectionItem, Error, arg, MsgHandler, MsgHandlerType, MsgHandlerResult}; +use Interface as IfaceName; +use std::fmt; +use std::ffi::CStr; +use super::leaves::prop_append_dict; + +fn introspect_map<I: fmt::Display, T: Introspect> + (h: &ArcMap<I, T>, indent: &str) -> String { + + h.iter().fold("".into(), |a, (k, v)| { + let (name, params, contents) = (v.xml_name(), v.xml_params(), v.xml_contents()); + format!("{}{}<{} name=\"{}\"{}{}>\n", + a, indent, name, &*k, params, if contents.len() > 0 { + format!(">\n{}{}</{}", contents, indent, name) + } + else { format!("/") } + ) + }) +} + +#[derive(Debug)] +/// Represents a D-Bus interface. +pub struct Interface<M: MethodType<D>, D: DataType> { + name: Arc<IfaceName<'static>>, + methods: ArcMap<Member<'static>, Method<M, D>>, + signals: ArcMap<Member<'static>, Signal<D>>, + properties: ArcMap<String, Property<M, D>>, + anns: Annotations, + data: D::Interface, +} + +impl<M: MethodType<D>, D: DataType> Interface<M, D> { + /// Builder function that adds a method to the interface. + pub fn add_m<I: Into<Arc<Method<M, D>>>>(mut self, m: I) -> Self { + let m = m.into(); + self.methods.insert(m.get_name().clone(), m); + self + } + + /// Builder function that adds a signal to the interface. + pub fn add_s<I: Into<Arc<Signal<D>>>>(mut self, s: I) -> Self { + let m = s.into(); + self.signals.insert(m.get_name().clone(), m); + self + } + + /// Builder function that adds a property to the interface. + pub fn add_p<I: Into<Arc<Property<M, D>>>>(mut self, p: I) -> Self { + let m = p.into(); + self.properties.insert(m.get_name().to_owned(), m); + self + } + + /// Builder function that adds an annotation to this interface. + pub fn annotate<N: Into<String>, V: Into<String>>(mut self, name: N, value: V) -> Self { + self.anns.insert(name, value); self + } + + /// Builder function that adds an annotation that this entity is deprecated. + pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } + + /// Get interface name + pub fn get_name(&self) -> &IfaceName<'static> { &self.name } + + /// Get associated data + pub fn get_data(&self) -> &D::Interface { &self.data } + + /// Iterates over methods implemented by this interface. + pub fn iter_m<'a>(&'a self) -> Iter<'a, Method<M, D>> { IterE::Member(self.methods.values()).into() } + + /// Iterates over signals implemented by this interface. + pub fn iter_s<'a>(&'a self) -> Iter<'a, Signal<D>> { IterE::Member(self.signals.values()).into() } + + /// Iterates over properties implemented by this interface. + pub fn iter_p<'a>(&'a self) -> Iter<'a, Property<M, D>> { IterE::String(self.properties.values()).into() } +} + +impl<M: MethodType<D>, D: DataType> Introspect for Interface<M, D> { + fn xml_name(&self) -> &'static str { "interface" } + fn xml_params(&self) -> String { String::new() } + fn xml_contents(&self) -> String { + format!("{}{}{}{}", + introspect_map(&self.methods, " "), + introspect_map(&self.properties, " "), + introspect_map(&self.signals, " "), + self.anns.introspect(" ")) + } +} + + +pub fn new_interface<M: MethodType<D>, D: DataType>(t: IfaceName<'static>, d: D::Interface) -> Interface<M, D> { + Interface { name: Arc::new(t), methods: ArcMap::new(), signals: ArcMap::new(), + properties: ArcMap::new(), anns: Annotations::new(), data: d + } +} + + +#[derive(Debug)] +/// Cache of built-in interfaces, in order to save memory when many object paths implement the same interface(s). +pub struct IfaceCache<M: MethodType<D>, D: DataType>(Mutex<ArcMap<IfaceName<'static>, Interface<M, D>>>); + +impl<M: MethodType<D>, D: DataType> IfaceCache<M, D> +where D::Interface: Default { + pub fn get<S: Into<IfaceName<'static>> + Clone, F>(&self, s: S, f: F) -> Arc<Interface<M, D>> + where F: FnOnce(Interface<M, D>) -> Interface<M, D> { + let s2 = s.clone().into(); + let mut m = self.0.lock().unwrap(); + m.entry(s2).or_insert_with(|| { + let i = new_interface(s.into(), Default::default()); + Arc::new(f(i)) + }).clone() + } +} + +impl<M: MethodType<D>, D: DataType> IfaceCache<M, D> { + pub fn get_factory<S: Into<IfaceName<'static>> + Clone, F>(&self, s: S, f: F) -> Arc<Interface<M, D>> + where F: FnOnce() -> Interface<M, D> { + let s2 = s.clone().into(); + let mut m = self.0.lock().unwrap(); + m.entry(s2).or_insert_with(|| { + Arc::new(f()) + }).clone() + } + + + pub fn new() -> Arc<Self> { Arc::new(IfaceCache(Mutex::new(ArcMap::new()))) } +} + +#[derive(Debug)] +/// A D-Bus Object Path. +pub struct ObjectPath<M: MethodType<D>, D: DataType> { + name: Arc<Path<'static>>, + default_iface: Option<IfaceName<'static>>, + ifaces: ArcMap<Arc<IfaceName<'static>>, Interface<M, D>>, + ifacecache: Arc<IfaceCache<M, D>>, + data: D::ObjectPath, +} + +impl<M: MethodType<D>, D: DataType> ObjectPath<M, D> { + + /// Get property name + pub fn get_name(&self) -> &Path<'static> { &self.name } + + /// Get associated data + pub fn get_data(&self) -> &D::ObjectPath { &self.data } + + /// Iterates over interfaces implemented by this object path. + pub fn iter<'a>(&'a self) -> Iter<'a, Interface<M, D>> { IterE::Iface(self.ifaces.values()).into() } + + pub(super) fn introspect(&self, tree: &Tree<M, D>) -> String { + let ifacestr = introspect_map(&self.ifaces, " "); + let olen = self.name.len()+1; + let childstr = tree.children(self, true).iter().fold("".to_string(), |na, n| + format!("{} <node name=\"{}\"/>\n", na, &n.name[olen..]) + ); + + 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"> +<node name="{}"> +{}{}</node>"##, self.name, ifacestr, childstr); + nodestr + } + + fn get_iface<'a>(&'a self, iface_name: &'a CStr) -> Result<&Arc<Interface<M, D>>, MethodErr> { + let j = try!(IfaceName::from_slice(iface_name.to_bytes_with_nul()).map_err(|e| MethodErr::invalid_arg(&e))); + self.ifaces.get(&j).ok_or_else(|| MethodErr::no_interface(&j)) + } + + fn prop_get(&self, m: &MethodInfo<M, D>) -> MethodResult { + let (iname, prop_name): (&CStr, &str) = try!(m.msg.read2()); + let iface = try!(self.get_iface(iname)); + let prop: &Property<M, D> = try!(iface.properties.get(&String::from(prop_name)) + .ok_or_else(|| MethodErr::no_property(&prop_name))); + try!(prop.can_get()); + let mut mret = m.msg.method_return(); + { + let mut iter = arg::IterAppend::new(&mut mret); + let pinfo = m.to_prop_info(iface, prop); + try!(prop.get_as_variant(&mut iter, &pinfo)); + } + Ok(vec!(mret)) + } + + fn prop_get_all(&self, m: &MethodInfo<M, D>) -> MethodResult { + let iface = try!(self.get_iface(try!(m.msg.read1()))); + let mut mret = m.msg.method_return(); + try!(prop_append_dict(&mut arg::IterAppend::new(&mut mret), + iface.properties.values().map(|v| &**v), m)); + Ok(vec!(mret)) + } + + + fn prop_set(&self, m: &MethodInfo<M, D>) -> MethodResult { + let (iname, prop_name): (&CStr, &str) = try!(m.msg.read2()); + let iface = try!(self.get_iface(iname)); + let prop: &Property<M, D> = try!(iface.properties.get(&String::from(prop_name)) + .ok_or_else(|| MethodErr::no_property(&prop_name))); + + let mut iter = arg::Iter::new(m.msg); + iter.next(); iter.next(); + let mut iter2 = iter; + try!(prop.can_set(Some(iter))); + + let pinfo = m.to_prop_info(iface, prop); + let mut r: Vec<Message> = try!(prop.set_as_variant(&mut iter2, &pinfo)).into_iter().collect(); + r.push(m.msg.method_return()); + Ok(r) + + } + + fn get_managed_objects(&self, m: &MethodInfo<M, D>) -> MethodResult { + use arg::{Dict, Variant}; + let mut paths = m.tree.children(&self, false); + paths.push(&self); + let mut result = Ok(()); + let mut r = m.msg.method_return(); + { + let mut i = arg::IterAppend::new(&mut r); + i.append_dict(&Signature::make::<Path>(), &Signature::make::<Dict<&str,Dict<&str,Variant<()>,()>,()>>(), |ii| { + for p in paths { + ii.append_dict_entry(|pi| { + pi.append(&*p.name); + pi.append_dict(&Signature::make::<&str>(), &Signature::make::<Dict<&str,Variant<()>,()>>(), |pii| { + for ifaces in p.ifaces.values() { + let m2 = MethodInfo { msg: m.msg, path: p, iface: ifaces, tree: m.tree, method: m.method }; + pii.append_dict_entry(|ppii| { + ppii.append(&**ifaces.name); + result = prop_append_dict(ppii, ifaces.properties.values().map(|v| &**v), &m2); + }); + if result.is_err() { break; } + } + }); + }); + if result.is_err() { break; } + } + }); + } + try!(result); + Ok(vec!(r)) + } + + fn handle(&self, m: &Message, t: &Tree<M, D>) -> MethodResult { + let iname = m.interface().or_else(|| { self.default_iface.clone() }); + let i = try!(iname.and_then(|i| self.ifaces.get(&i)).ok_or_else(|| MethodErr::no_interface(&""))); + let me = try!(m.member().and_then(|me| i.methods.get(&me)).ok_or_else(|| MethodErr::no_method(&""))); + let minfo = MethodInfo { msg: m, tree: t, path: self, iface: i, method: me }; + me.call(&minfo) + } + +} + +impl<M: MethodType<D>, D: DataType> ObjectPath<M, D> +where <D as DataType>::Interface: Default, <D as DataType>::Method: Default +{ + /// Adds introspection support for this object path. + pub fn introspectable(self) -> Self { + let z = self.ifacecache.get_factory("org.freedesktop.DBus.Introspectable", || { + let f = Factory::from(self.ifacecache.clone()); + methodtype::org_freedesktop_dbus_introspectable_server(&f, Default::default()) + }); + self.add(z) + } + + /// Builder function that adds a interface to the object path. + pub fn add<I: Into<Arc<Interface<M, D>>>>(mut self, s: I) -> Self { + let m = s.into(); + if !m.properties.is_empty() { self.add_property_handler(); } + self.ifaces.insert(m.name.clone(), m); + self + } + + /// Builder function that sets what interface should be dispatched on an incoming + /// method call without interface. + pub fn default_interface(mut self, i: IfaceName<'static>) -> Self { + self.default_iface = Some(i); + self + } + + /// Adds ObjectManager support for this object path. + /// + /// It is not possible to add/remove interfaces while the object path belongs to a tree, + /// hence no InterfacesAdded / InterfacesRemoved signals are sent. + pub fn object_manager(mut self) -> Self { + use arg::{Variant, Dict}; + let ifname = IfaceName::from("org.freedesktop.DBus.ObjectManager"); + if self.ifaces.contains_key(&ifname) { return self }; + let z = self.ifacecache.get(ifname, |i| { + i.add_m(super::leaves::new_method("GetManagedObjects".into(), Default::default(), + M::make_method(|m| m.path.get_managed_objects(m))) + .outarg::<Dict<Path,Dict<&str,Dict<&str,Variant<()>,()>,()>,()>,_>("objpath_interfaces_and_properties")) + }); + self.ifaces.insert(z.name.clone(), z); + self + } + + fn add_property_handler(&mut self) { + use arg::{Variant, Dict}; + let ifname = IfaceName::from("org.freedesktop.DBus.Properties"); + if self.ifaces.contains_key(&ifname) { return }; + let z = self.ifacecache.get(ifname, |i| { + i.add_m(super::leaves::new_method("Get".into(), Default::default(), + M::make_method(|m| m.path.prop_get(m))) + .inarg::<&str,_>("interface_name") + .inarg::<&str,_>("property_name") + .outarg::<Variant<()>,_>("value")) + .add_m(super::leaves::new_method("GetAll".into(), Default::default(), + M::make_method(|m| m.path.prop_get_all(m))) + .inarg::<&str,_>("interface_name") + .outarg::<Dict<&str, Variant<()>, ()>,_>("props")) + .add_m(super::leaves::new_method("Set".into(), Default::default(), + M::make_method(|m| m.path.prop_set(m))) + .inarg::<&str,_>("interface_name") + .inarg::<&str,_>("property_name") + .inarg::<Variant<bool>,_>("value")) + }); + self.ifaces.insert(z.name.clone(), z); + } +} + +pub fn new_objectpath<M: MethodType<D>, D: DataType>(n: Path<'static>, d: D::ObjectPath, cache: Arc<IfaceCache<M, D>>) + -> ObjectPath<M, D> { + ObjectPath { name: Arc::new(n), data: d, ifaces: ArcMap::new(), ifacecache: cache, default_iface: None } +} + + +/// A collection of object paths. +#[derive(Debug, Default)] +pub struct Tree<M: MethodType<D>, D: DataType> { + paths: ArcMap<Arc<Path<'static>>, ObjectPath<M, D>>, + data: D::Tree, +} + +impl<M: MethodType<D>, D: DataType> Tree<M, D> { + /// Builder function that adds an object path to this tree. + /// + /// Note: This does not register a path with the connection, so if the tree is currently registered, + /// you might want to call Connection::register_object_path to add the path manually. + pub fn add<I: Into<Arc<ObjectPath<M, D>>>>(mut self, s: I) -> Self { + self.insert(s); + self + } + + /// Get a reference to an object path from the tree. + pub fn get(&self, p: &Path<'static>) -> Option<&Arc<ObjectPath<M, D>>> { + self.paths.get(p) + } + + /// Iterates over object paths in this tree. + pub fn iter<'a>(&'a self) -> Iter<'a, ObjectPath<M, D>> { IterE::Path(self.paths.values()).into() } + + /// Non-builder function that adds an object path to this tree. + /// + /// Note: This does not register a path with the connection, so if the tree is currently registered, + /// you might want to call Connection::register_object_path to add the path manually. + pub fn insert<I: Into<Arc<ObjectPath<M, D>>>>(&mut self, s: I) { + let m = s.into(); + self.paths.insert(m.name.clone(), m); + } + + + /// Remove a object path from the Tree. Returns the object path removed, or None if not found. + /// + /// Note: This does not unregister a path with the connection, so if the tree is currently registered, + /// you might want to call Connection::unregister_object_path to remove the path manually. + pub fn remove(&mut self, p: &Path<'static>) -> Option<Arc<ObjectPath<M, D>>> { + // There is no real reason p needs to have a static lifetime; but + // the borrow checker doesn't agree. :-( + self.paths.remove(p) + } + + /// Registers or unregisters all object paths in the tree. + pub fn set_registered(&self, c: &Connection, b: bool) -> Result<(), Error> { + let mut regd_paths = Vec::new(); + for p in self.paths.keys() { + if b { + match c.register_object_path(p) { + Ok(()) => regd_paths.push(p.clone()), + Err(e) => { + while let Some(rp) = regd_paths.pop() { + c.unregister_object_path(&rp); + } + return Err(e) + } + } + } else { + c.unregister_object_path(p); + } + } + Ok(()) + } + + /// This method takes an `ConnectionItem` iterator (you get it from `Connection::iter()`) + /// and handles all matching items. Non-matching items (e g signals) are passed through. + pub fn run<'a, I: Iterator<Item=ConnectionItem>>(&'a self, c: &'a Connection, i: I) -> TreeServer<'a, I, M, D> { + TreeServer { iter: i, tree: &self, conn: c } + } + + /// Handles a message. + /// + /// Will return None in case the object path was not + /// found in this tree, or otherwise a list of messages to be sent back. + pub fn handle(&self, m: &Message) -> Option<Vec<Message>> { + if m.msg_type() != MessageType::MethodCall { None } + else { m.path().and_then(|p| self.paths.get(&p).map(|s| s.handle(m, &self) + .unwrap_or_else(|e| vec!(e.to_message(m))))) } + } + + + fn children(&self, o: &ObjectPath<M, D>, direct_only: bool) -> Vec<&ObjectPath<M, D>> { + let parent: &str = &o.name; + let plen = parent.len()+1; + self.paths.values().filter_map(|v| { + let k: &str = &v.name; + if !k.starts_with(parent) || k.len() <= plen || &k[plen-1..plen] != "/" {None} else { + let child = &k[plen..]; + if direct_only && child.contains("/") {None} else {Some(&**v)} + } + }).collect() + } + + /// Get associated data + pub fn get_data(&self) -> &D::Tree { &self.data } + +} + +pub fn new_tree<M: MethodType<D>, D: DataType>(d: D::Tree) -> Tree<M, D> { + Tree { paths: ArcMap::new(), data: d } +} + +impl<M: MethodType<D>, D: DataType> MsgHandler for Tree<M, D> { + fn handle_msg(&mut self, msg: &Message) -> Option<MsgHandlerResult> { + self.handle(msg).map(|v| MsgHandlerResult { handled: true, done: false, reply: v }) + } + fn handler_type(&self) -> MsgHandlerType { MsgHandlerType::MsgType(MessageType::MethodCall) } +} + +impl<M: MethodType<D>, D: DataType> MsgHandler for Arc<Tree<M, D>> { + fn handle_msg(&mut self, msg: &Message) -> Option<MsgHandlerResult> { + self.handle(msg).map(|v| MsgHandlerResult { handled: true, done: false, reply: v }) + } + fn handler_type(&self) -> MsgHandlerType { MsgHandlerType::MsgType(MessageType::MethodCall) } +} + +/// An iterator adapter that handles incoming method calls. +/// +/// Method calls that match an object path in the tree are handled and consumed by this +/// iterator. Other messages are passed through. +pub struct TreeServer<'a, I, M: MethodType<D> + 'a, D: DataType + 'a> { + iter: I, + conn: &'a Connection, + tree: &'a Tree<M, D>, +} + +impl<'a, I: Iterator<Item=ConnectionItem>, M: 'a + MethodType<D>, D: DataType + 'a> Iterator for TreeServer<'a, I, M, D> { + type Item = ConnectionItem; + + fn next(&mut self) -> Option<ConnectionItem> { + loop { + let n = self.iter.next(); + if let &Some(ConnectionItem::MethodCall(ref msg)) = &n { + if let Some(v) = self.tree.handle(&msg) { + // Probably the wisest is to ignore any send errors here - + // maybe the remote has disconnected during our processing. + for m in v { let _ = self.conn.send(m); }; + continue; + } + } + return n; + } + } +} + + +#[test] +fn test_iter() { + let f = super::Factory::new_fn::<()>(); + let t = f.tree(()) + .add(f.object_path("/echo", ()).introspectable() + .add(f.interface("com.example.echo", ()) + .add_m(f.method("Echo", (), |_| unimplemented!()).in_arg(("request", "s")).out_arg(("reply", "s"))) + .add_p(f.property::<i32,_>("EchoCount", ())) + .add_s(f.signal("Echoed", ()).arg(("data", "s")).deprecated() + ) + )).add(f.object_path("/echo/subpath", ())); + + let paths: Vec<_> = t.iter().collect(); + assert_eq!(paths.len(), 2); +} + +#[test] +fn test_set_default_interface() { + let iface_name: IfaceName<'_> = "com.example.echo".into(); + let f = super::Factory::new_fn::<()>(); + let t = f.object_path("/echo", ()).default_interface(iface_name.clone()); + assert_eq!(t.default_iface, Some(iface_name)); +} + + +#[test] +fn test_introspection() { + let f = super::Factory::new_fn::<()>(); + let t = f.object_path("/echo", ()).introspectable() + .add(f.interface("com.example.echo", ()) + .add_m(f.method("Echo", (), |_| unimplemented!()).in_arg(("request", "s")).out_arg(("reply", "s"))) + .add_p(f.property::<i32,_>("EchoCount", ())) + .add_s(f.signal("Echoed", ()).arg(("data", "s")).deprecated()) + ); + + let actual_result = t.introspect(&f.tree(()).add(f.object_path("/echo/subpath", ()))); + println!("\n=== Introspection XML start ===\n{}\n=== Introspection XML end ===", actual_result); + + 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"> +<node name="/echo"> + <interface name="com.example.echo"> + <method name="Echo"> + <arg name="request" type="s" direction="in"/> + <arg name="reply" type="s" direction="out"/> + </method> + <property name="EchoCount" type="i" access="read"/> + <signal name="Echoed"> + <arg name="data" type="s"/> + <annotation name="org.freedesktop.DBus.Deprecated" value="true"/> + </signal> + </interface> + <interface name="org.freedesktop.DBus.Introspectable"> + <method name="Introspect"> + <arg name="xml_data" type="s" direction="out"/> + </method> + </interface> + <interface name="org.freedesktop.DBus.Properties"> + <method name="Get"> + <arg name="interface_name" type="s" direction="in"/> + <arg name="property_name" type="s" direction="in"/> + <arg name="value" type="v" direction="out"/> + </method> + <method name="GetAll"> + <arg name="interface_name" type="s" direction="in"/> + <arg name="props" type="a{sv}" direction="out"/> + </method> + <method name="Set"> + <arg name="interface_name" type="s" direction="in"/> + <arg name="property_name" type="s" direction="in"/> + <arg name="value" type="v" direction="in"/> + </method> + </interface> + <node name="subpath"/> +</node>"##; + + assert_eq!(expected_result, actual_result); +} + diff --git a/third_party/rust/dbus/src/tree/utils.rs b/third_party/rust/dbus/src/tree/utils.rs new file mode 100644 index 0000000000..5b1418908b --- /dev/null +++ b/third_party/rust/dbus/src/tree/utils.rs @@ -0,0 +1,100 @@ +// Small structs that don't have their own unit. + +use {Signature, Member, Path, Interface as IfaceName}; +use std::collections::{BTreeMap, btree_map}; +use std::sync::Arc; + +pub type ArcMap<K, V> = BTreeMap<K, Arc<V>>; + +#[derive(Clone, Debug)] +pub enum IterE<'a, V: 'a> { + Path(btree_map::Values<'a, Arc<Path<'static>>, Arc<V>>), + Iface(btree_map::Values<'a, Arc<IfaceName<'static>>, Arc<V>>), + Member(btree_map::Values<'a, Member<'static>, Arc<V>>), + String(btree_map::Values<'a, String, Arc<V>>), +} + +#[derive(Clone, Debug)] +/// Iterator struct, returned from iterator methods on Tree, Objectpath and Interface. +pub struct Iter<'a, V: 'a>(IterE<'a, V>); + +impl<'a, V: 'a> From<IterE<'a, V>> for Iter<'a, V> { fn from(x: IterE<'a, V>) -> Iter<'a, V> { Iter(x) }} + +impl<'a, V: 'a> Iterator for Iter<'a, V> { + type Item = &'a Arc<V>; + fn next(&mut self) -> Option<Self::Item> { + match self.0 { + IterE::Path(ref mut x) => x.next(), + IterE::Iface(ref mut x) => x.next(), + IterE::Member(ref mut x) => x.next(), + IterE::String(ref mut x) => x.next(), + } + } +} + +#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] +/// A D-Bus Argument. +pub struct Argument(Option<String>, Signature<'static>); + +impl Argument { + /// Create a new Argument. + pub fn new(name: Option<String>, sig: Signature<'static>) -> Argument { Argument(name, sig) } + + /// Descriptive name (if any). + pub fn name(&self) -> Option<&str> { self.0.as_ref().map(|s| &**s) } + + /// Type signature of argument. + pub fn signature(&self) -> &Signature<'static> { &self.1 } + + fn introspect(&self, indent: &str, dir: &str) -> String { + let n = self.0.as_ref().map(|n| format!("name=\"{}\" ", n)).unwrap_or("".into()); + format!("{}<arg {}type=\"{}\"{}/>\n", indent, n, self.1, dir) + } + +} + +pub fn introspect_args(args: &[Argument], indent: &str, dir: &str) -> String { + args.iter().fold("".to_string(), |aa, az| format!("{}{}", aa, az.introspect(indent, dir))) +} + +// Small helper struct to reduce memory somewhat for objects without annotations +#[derive(Clone, Debug, Default)] +pub struct Annotations(Option<BTreeMap<String, String>>); + +impl Annotations { + pub fn new() -> Annotations { Annotations(None) } + + pub fn insert<N: Into<String>, V: Into<String>>(&mut self, n: N, v: V) { + if self.0.is_none() { self.0 = Some(BTreeMap::new()) } + self.0.as_mut().unwrap().insert(n.into(), v.into()); + } + + pub fn introspect(&self, indent: &str) -> String { + self.0.as_ref().map(|s| s.iter().fold("".into(), |aa, (ak, av)| { + format!("{}{}<annotation name=\"{}\" value=\"{}\"/>\n", aa, indent, ak, av) + })).unwrap_or(String::new()) + } +} + +// Doesn't work, conflicting impls +// impl<S: Into<Signature>> From<S> for Argument + +impl From<Signature<'static>> for Argument { + fn from(t: Signature<'static>) -> Argument { Argument(None, t) } +} + +impl<'a> From<&'a str> for Argument { + fn from(t: &'a str) -> Argument { Argument(None, String::from(t).into()) } +} + +impl<N: Into<String>, S: Into<Signature<'static>>> From<(N, S)> for Argument { + fn from((n, s): (N, S)) -> Argument { Argument(Some(n.into()), s.into()) } +} + +pub trait Introspect { + // At some point we might want to switch to fmt::Write / fmt::Formatter for performance... + fn xml_name(&self) -> &'static str; + fn xml_params(&self) -> String; + fn xml_contents(&self) -> String; +} + |