dbus-tree-0.9.2/.cargo_vcs_info.json0000644000000001120000000000000127270ustar { "git": { "sha1": "dc00561b7fcfd09e385dfcf2c2fdebbb89a51aef" } } dbus-tree-0.9.2/Cargo.lock0000644000000032700000000000000107120ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "dbus" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de0a745c25b32caa56b82a3950f5fec7893a960f4c10ca3b02060b0c38d8c2ce" dependencies = [ "libc", "libdbus-sys", "winapi", ] [[package]] name = "dbus-tree" version = "0.9.2" dependencies = [ "dbus", ] [[package]] name = "libc" version = "0.2.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" [[package]] name = "libdbus-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c185b5b7ad900923ef3a8ff594083d4d9b5aea80bb4f32b8342363138c0d456b" dependencies = [ "pkg-config", ] [[package]] name = "pkg-config" version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" dbus-tree-0.9.2/Cargo.toml0000644000000017770000000000000107470ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "dbus-tree" version = "0.9.2" authors = ["David Henningsson "] description = "Framework for writing D-Bus method handlers (legacy)" documentation = "http://docs.rs/dbus-tree" readme = "../README.md" keywords = ["D-Bus", "DBus", "IPC"] categories = ["os::unix-apis", "api-bindings"] license = "Apache-2.0/MIT" repository = "https://github.com/diwic/dbus-rs" [dependencies.dbus] version = "0.9" [badges.maintenance] status = "passively-maintained" dbus-tree-0.9.2/Cargo.toml.orig000064400000000000000000000010230000000000000143660ustar 00000000000000[package] name = "dbus-tree" version = "0.9.2" authors = ["David Henningsson "] edition = "2018" description = "Framework for writing D-Bus method handlers (legacy)" repository = "https://github.com/diwic/dbus-rs" documentation = "http://docs.rs/dbus-tree" keywords = ["D-Bus", "DBus", "IPC"] license = "Apache-2.0/MIT" categories = ["os::unix-apis", "api-bindings"] readme = "../README.md" [dependencies] dbus = { path = "../dbus", version = "0.9" } [badges] maintenance = { status = "passively-maintained" } dbus-tree-0.9.2/LICENSE-APACHE000064400000000000000000000261420000000000000134340ustar 00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2014-2018 David Henningsson and other contributors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. dbus-tree-0.9.2/LICENSE-MIT000064400000000000000000000021230000000000000131350ustar 00000000000000Copyright (c) 2014-2018 David Henningsson and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.dbus-tree-0.9.2/examples/adv_server.rs000064400000000000000000000136220000000000000160330ustar 00000000000000// More advanced server example. // This is supposed to look like a D-Bus service that allows the user to manipulate storage devices. // Note: in the dbus-codegen/example directory, there is a version of this example where dbus-codegen // was used to create some boilerplate code - feel free to compare the two examples. use std::sync::Arc; use std::sync::mpsc; use std::cell::Cell; use std::thread; use dbus_tree as tree; use dbus::Path; use dbus_tree::{Interface, Signal, MTFn, Access, MethodErr, EmitsChangedSignal}; use dbus::ffidisp::Connection; // Our storage device #[derive(Debug)] struct Device { description: String, path: Path<'static>, index: i32, online: Cell, checking: Cell, } // Every storage device has its own object path. // We therefore create a link from the object path to the Device. #[derive(Copy, Clone, Default, Debug)] struct TData; impl tree::DataType for TData { type Tree = (); type ObjectPath = Arc; type Property = (); type Interface = (); type Method = (); type Signal = (); } impl Device { // Creates a "test" device (not a real one, since this is an example). fn new_bogus(index: i32) -> Device { Device { description: format!("This is device {}, which is {}.", index, ["totally awesome", "really fancy", "still going strong"][(index as usize) % 3]), path: format!("/Device{}", index).into(), index: index, online: Cell::new(index % 2 == 0), checking: Cell::new(false), } } } // Here's where we implement the code for our interface. fn create_iface(check_complete_s: mpsc::Sender) -> (Interface, TData>, Arc>) { let f = tree::Factory::new_fn(); let check_complete = Arc::new(f.signal("CheckComplete", ())); (f.interface("com.example.dbus.rs.device", ()) // The online property can be both set and get .add_p(f.property::("online", ()) .access(Access::ReadWrite) .on_get(|i, m| { let dev: &Arc = m.path.get_data(); i.append(dev.online.get()); Ok(()) }) .on_set(|i, m| { let dev: &Arc = m.path.get_data(); let b: bool = i.read()?; if b && dev.checking.get() { return Err(MethodErr::failed(&"Device currently under check, cannot bring online")) } dev.online.set(b); Ok(()) }) ) // The "checking" property is read only .add_p(f.property::("checking", ()) .emits_changed(EmitsChangedSignal::False) .on_get(|i, m| { let dev: &Arc = m.path.get_data(); i.append(dev.checking.get()); Ok(()) }) ) // ...and so is the "description" property .add_p(f.property::<&str,_>("description", ()) .emits_changed(EmitsChangedSignal::Const) .on_get(|i, m| { let dev: &Arc = m.path.get_data(); i.append(&dev.description); Ok(()) }) ) // ...add a method for starting a device check... .add_m(f.method("check", (), move |m| { let dev: &Arc = m.path.get_data(); if dev.checking.get() { return Err(MethodErr::failed(&"Device currently under check, cannot start another check")) } if dev.online.get() { return Err(MethodErr::failed(&"Device is currently online, cannot start check")) } dev.checking.set(true); // Start some lengthy processing in a separate thread... let devindex = dev.index; let ch = check_complete_s.clone(); thread::spawn(move || { // Bogus check of device use std::time::Duration; thread::sleep(Duration::from_secs(15)); // Tell main thread that we finished ch.send(devindex).unwrap(); }); Ok(vec!(m.msg.method_return())) })) // Indicate that we send a special signal once checking has completed. .add_s(check_complete.clone()) , check_complete) } fn create_tree(devices: &[Arc], iface: &Arc, TData>>) -> tree::Tree, TData> { let f = tree::Factory::new_fn(); let mut tree = f.tree(()); for dev in devices { tree = tree.add(f.object_path(dev.path.clone(), dev.clone()) .introspectable() .add(iface.clone()) ); } tree } fn run() -> Result<(), Box> { // Create our bogus devices let devices: Vec> = (0..10).map(|i| Arc::new(Device::new_bogus(i))).collect(); // Create tree let (check_complete_s, check_complete_r) = mpsc::channel::(); let (iface, sig) = create_iface(check_complete_s); let tree = create_tree(&devices, &Arc::new(iface)); // Setup DBus connection let c = Connection::new_session()?; c.register_name("com.example.dbus.rs.advancedserverexample", 0)?; tree.set_registered(&c, true)?; // ...and serve incoming requests. c.add_handler(tree); loop { // Wait for incoming messages. This will block up to one second. // Discard the result - relevant messages have already been handled. c.incoming(1000).next(); // Do all other things we need to do in our main loop. if let Ok(idx) = check_complete_r.try_recv() { let dev = &devices[idx as usize]; dev.checking.set(false); c.send(sig.msg(&dev.path, &"com.example.dbus.rs.device".into())).map_err(|_| "Sending DBus signal failed")?; } } } fn main() { if let Err(e) = run() { println!("{}", e); } } dbus-tree-0.9.2/examples/server.rs000064400000000000000000000056020000000000000152000ustar 00000000000000/* This example creates a D-Bus server with the following functionality: It registers the "com.example.dbustest" name, creates a "/hello" object path, which has an "com.example.dbustest" interface. The interface has a "Hello" method (which takes no arguments and returns a string), and a "HelloHappened" signal (with a string argument) which is sent every time someone calls the "Hello" method. */ use std::sync::Arc; use dbus::blocking::LocalConnection; use dbus_tree::Factory; use std::error::Error; use std::time::Duration; fn main() -> Result<(), Box> { // Let's start by starting up a connection to the session bus and request a name. let c = LocalConnection::new_session()?; c.request_name("com.example.dbustest", false, true, false)?; // The choice of factory tells us what type of tree we want, // and if we want any extra data inside. We pick the simplest variant. let f = Factory::new_fn::<()>(); // We create the signal first, since we'll need it in both inside the method callback // and when creating the tree. let signal = Arc::new(f.signal("HelloHappened", ()).sarg::<&str,_>("sender")); let signal2 = signal.clone(); // We create a tree with one object path inside and make that path introspectable. let tree = f.tree(()).add(f.object_path("/hello", ()).introspectable().add( // We add an interface to the object path... f.interface("com.example.dbustest", ()).add_m( // ...and a method inside the interface. f.method("Hello", (), move |m| { // This is the callback that will be called when another peer on the bus calls our method. // the callback receives "MethodInfo" struct and can return either an error, or a list of // messages to send back. let name: &str = m.msg.read1()?; let s = format!("Hello {}!", name); let mret = m.msg.method_return().append1(s); let sig = signal.msg(m.path.get_name(), m.iface.get_name()) .append1(&*name); // Two messages will be returned - one is the method return (and should always be there), // and in our case we also have a signal we want to send at the same time. Ok(vec!(mret, sig)) // Our method has one output argument and one input argument. }).outarg::<&str,_>("reply") .inarg::<&str,_>("name") // We also add the signal to the interface. This is mainly for introspection. ).add_s(signal2) // Also add the root path, to help introspection from debugging tools. )).add(f.object_path("/", ()).introspectable()); // We add the tree to the connection so that incoming method calls will be handled. tree.start_receive(&c); // Serve clients forever. loop { c.process(Duration::from_millis(1000))?; } } dbus-tree-0.9.2/src/factory.rs000064400000000000000000000116670000000000000143220ustar 00000000000000use 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 dbus::strings::{Interface as IfaceName, Member}; use dbus::{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, D: DataType=()>(Arc>); impl, D: DataType> From>> for Factory { fn from(f: Arc>) -> Self { Factory(f) } } impl Factory, ()> { /// Creates a new factory for single-thread use. pub fn new_fn() -> Factory, D> { Factory(IfaceCache::new()) } /// Creates a new factory for single-thread use, where callbacks can mutate their environment. pub fn new_fnmut() -> Factory, D> { Factory(IfaceCache::new()) } /// Creates a new factory for multi-thread use. pub fn new_sync() -> Factory, D> { Factory(IfaceCache::new()) } } impl Factory, D> { /// Creates a new method for single-thread use. pub fn method(&self, t: T, data: D::Method, handler: H) -> Method, D> where H: 'static + Fn(&MethodInfo, D>) -> MethodResult, T: Into> { super::leaves::new_method(t.into(), data, Box::new(handler) as Box<_>) } } impl Factory, D> { /// Creates a new method for single-thread use. pub fn method(&self, t: T, data: D::Method, handler: H) -> Method, D> where H: 'static + FnMut(&MethodInfo, D>) -> MethodResult, T: Into> { super::leaves::new_method(t.into(), data, Box::new(RefCell::new(handler)) as Box<_>) } } impl Factory, D> { /// Creates a new method for multi-thread use. pub fn method(&self, t: T, data: D::Method, handler: H) -> Method, D> where H: Fn(&MethodInfo, D>) -> MethodResult + Send + Sync + 'static, T: Into> { super::leaves::new_method(t.into(), data, Box::new(handler) as Box<_>) } } impl, D: DataType> Factory { /// Creates a new property. /// /// `A` is used to calculate the type signature of the property. pub fn property>(&self, name: T, data: D::Property) -> Property { let sig = A::signature(); super::leaves::new_property(name.into(), sig, data) } /// Creates a new signal. pub fn signal>>(&self, name: T, data: D::Signal) -> Signal { super::leaves::new_signal(name.into(), data) } /// Creates a new interface. pub fn interface>>(&self, name: T, data: D::Interface) -> Interface { super::objectpath::new_interface(name.into(), data) } /// Creates a new object path. pub fn object_path>>(&self, name: T, data: D::ObjectPath) -> ObjectPath { super::objectpath::new_objectpath(name.into(), data, self.0.clone()) } /// Creates a new tree. pub fn tree(&self, data: D::Tree) -> Tree { 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(&self, t: T, data: D::Method, handler: H) -> Method where H: Fn(&MethodInfo) -> MethodResult + Send + Sync + 'static, T: Into> { 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; type Interface = (); type Property = (); type Method = i32; type Signal = (); } let f = Factory::new_fn::(); 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); } dbus-tree-0.9.2/src/leaves.rs000064400000000000000000000651710000000000000141310ustar 00000000000000// 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 dbus::strings::{Interface as IfaceName, Member, Signature, Path}; use dbus::{arg, Message}; use std::fmt; use std::cell::RefCell; use dbus::ffidisp::stdintf::org_freedesktop_dbus::PropertiesPropertiesChanged; // Workaround for https://github.com/rust-lang/rust/issues/31518 struct DebugMethod, D: DataType>(Box); impl, D: DataType> fmt::Debug for DebugMethod { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") } } struct DebugGetProp, D: DataType>(Box); impl, D: DataType> fmt::Debug for DebugGetProp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") } } struct DebugSetProp, D: DataType>(Box); impl, D: DataType> fmt::Debug for DebugSetProp { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") } } #[derive(Debug)] /// A D-Bus Method. pub struct Method, D: DataType> { cb: DebugMethod, data: D::Method, name: Member<'static>, i_args: Vec, o_args: Vec, anns: Annotations, } impl, D: DataType> Method { /// Builder method that adds an "in" Argument to this Method. pub fn in_arg>(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>(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, A: IntoIterator>(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>(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>(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, A: IntoIterator>(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, V: Into>(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) -> 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, D: DataType> Introspect for Method { 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, D: DataType>(n: Member<'static>, data: D::Method, cb: Box) -> Method { 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 { name: Member<'static>, data: D::Signal, arguments: Vec, anns: Annotations, } impl Signal { /// Builder method that adds an Argument to the Signal. pub fn arg>(mut self, a: A) -> Self { self.arguments.push(a.into()); self } /// Builder method that adds an Argument to the Signal. pub fn sarg>(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, A: IntoIterator>(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, V: Into>(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 list of arguments to send. pub fn emit(&self, p: &Path<'static>, i: &IfaceName<'static>, items: &[A]) -> Message { let mut m = self.msg(p, i); let mut ia = arg::IterAppend::new(&mut m); for a in items { a.append_by_ref(&mut ia) } m } /// Returns a message which emits the signal when sent. /// /// Same as "emit" but does not take an "items" argument. pub fn msg(&self, p: &Path<'static>, i: &IfaceName<'static>) -> Message { Message::signal(p, i, &self.name) } } impl Introspect for Signal { 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(n: Member<'static>, data: D::Signal) -> Signal { 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 + 'v, D: DataType + 'v, I: Iterator>> (iter: &mut arg::IterAppend, mut props: I, minfo: &MethodInfo) -> Result<(), MethodErr> { let mut result = Ok(()); iter.append_dict(&Signature::make::<&str>(), &Signature::make::>(), |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, D: DataType> { name: String, data: D::Property, sig: Signature<'static>, emits: EmitsChangedSignal, auto_emit: bool, rw: Access, get_cb: Option>, set_cb: Option>, anns: Annotations, } impl, D: DataType> Property { /// 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, V: Into>(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) -> 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) -> Result<(), MethodErr> { use dbus::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 = i.recurse(arg::Variant::::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) -> Result, MethodErr> { use dbus::arg::Arg; let mut subiter = i.recurse(arg::Variant::::ARG_TYPE).ok_or_else(|| MethodErr::invalid_arg(&2))?; 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) -> 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 Box>(&self, v: &mut Vec, 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) -> Result, 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); 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, _>::new(vec!()), arg::Array::new(Some(&*self.name).into_iter()) ))), } } } impl<'a, D: DataType> Property, D> { /// Sets the callback for getting a property. /// /// For single-thread use. pub fn on_get(mut self, handler: H) -> Property, D> where H: 'static + Fn(&mut arg::IterAppend, &PropInfo, 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(mut self, handler: H) -> Property, D> where H: 'static + Fn(&mut arg::Iter, &PropInfo, D>) -> Result<(), MethodErr> { self.set_cb = Some(DebugSetProp(Box::new(handler) as Box<_>)); self } } impl<'a, D: DataType> Property, D> { /// Sets the callback for getting a property. /// /// For single-thread use. pub fn on_get(mut self, handler: H) -> Property, D> where H: 'static + Fn(&mut arg::IterAppend, &PropInfo, 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(mut self, handler: H) -> Property, D> where H: 'static + Fn(&mut arg::Iter, &PropInfo, D>) -> Result<(), MethodErr> { self.set_cb = Some(DebugSetProp(Box::new(RefCell::new(handler)) as Box<_>)); self } } impl Property, D> { /// Sets the callback for getting a property. /// /// For multi-thread use. pub fn on_get(mut self, handler: H) -> Property, D> where H: Fn(&mut arg::IterAppend, &PropInfo, 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(mut self, handler: H) -> Property, D> where H: Fn(&mut arg::Iter, &PropInfo, D>) -> Result<(), MethodErr> + Send + Sync + 'static { self.set_cb = Some(DebugSetProp(Box::new(handler) as Box<_>)); self } } impl, D: DataType> Property 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| { i.append(p.prop.get_data()); Ok(()) }; self.get_cb = Some(DebugGetProp(M::make_getprop(g))); self } } impl, D: DataType> Property 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| { arg::RefArg::append(p.prop.get_data(),i); Ok(()) }; self.get_cb = Some(DebugGetProp(M::make_getprop(g))); self } } impl, D: DataType> Introspect for Property { 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, D: DataType> (n: String, sig: Signature<'static>, data: D::Property) -> Property { 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 crate::Factory; use std::collections::BTreeMap; use dbus::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::(); let tree = f.tree(()).add(f.object_path("/test", ()).introspectable().object_manager() .add(f.interface("com.example.test", ()) .add_p(f.property::("Value1", 5i32).default_get()) .add_p(f.property::("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"); msg.set_serial(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)); msg.set_serial(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"); msg.set_serial(4); let res = tree.handle(&msg).unwrap(); let d: Dict<&str, Variant, _> = 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); } #[test] fn test_get_managed_objects() { use std::collections::BTreeMap; use crate::Factory; use dbus::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::(); let tree = f.tree(()).add(f.object_path("/test", ()).introspectable().object_manager() .add(f.interface("com.example.test", ()) .add_p(f.property::("Value1", 5i32).default_get()) .add_p(f.property::("Value2", 9i32).default_get()) ) ).add(f.object_path("/test/subtest", ()).introspectable() .add(f.interface("com.example.subtest", ()) .add_p(f.property::("Value3", 7i32).default_get()) ) ); let mut msg = Message::new_method_call("com.example.test", "/test", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects").unwrap(); msg.set_serial(4); let res = tree.handle(&msg).unwrap(); let pdict: arg::Dict, _>, _>, _> = res[0].get1().unwrap(); let pmap: BTreeMap<_, _> = pdict.collect(); assert!(pmap.get(&Path::from("/test")).is_none()); let idict = pmap.get(&Path::from("/test/subtest")).unwrap(); let imap: BTreeMap<_, _> = idict.collect(); let propdict = imap.get("com.example.subtest").unwrap(); let propmap: BTreeMap<_, _> = propdict.collect(); assert_eq!(propmap.get("Value3"), Some(&arg::Variant(7i32))); } #[test] fn test_set_prop() { use crate::{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::("changes", ()) .on_get(move |i, _| { i.append(changes1.get()); Ok(()) })) .add_p(f.property::("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)); msg.set_serial(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)); msg.set_serial(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")); msg.set_serial(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, _>>) = 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 crate::{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::("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::().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)); msg.set_serial(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() .append1("com.example.syncprop").append1("syncprop"); msg.set_serial(4); let mut r = tree1.handle(&msg).unwrap(); let r = r[0].as_result().unwrap(); let z: arg::Variant = r.get1().unwrap(); if z.0 == 5 { break; } assert_eq!(z.0, 3); } assert_eq!(count.load(Ordering::SeqCst), 5); } dbus-tree-0.9.2/src/lib.rs000064400000000000000000000024200000000000000134040ustar 00000000000000//! Contains functionality for dispatching methods on a D-Bus "server". //! //! # Example //! ```rust,no_run //! use dbus_tree::Factory; //! use dbus::ffidisp::Connection; //! let f = 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().append1("Thanks!"))) } //! ).out_arg("s")) //! )); //! //! let c = Connection::new_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 dbus::MethodErr; pub use self::utils::{Argument, Iter}; pub use self::methodtype::{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; dbus-tree-0.9.2/src/methodtype.rs000064400000000000000000000206010000000000000150210ustar 00000000000000// Methods and method types. Glue to make stuff generic over MFn, MFnMut and MSync use std::fmt; use dbus::Message; use dbus::ffidisp::stdintf; use dbus::arg::{Iter, IterAppend}; use std::marker::PhantomData; use super::{Method, Interface, Property, ObjectPath, Tree}; use std::cell::RefCell; use dbus::MethodErr; /// Result containing the Messages returned from the Method, or a MethodErr. pub type MethodResult = Result, 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: 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) -> Result<(), MethodErr>; /// For internal use. fn call_setprop(_: &Self::SetProp, _: &mut Iter, _: &PropInfo) -> Result<(), MethodErr>; /// For internal use. fn call_method(_: &Self::Method, _: &MethodInfo) -> MethodResult; /// For internal use. fn make_getprop(h: H) -> Box where H: Fn(&mut IterAppend, &PropInfo) -> Result<(), MethodErr> + Send + Sync + 'static; /// For internal use. fn make_method(h: H) -> Box where H: Fn(&MethodInfo) -> MethodResult + Send + Sync + 'static; } /// An abstract type to represent Fn functions. #[derive(Default, Debug, Copy, Clone)] pub struct MTFn(PhantomData<*const D>); impl MethodType for MTFn { type GetProp = dyn Fn(&mut IterAppend, &PropInfo) -> Result<(), MethodErr>; type SetProp = dyn Fn(&mut Iter, &PropInfo) -> Result<(), MethodErr>; type Method = dyn Fn(&MethodInfo) -> MethodResult; fn call_getprop(p: &Self::GetProp, i: &mut IterAppend, pinfo: &PropInfo) -> Result<(), MethodErr> { p(i, pinfo) } fn call_setprop(p: &Self::SetProp, i: &mut Iter, pinfo: &PropInfo) -> Result<(), MethodErr> { p(i, pinfo) } fn call_method(p: &Self::Method, minfo: &MethodInfo) -> MethodResult { p(minfo) } fn make_getprop(h: H) -> Box where H: Fn(&mut IterAppend, &PropInfo) -> Result<(), MethodErr> + Send + Sync + 'static { Box::new(h) } fn make_method(h: H) -> Box where H: Fn(&MethodInfo) -> MethodResult + Send + Sync + 'static { Box::new(h) } } /// An abstract type to represent FnMut functions. #[derive(Default, Debug, Copy, Clone)] pub struct MTFnMut(PhantomData<*const D>); impl MethodType for MTFnMut { type GetProp = RefCell) -> Result<(), MethodErr>>; type SetProp = RefCell) -> Result<(), MethodErr>>; type Method = RefCell) -> MethodResult>; fn call_getprop(p: &Self::GetProp, i: &mut IterAppend, pinfo: &PropInfo) -> Result<(), MethodErr> { (&mut *p.borrow_mut())(i, pinfo) } fn call_setprop(p: &Self::SetProp, i: &mut Iter, pinfo: &PropInfo) -> Result<(), MethodErr> { (&mut *p.borrow_mut())(i, pinfo) } fn call_method(p: &Self::Method, minfo: &MethodInfo) -> MethodResult { (&mut *p.borrow_mut())(minfo) } fn make_getprop(h: H) -> Box where H: Fn(&mut IterAppend, &PropInfo) -> Result<(), MethodErr> + Send + Sync + 'static { Box::new(RefCell::new(h)) } fn make_method(h: H) -> Box where H: Fn(&MethodInfo) -> 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(PhantomData<*const D>); impl MethodType for MTSync { type GetProp = dyn Fn(&mut IterAppend, &PropInfo) -> Result<(), MethodErr> + Send + Sync + 'static; type SetProp = dyn Fn(&mut Iter, &PropInfo) -> Result<(), MethodErr> + Send + Sync + 'static; type Method = dyn Fn(&MethodInfo) -> MethodResult + Send + Sync + 'static; fn call_getprop(p: &Self::GetProp, i: &mut IterAppend, pinfo: &PropInfo) -> Result<(), MethodErr> { p(i, pinfo) } fn call_setprop(p: &Self::SetProp, i: &mut Iter, pinfo: &PropInfo) -> Result<(), MethodErr> { p(i, pinfo) } fn call_method(p: &Self::Method, minfo: &MethodInfo) -> MethodResult { p(minfo) } fn make_getprop(h: H) -> Box where H: Fn(&mut IterAppend, &PropInfo) -> Result<(), MethodErr> + Send + Sync + 'static { Box::new(h) } fn make_method(h: H) -> Box where H: Fn(&MethodInfo) -> 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: 'a + DataType> { /// Message pub msg: &'a Message, /// The method to be called pub method: &'a Method, /// Interface pub iface: &'a Interface, /// Object path pub path: &'a ObjectPath, /// Tree pub tree: &'a Tree, } impl<'a, M: 'a + MethodType, D: 'a + DataType> MethodInfo<'a, M, D> { /// MethodInfo to PropInfo conversion pub fn to_prop_info(&self, iface: &'a Interface, prop: &'a Property) -> 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: 'a + DataType> stdintf::OrgFreedesktopDBusIntrospectable for MethodInfo<'a, M, D> { type Err = MethodErr; fn introspect(&self) -> Result { Ok(self.path.introspect(self.tree)) } } // Mostly autogenerated by dbus-codegen pub fn org_freedesktop_dbus_introspectable_server(factory: &super::Factory, data: D::Interface) -> super::Interface where D: super::DataType, D::Method: Default, M: MethodType, { let i = factory.interface("org.freedesktop.DBus.Introspectable", data); let h = move |minfo: &super::MethodInfo| { let d: &dyn stdintf::OrgFreedesktopDBusIntrospectable = minfo; let arg0 = 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")); i.add_m(m) } #[derive(Debug, Copy, Clone)] /// Contains information about the incoming property get/set request. pub struct PropInfo<'a, M: 'a + MethodType, D: 'a + DataType> { /// Message pub msg: &'a Message, /// Get, Set or GetAll pub method: &'a Method, /// The property to be set/get pub prop: &'a Property, /// The interface the property belongs to pub iface: &'a Interface, /// Object path pub path: &'a ObjectPath, /// Tree pub tree: &'a Tree, } impl<'a, M: 'a + MethodType, 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 } } } dbus-tree-0.9.2/src/objectpath.rs000064400000000000000000000707230000000000000147740ustar 00000000000000use 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 dbus::{Message, MessageType, Error, arg, message, channel}; use dbus::strings::{Member, Path, Signature, Interface as IfaceName}; use dbus::ffidisp::{ConnectionItem, MsgHandler, Connection, MsgHandlerType, MsgHandlerResult}; use std::fmt; use super::leaves::prop_append_dict; use dbus::channel::Channel; use std::time::Duration; fn introspect_map (h: &ArcMap, 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.is_empty() { format!(">\n{}{}, D: DataType> { name: Arc>, methods: ArcMap, Method>, signals: ArcMap, Signal>, properties: ArcMap>, anns: Annotations, data: D::Interface, } impl, D: DataType> Interface { /// Builder function that adds a method to the interface. pub fn add_m>>>(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>>>(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>>>(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, V: Into>(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> { IterE::Member(self.methods.values()).into() } /// Iterates over signals implemented by this interface. pub fn iter_s<'a>(&'a self) -> Iter<'a, Signal> { IterE::Member(self.signals.values()).into() } /// Iterates over properties implemented by this interface. pub fn iter_p<'a>(&'a self) -> Iter<'a, Property> { IterE::String(self.properties.values()).into() } } impl, D: DataType> Introspect for Interface { 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, D: DataType>(t: IfaceName<'static>, d: D::Interface) -> Interface { 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, D: DataType>(Mutex, Interface>>); impl, D: DataType> IfaceCache where D::Interface: Default { pub fn get> + Clone, F>(&self, s: S, f: F) -> Arc> where F: FnOnce(Interface) -> Interface { 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, D: DataType> IfaceCache { pub fn get_factory> + Clone, F>(&self, s: S, f: F) -> Arc> where F: FnOnce() -> Interface { 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 { Arc::new(IfaceCache(Mutex::new(ArcMap::new()))) } } #[derive(Debug)] /// A D-Bus Object Path. pub struct ObjectPath, D: DataType> { name: Arc>, default_iface: Option>, ifaces: ArcMap>, Interface>, ifacecache: Arc>, data: D::ObjectPath, } impl, D: DataType> ObjectPath { /// 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> { IterE::Iface(self.ifaces.values()).into() } pub(super) fn introspect(&self, tree: &Tree) -> String { let ifacestr = introspect_map(&self.ifaces, " "); let olen = if &**self.name == "/" { 1 } else { self.name.len()+1 }; let childstr = tree.children(self, true).iter().fold("".to_string(), |na, n| format!("{} \n", na, &n.name[olen..]) ); let nodestr = format!(r##" {}{}"##, self.name, ifacestr, childstr); nodestr } fn get_iface<'a>(&'a self, iface_name: &'a str) -> Result<&Arc>, MethodErr> { let j = IfaceName::from_slice(iface_name).map_err(|e| MethodErr::invalid_arg(&e))?; self.ifaces.get(&j).ok_or_else(|| MethodErr::no_interface(&j)) } fn prop_get(&self, m: &MethodInfo) -> MethodResult { let (iname, prop_name): (&str, &str) = m.msg.read2()?; let iface = self.get_iface(iname)?; let prop: &Property = iface.properties.get(&String::from(prop_name)) .ok_or_else(|| MethodErr::no_property(&prop_name))?; 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); prop.get_as_variant(&mut iter, &pinfo)?; } Ok(vec!(mret)) } fn prop_get_all(&self, m: &MethodInfo) -> MethodResult { let iface = self.get_iface(m.msg.read1()?)?; let mut mret = m.msg.method_return(); 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) -> MethodResult { let (iname, prop_name): (&str, &str) = m.msg.read2()?; let iface = self.get_iface(iname)?; let prop: &Property = 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; prop.can_set(Some(iter))?; let pinfo = m.to_prop_info(iface, prop); let mut r: Vec = 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) -> MethodResult { use dbus::arg::{Dict, Variant}; let paths = m.tree.children(&self, false); let mut result = Ok(()); let mut r = m.msg.method_return(); { let mut i = arg::IterAppend::new(&mut r); i.append_dict(&Signature::make::(), &Signature::make::,()>,()>>(), |ii| { for p in paths { ii.append_dict_entry(|pi| { pi.append(&*p.name); pi.append_dict(&Signature::make::<&str>(), &Signature::make::,()>>(), |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; } } }); } result?; Ok(vec!(r)) } fn handle(&self, m: &Message, t: &Tree) -> MethodResult { let iname = m.interface().or_else(|| { self.default_iface.clone() }); let i = iname.and_then(|i| self.ifaces.get(&i)).ok_or_else(|| MethodErr::no_interface(&""))?; let me = 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, D: DataType> ObjectPath where ::Interface: Default, ::Method: Default, ::Signal: 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>>>(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 dbus::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::,()>,()>,()>,_>("objpath_interfaces_and_properties")) }); self.ifaces.insert(z.name.clone(), z); self } fn add_property_handler(&mut self) { use dbus::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::,_>("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::, ()>,_>("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::,_>("value")) .add_s(super::leaves::new_signal("PropertiesChanged".into(), Default::default()) .sarg::<&str, _>("interface_name") .sarg::, ()>, _>("changed_properties") .sarg::, _>("invalidated_properties")) }); self.ifaces.insert(z.name.clone(), z); } } pub fn new_objectpath, D: DataType>(n: Path<'static>, d: D::ObjectPath, cache: Arc>) -> ObjectPath { 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, D: DataType> { paths: ArcMap>, ObjectPath>, data: D::Tree, } impl, D: DataType> Tree { /// Builder function that adds an object path to this tree. pub fn add>>>(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>> { self.paths.get(p) } /// Iterates over object paths in this tree. pub fn iter<'a>(&'a self) -> Iter<'a, ObjectPath> { IterE::Path(self.paths.values()).into() } /// Non-builder function that adds an object path to this tree. pub fn insert>>>(&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. pub fn remove(&mut self, p: &Path<'static>) -> Option>> { // 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 to a ffidisp::Connection. 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>(&'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> { 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))))) } } /// Tries to handle an incoming message from the provided channel if there is one. If there isn't one, /// it will wait up to timeout pub fn process_channel(&self, channel: &Channel, timeout: Duration) -> Result { if let Some(msg) = channel.blocking_pop_message(timeout)? { if let Some(replies) = self.handle(&msg) { for r in replies { let _ = channel.send(r); } } else if let Some(reply) = dbus::channel::default_reply(&msg) { let _ = channel.send(reply); } Ok(true) } else { Ok(false) } } fn children(&self, o: &ObjectPath, direct_only: bool) -> Vec<&ObjectPath> { let parent: &str = &o.name; let plen = if parent == "/" { 1 } else { parent.len()+1 }; let mut r: Vec<&ObjectPath> = 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 { Some(&**v) } }).collect(); if direct_only { r.sort_by_key(|v| &**v.name); // println!("DEBUG before: {:?}", r.iter().map(|v| &**v.name).collect::>()); let mut prev: Option<&ObjectPath> = None; r.retain(|v| { let a = prev.map(|prev| !(v.name.starts_with(&**prev.name) && v.name.as_bytes().get(prev.name.len()) == Some(&b'/')) ).unwrap_or(true); if a { prev = Some(v); } a }); } r } /// Get associated data pub fn get_data(&self) -> &D::Tree { &self.data } } impl + 'static, D: DataType + 'static> Tree { /// Connects a SyncConnection with a Tree so that incoming method calls are handled. /// /// The tree needs to be of type MTSync. pub fn start_receive_sync(self, connection: &C) where C: channel::MatchingReceiver bool + Send + Sync>> + channel::Sender, D::Tree: Send + Sync, D::ObjectPath: Send + Sync, D::Interface: Send + Sync, D::Property: Send + Sync, D::Method: Send + Sync, D::Signal: Send + Sync, M::Method: Send + Sync, M::GetProp: Send + Sync, M::SetProp: Send + Sync, { connection.start_receive(message::MatchRule::new_method_call(), Box::new(move |msg, c| { if let Some(replies) = self.handle(&msg) { for r in replies { let _ = c.send(r); } } true })); } /// Connects a Connection with a Tree so that incoming method calls are handled. /// /// The tree needs to be of type MTSync. pub fn start_receive_send(self, connection: &C) where C: channel::MatchingReceiver bool + Send>> + channel::Sender, D::Tree: Send + Sync, D::ObjectPath: Send + Sync, D::Interface: Send + Sync, D::Property: Send + Sync, D::Method: Send + Sync, D::Signal: Send + Sync, M::Method: Send + Sync, M::GetProp: Send + Sync, M::SetProp: Send + Sync, { connection.start_receive(message::MatchRule::new_method_call(), Box::new(move |msg, c| { if let Some(replies) = self.handle(&msg) { for r in replies { let _ = c.send(r); } } true })); } /// Connects a LocalConnection with a Tree so that incoming method calls are handled. pub fn start_receive(self, connection: &C) where C: channel::MatchingReceiver bool>> + channel::Sender { connection.start_receive(message::MatchRule::new_method_call(), Box::new(move |msg, c| { if let Some(replies) = self.handle(&msg) { for r in replies { let _ = c.send(r); } } true })); } } pub fn new_tree, D: DataType>(d: D::Tree) -> Tree { Tree { paths: ArcMap::new(), data: d } } impl, D: DataType> MsgHandler for Tree { fn handle_msg(&mut self, msg: &Message) -> Option { self.handle(msg).map(|v| MsgHandlerResult { handled: true, done: false, reply: v }) } fn handler_type(&self) -> MsgHandlerType { MsgHandlerType::MsgType(MessageType::MethodCall) } } /* impl, D: DataType> MsgHandler for Arc> { fn handle_msg(&mut self, msg: &Message) -> Option { 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 + 'a, D: DataType + 'a> { iter: I, conn: &'a Connection, tree: &'a Tree, } impl<'a, I: Iterator, M: 'a + MethodType, D: DataType + 'a> Iterator for TreeServer<'a, I, M, D> { type Item = ConnectionItem; fn next(&mut self) -> Option { 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::("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::("EchoCount", ())) .add_s(f.signal("Echoed", ()).arg(("data", "s")).deprecated()) ); let actual_result = t.introspect(&f.tree(()).add(f.object_path("/echo/subpath2", ())).add(f.object_path("/echo/subpath", ()))); println!("\n=== Introspection XML start ===\n{}\n=== Introspection XML end ===", actual_result); let expected_result = r##" "##; assert_eq!(expected_result, actual_result); } #[test] fn test_introspection_dynamic() { let f = super::Factory::new_fn::<()>(); let tree = f .tree(()) .add(f.object_path("/", ()).introspectable()) .add(f.object_path("/foo/bar", ()).introspectable()) .add(f.object_path("/foo/bar/item1", ()).introspectable()); // For / only node /foo/bar is listed. let o = f.object_path("/", ()).introspectable(); let actual_result = o.introspect(&tree); println!("\n=== Introspection XML start ===\n{}\n=== Introspection XML end ===", actual_result); let expected_result = r##" "##; assert_eq!(expected_result, actual_result); // For /foo/bar node /foo/bar/item1 is listed. let o = f.object_path("/foo/bar", ()).introspectable(); let actual_result = o.introspect(&tree); println!("\n=== Introspection XML start ===\n{}\n=== Introspection XML end ===", actual_result); let expected_result = r##" "##; assert_eq!(expected_result, actual_result); // Dynamically add an object /foo/bar/item2. let tree = tree.add(f.object_path("/foo/bar/item2", ()).introspectable()); // Now, for / still only node /foo/bar is listed. let o = f.object_path("/", ()).introspectable(); let actual_result = o.introspect(&tree); println!("\n=== Introspection XML start ===\n{}\n=== Introspection XML end ===", actual_result); let expected_result = r##" "##; assert_eq!(expected_result, actual_result); // And for /foo/bar node /foo/bar/item2 is now listed too. let o = f.object_path("/foo/bar", ()).introspectable(); let actual_result = o.introspect(&tree); println!("\n=== Introspection XML start ===\n{}\n=== Introspection XML end ===", actual_result); let expected_result = r##" "##; assert_eq!(expected_result, actual_result); } dbus-tree-0.9.2/src/utils.rs000064400000000000000000000066200000000000000140040ustar 00000000000000// Small structs that don't have their own unit. use dbus::strings::{Signature, Member, Path, Interface as IfaceName}; use std::collections::{BTreeMap, btree_map}; use std::sync::Arc; pub type ArcMap = BTreeMap>; #[derive(Clone, Debug)] pub enum IterE<'a, V: 'a> { Path(btree_map::Values<'a, Arc>, Arc>), Iface(btree_map::Values<'a, Arc>, Arc>), Member(btree_map::Values<'a, Member<'static>, Arc>), String(btree_map::Values<'a, String, Arc>), } #[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> 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; fn next(&mut self) -> Option { 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, Signature<'static>); impl Argument { /// Create a new Argument. pub fn new(name: Option, 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_default(); format!("{}\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>); impl Annotations { pub fn new() -> Annotations { Annotations(None) } pub fn insert, V: Into>(&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!("{}{}\n", aa, indent, ak, av) })).unwrap_or_default() } } // Doesn't work, conflicting impls // impl> From for Argument impl From> 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, S: Into>> 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; }