diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/dbus | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/dbus')
41 files changed, 10317 insertions, 0 deletions
diff --git a/third_party/rust/dbus/.cargo-checksum.json b/third_party/rust/dbus/.cargo-checksum.json new file mode 100644 index 0000000000..ededf20e3b --- /dev/null +++ b/third_party/rust/dbus/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"ff5bac5ffe795109feabd656a03c823b02fcad9186f6e6a957c2fb7201cd1bd8","LICENSE-APACHE":"453745410e3be8cf25d56872ea2aec975a78e6c9f217443d0bf908a5bce7c8ff","LICENSE-MIT":"de3911c2d98c8bd2d701ee721347053d9b55995a11f9a8c955e44d3ca1b376bf","examples/adv_server.rs":"45526c1ebeabad80e5dd5e8ecf071ea7a5e4870c58e47fad194f849fa347ef3a","examples/argument_guide.md":"b9824fc2b20c6478b3d42195f7d2486d11401aa9f72267e81ec3878bd6882e95","examples/client.rs":"56579c1a4fc3386ca41ddb350d9283d270e31cdf117fb815c84b6b0dd6548335","examples/properties.rs":"dac7eef86e17af1bc4b8d37f431686d3cc1f5890214ea880894c7cbd04ba2ad7","examples/properties_msgitem.rs":"b51e2cd87dc38696e8f0f98bac974d802ba00d69e0aff1102460fdfbf4448ffc","examples/rtkit.rs":"20b46aecac84aa90f0daa423dd9e2c118f1195ac777732bc4cedf9a0d85c4861","examples/server.rs":"9c286b26e45990cab5a7300bb5dd20048dd4dde6366d0e1cb6cdc85b7bf76eb6","examples/unity_focused_window.rs":"f1098e4ebce0feda47af862c6f3e755d6d74f78928731a75f15416bd76577fe1","src/arg/array_impl.rs":"bd18d93fe37a8053c0eb6e4e2d29cfc619adec69ba5ee3411cf03f6ad01fa0c0","src/arg/basic_impl.rs":"7c596da14de18220eb2258c80bc6eb0db9a7cd72bc85ba8d60da78cbf0232454","src/arg/mod.rs":"63274e49ed6f6dd632349e336504e0508d191f35403a7eec2ea20fee7e4ed4a2","src/arg/msgarg.rs":"1df0674603ef7d3e6d3fc1674aa7ffe6a048c3631ea76b533757b9daed958a7b","src/arg/variantstruct_impl.rs":"d9ddc73c44f1e57b67a7a73e8204f6915b51365cd47b1caf68573eb802b93a01","src/connection.rs":"8178688223b392ba5c29bb66b7e89ab642a129da7c777e34014ff9fb6a0daae8","src/connection2.rs":"ef0ba9ace17c8465bf25f3b8d97666e4ab1a6db048653cde408f946206592dcf","src/crossroads/crossroads.rs":"a1f17a5359bb38a54c89553f08c41b2268265fcd195f757c8f409c4ea1b43272","src/crossroads/handlers.rs":"21fbc36bb79aeeb02ac05b11f02ababd544b019d3ec258d73894344fc742c58a","src/crossroads/info.rs":"88a1648d367d0b8a4870bf2fc276c576ad60a23976e2f67f6d784f2dc0c3bc28","src/crossroads/mod.rs":"3e7e03ace37f0a18422d4a724b9db166c114216b2e6d1991ca9453380aa42dff","src/crossroads/stdimpl.rs":"1ded85e9efa18e55d3e65a8cc253b0544869093723157a99b8f7bdd7c3d3149f","src/dispatcher.rs":"b98c29a2063d6e9b263854256487dedf644d3fdb9071623f1e30e7132abedc7a","src/lib.rs":"2fad04e6f162e8121a72c0bad95b77ceabf4bc1c4a86c648602a929983272dc4","src/matchrule.rs":"502fc71bfb5c8bec2b3f0b92d95679513e656b0aa89094164223042fac26435b","src/message.rs":"d07db0c6208f5057961e904e5da8aa12352d0a1d1917c07d0ab0d18a981637aa","src/methoddisp.rs":"57d5b0e7eef7b76393dfd80e2e5125b266521f3252edbc402b893f12791b933e","src/objpath.rs":"e9f662d6cc81ea0717553787773261e7fb3221b36fab1799fb281f21de961b72","src/prop.rs":"60541e569ff06f503d889a7b62dd0aa980d39be60cde01f917503a80e0b7b27f","src/signalargs.rs":"7f0a380d51788208d153e2b51a1759d43a9e4bc41b38b3268a31cd9d681b0cce","src/stdintf.rs":"29027f8cffde7354fc432b306a3ade149bc12440f08fe86d1a72074d514d7101","src/strings.rs":"49635568c8e7f5993e67bfc72941f4ef9407671c63d57c37489620386fbed724","src/tree/factory.rs":"77bfdfa9074e653b43e9c51b3e0b2dcfc9bc02948b7f9eb8e2fc0fe1a9009d14","src/tree/leaves.rs":"9c8b07925c07424a3977b8effa154e63d32addcbab1f59331025039592705bc1","src/tree/methodtype.rs":"6e77276ad1ca4040cd4bea320f525e49165e1d21df89281de4d9b1913129e0e2","src/tree/mod.rs":"471ff6e4bbd9c360326ed92867744b78235d769ba4483e0a8c20137153ab1e82","src/tree/objectpath.rs":"727fa86029f3ebd1d5de183e8f105deb1b3a273f1f89d0e02a5b227c6b1ff515","src/tree/utils.rs":"d15e64ed288ffa7477c2c35532d7f61feafb50ef6068606014101622bcd36caa","src/watch.rs":"9f323ea92206c77e184169e4b7f2a01f2014bf761d3aa5228431109283a1a4fe"},"package":"48b5f0f36f1eebe901b0e6bee369a77ed3396334bf3f09abd46454a576f71819"}
\ No newline at end of file diff --git a/third_party/rust/dbus/Cargo.toml b/third_party/rust/dbus/Cargo.toml new file mode 100644 index 0000000000..e1d7e7c49e --- /dev/null +++ b/third_party/rust/dbus/Cargo.toml @@ -0,0 +1,38 @@ +# 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] +name = "dbus" +version = "0.6.5" +authors = ["David Henningsson <diwic@ubuntu.com>"] +description = "Bindings to D-Bus, which is a bus commonly used on Linux for inter-process communication." +documentation = "http://docs.rs/dbus" +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.libc] +version = "0.2.7" + +[dependencies.libdbus-sys] +version = "0.2" +[dev-dependencies.tempdir] +version = "0.3" + +[features] +no-string-validation = [] +[badges.is-it-maintained-issue-resolution] +repository = "diwic/dbus-rs" + +[badges.is-it-maintained-open-issues] +repository = "diwic/dbus-rs" diff --git a/third_party/rust/dbus/LICENSE-APACHE b/third_party/rust/dbus/LICENSE-APACHE new file mode 100644 index 0000000000..6003c7dd9a --- /dev/null +++ b/third_party/rust/dbus/LICENSE-APACHE @@ -0,0 +1,202 @@ +Apache 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 <diwic@ubuntu.com> 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. + diff --git a/third_party/rust/dbus/LICENSE-MIT b/third_party/rust/dbus/LICENSE-MIT new file mode 100644 index 0000000000..aec16d2daa --- /dev/null +++ b/third_party/rust/dbus/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright (c) 2014-2018 David Henningsson <diwic@ubuntu.com> 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.
\ No newline at end of file diff --git a/third_party/rust/dbus/examples/adv_server.rs b/third_party/rust/dbus/examples/adv_server.rs new file mode 100644 index 0000000000..a71fc39a6a --- /dev/null +++ b/third_party/rust/dbus/examples/adv_server.rs @@ -0,0 +1,177 @@ +// 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. + +extern crate dbus; + +use std::sync::Arc; +use std::sync::mpsc; +use std::cell::Cell; +use std::thread; + +use dbus::{Connection, BusType, tree, Path}; +use dbus::tree::{Interface, Signal, MTFn, Access, MethodErr, EmitsChangedSignal}; + +// Our storage device +#[derive(Debug)] +struct Device { + description: String, + path: Path<'static>, + index: i32, + online: Cell<bool>, + checking: Cell<bool>, +} + +// 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<Device>; + 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<i32>) -> (Interface<MTFn<TData>, TData>, Arc<Signal<TData>>) { + 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::<bool,_>("online", ()) + .access(Access::ReadWrite) + .on_get(|i, m| { + let dev: &Arc<Device> = m.path.get_data(); + i.append(dev.online.get()); + Ok(()) + }) + .on_set(|i, m| { + let dev: &Arc<Device> = m.path.get_data(); + let b: bool = try!(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::<bool,_>("checking", ()) + .emits_changed(EmitsChangedSignal::False) + .on_get(|i, m| { + let dev: &Arc<Device> = 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<Device> = 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<Device> = 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<Device>], iface: &Arc<Interface<MTFn<TData>, TData>>) + -> tree::Tree<MTFn<TData>, 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<std::error::Error>> { + // Create our bogus devices + let devices: Vec<Arc<Device>> = (0..10).map(|i| Arc::new(Device::new_bogus(i))).collect(); + + // Create tree + let (check_complete_s, check_complete_r) = mpsc::channel::<i32>(); + let (iface, sig) = create_iface(check_complete_s); + let tree = create_tree(&devices, &Arc::new(iface)); + + // Setup DBus connection + let c = try!(Connection::get_private(BusType::Session)); + try!(c.register_name("com.example.dbus.rs.advancedserverexample", 0)); + try!(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); + try!(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); + } +} diff --git a/third_party/rust/dbus/examples/argument_guide.md b/third_party/rust/dbus/examples/argument_guide.md new file mode 100644 index 0000000000..b36f2e937a --- /dev/null +++ b/third_party/rust/dbus/examples/argument_guide.md @@ -0,0 +1,193 @@ +Preamble +-------- + +The different ways you can append and get message arguments can be a bit bewildering. I've iterated a few times on the design and didn't want to lose backwards compatibility. + +This guide is to help you on your way. In addition, many of the examples in the examples directory append and read arguments. + +Code generation +--------------- + +First - if you can get D-Bus introspection data, you can use the the `dbus-codegen` tool to generate some boilerplate code for you. E g, if you want to talk to NetworkManager: + +```rust +cargo install dbus-codegen +dbus-codegen-rust -s -g -m None -d org.freedesktop.NetworkManager -p /org/freedesktop/NetworkManager > networkmanager.rs +``` + +You would then use this code like: + +```rust +// main.rs +mod networkmanager; + +/* ... */ + +// Start a connection to the system bus. +let c = Connection::get_private(BusType::System)?; + +// Make a "ConnPath" struct that just contains a Connection, a destination and a path. +let p = c.with_path("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", 5000); + +// Bring our generated code into scope. +use networkmanager::OrgFreedesktopNetworkManager; + +// Now we can call methods on our connpath from the "org.freedesktop.NetworkManager" interface. +let devices = c.get_all_devices()?; +``` + +There is also pre-generated code for standard D-Bus interfaces in the `stdintf` module. A similar example: + +```rust +let c = Connection::get_private(BusType::Session)?; + +// Make a "ConnPath" struct that just contains a Connection, a destination and a path. +let p = c.with_path("org.mpris.MediaPlayer2.rhythmbox", "/org/mpris/MediaPlayer2", 5000); + +// The ConnPath struct implements many traits, e g `org.freedesktop.DBus.Properties`. Bring the trait into scope. +use stdintf::org_freedesktop_dbus::Properties; + +// Now we can call org.freedesktop.DBus.Properties.Get just like an ordinary method and get the result back. +let metadata = p.get("org.mpris.MediaPlayer2.Player", "Metadata")?; +``` + +For more details, see `dbus-codegen-rust --help` and the `README.md` in the dbus-codegen directory. + +Now, if you want to make a service yourself, the generated code is more complex. And for some use cases, codegen isn't really an option, so let's move on: + +Append / get basic types +------------------------ + +If you just want to get/append simple types, just use `append1` / `append2` / `append3`, and +`read1` / `read2` / `read3`. The imaginary method below takes one byte parameter and one string parameter, and returns one string parameter and one int parameter. + +```rust +let m = Message::new_method_call(dest, path, intf, member)?.append2(5u8, "Foo"); +let r = c.send_with_reply_and_block(m, 2000)?; +let (data1, data2): (&str, i32) = c.read2()?; +``` + +Arrays and dictionaries +----------------------- + +D-Bus arrays and dictionaries usually correspond to `Vec` and `HashMap`. You can just append and get them like basic types: + +```rust +let v = vec![3i32, 4i32, 5i32]; +let mut map = HashMap::new(); +map.insert("Funghi", 5u16); +map.insert("Mold", 8u16); + +let m = Message::new_method_call(dest, path, intf, member)?.append2(v, map); +let r = c.send_with_reply_and_block(m, 2000)?; +let (data1, data2): (Vec<i32>, HashMap<&str, u16>) = r.read2()?; +``` + +Or combine them as you wish, e g, use a `Vec<Vec<u8>>`, a `HashMap<u64, Vec<String>>` or `HashMap<String, HashMap<String, i32>>` to construct more difficult types. + +Slices can sometimes be used as arrays - e g, `&[&str]` can be appended, but only very simple types can be used with `get` and `read`, e g `&[u8]`. + +This is the easiest way to get started, but in case you want to avoid the overhead of creating `Vec` or `HashMap`s, the "Array and Dict types" and "Iter / IterAppend" sections offer useful alternatives. + +Variants +-------- + +Things are getting slightly more complex with Variants, because they are not strongly typed and thus not fit as well into Rust's strongly typed as arrays and dicts. + +If you know the type beforehand, it's still easy: + +```rust +let v = Variant("This is a variant containing a &str"); +let m = Message::new_method_call(dest, path, intf, member)?.append1(v); +let r = c.send_with_reply_and_block(m, 2000)?; +let z: Variant<i32> = r.read1()?; +println!("Method returned {}", z.0); +``` + +The `Variant` struct is just a wrapper with a public interior, so you can easily both read from it and write to it with the `.0` accessor. + +Sometimes you don't know the type beforehand. We can solve this in two ways (choose whichever is more appropriate for your use case), either through the trait object `Box<RefArg>` or through `Iter` / `IterAppend` (see later sections). + +Through trait objects: + +```rust +let x = Box::new(5000i32) as Box<RefArg>; +let m = Message::new_method_call(dest, path, intf, member)?.append1(Variant(x)); +let r = c.send_with_reply_and_block(m, 2000)?; +let z: Variant<Box<RefArg>> = r.read1()?; +``` + +Ok, so we retrieved our `Box<RefArg>`. We now need to use the `RefArg` methods to probe it, to see what's inside. Easiest is to use `as_i64` or `as_str` if you want to test for integer or string types. Use `as_iter` if the variant contains a complex type you need to iterate over. +For floating point values, use `arg::cast` (this requires that the RefArg is `static` though, due to Rust type system limitations). +Match over `arg_type` if you need to know the exact type. + + +```rust +let z: Variant<Box<RefArg + 'static>> = r.read1()?; +let value = &z.0; + +if let Some(s) = value.as_str() { println!("It's a string: {}", s); } +else if let Some(i) = value.as_i64() { println!("It's an integer: {}", i); } +else if let Some(f) = arg::cast::<f64>(value) { println!("It's a float: {}", f); } +else { println!("Don't know how to handle a {:?}", value.arg_type()) } +``` + +Dicts and variants are sometimes combined, e g, you might need to read a D-Bus dictionary of String to Variants. You can then read these as `HashMap<String, Variant<Box<RefArg>>>`. + +Structs +------- + +D-Bus structs are implemented as Rust tuples. You can append and get tuples like you do with other types of arguments. + +TODO: Example + +Declare method arguments +------------------------ + +When you make a `Tree`, you want to declare what input and output arguments your method expects - so that correct D-Bus introspection data can be generated. You'll use the same types as you learned earlier in this guide: + +```rust +factory.method( /* ... */ ) +.inarg::<HashMap<i32, Vec<(i32, bool, String)>>,_>("request") +.outarg::<&str,_>("reply") +``` + +The types are just for generating a correct signature, they are never instantiated. Many different types can generate the same signature - e g, `Array<u8, _>`, `Vec<u8>` and `&[u8]` will all generate the same signature. `Variant` will generate the same type signature regardless of what's inside, so just write `Variant<()>` for simplicity. + + +Iter / IterAppend +----------------- + +Iter and IterAppend are more low-level, direct methods to get and append arguments. They can, e g, come handy if you have more than five arguments to read. + +E g, for appending a variant with IterAppend you can use `IterAppend::new(&msg).append_variant(|i| i.append(5000i32))` to append what you need to your variant inside the closure. +To read a variant you can use `let i = msg.read1::<Variant<Iter>>::()?` and then examine the methods on `i.0` to probe the variant. + +Array and Dict types +-------------------- + +These provide slightly better flexibility than using `Vec` and `HashMap` by instead integrating with `Iterator`. Here's an example where you can append and get a dictionary without having to create a HashMap: + +```rust +let x = &[("Hello", true), ("World", false)]; +let m = Message::new_method_call(dest, path, intf, member)?.append1(Dict::new(x)); +let r = c.send_with_reply_and_block(m, 2000)?; +let z: Dict<i32, &str, _> = r.read1()?; +for (key, value) in z { /* do something */ } +``` + +An edge case where this is necessary is having floating point keys in a dictionary - this is supported in D-Bus but not in Rust's `HashMap`. I have never seen this in practice, though. + +Unusual types +------------- + +The types `Path`, `Signature` and `OwnedFd` are not often used, but they can be appended and read as other argument types. `Path` and `Signature` will return strings with a borrowed lifetime - use `.into_static()` if you want to untie that lifetime. + +For `OwnedFd`, which a wrapper around a file descriptor, remember that the file descriptor will be closed when it goes out of scope. + +MessageItem +----------- + +MessageItem was the first design - an enum representing a D-Bus argument. It still works, but I doubt you'll ever need to use it. Newer methods provide better type safety, speed, and ergonomics. + + diff --git a/third_party/rust/dbus/examples/client.rs b/third_party/rust/dbus/examples/client.rs new file mode 100644 index 0000000000..a8707013ed --- /dev/null +++ b/third_party/rust/dbus/examples/client.rs @@ -0,0 +1,14 @@ +extern crate dbus; + +use dbus::{Connection, BusType, Message}; +use dbus::arg::Array; + +fn main() { + let c = Connection::get_private(BusType::Session).unwrap(); + let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap(); + let r = c.send_with_reply_and_block(m, 2000).unwrap(); + // ListNames returns one argument, which is an array of strings. + let arr: Array<&str, _> = r.get1().unwrap(); + for name in arr { println!("{}", name); } +} + diff --git a/third_party/rust/dbus/examples/properties.rs b/third_party/rust/dbus/examples/properties.rs new file mode 100644 index 0000000000..a410fc2ec1 --- /dev/null +++ b/third_party/rust/dbus/examples/properties.rs @@ -0,0 +1,50 @@ +extern crate dbus; + +use dbus::{Connection, BusType, stdintf, arg}; +use std::collections::HashMap; + +fn print_refarg(value: &arg::RefArg) { + // We don't know what type the value is. We'll try a few and fall back to + // debug printing if the value is more complex than that. + if let Some(s) = value.as_str() { println!("{}", s); } + else if let Some(i) = value.as_i64() { println!("{}", i); } + else { println!("{:?}", value); } +} + +fn main() { + // Connect to server and create a ConnPath. A ConnPath implements several interfaces, + // in this case we'll use OrgFreedesktopDBusProperties, which allows us to call "get". + let c = Connection::get_private(BusType::Session).unwrap(); + let p = c.with_path("org.mpris.MediaPlayer2.rhythmbox", "/org/mpris/MediaPlayer2", 5000); + use stdintf::org_freedesktop_dbus::Properties; + + // The Metadata property is a Dict<String, Variant>. + + // Option 1: we can get the dict straight into a hashmap, like this: + + let metadata: HashMap<String, arg::Variant<Box<arg::RefArg>>> = p.get("org.mpris.MediaPlayer2.Player", "Metadata").unwrap(); + + println!("Option 1:"); + + // We now iterate over the hashmap. + for (key, value) in metadata.iter() { + print!(" {}: ", key); + print_refarg(&value); + } + + + // Option 2: we can get the entire dict as a RefArg and get the values out by iterating over it. + + let metadata: Box<arg::RefArg> = p.get("org.mpris.MediaPlayer2.Player", "Metadata").unwrap(); + + // When using "as_iter()" for a dict, we'll get one key, it's value, next key, it's value, etc. + let mut iter = metadata.as_iter().unwrap(); + + println!("Option 2:"); + while let Some(key) = iter.next() { + // Printing the key is easy, since we know it's a String. + print!(" {}: ", key.as_str().unwrap()); + let value = iter.next().unwrap(); + print_refarg(&value); + } +} diff --git a/third_party/rust/dbus/examples/properties_msgitem.rs b/third_party/rust/dbus/examples/properties_msgitem.rs new file mode 100644 index 0000000000..86e1aa5086 --- /dev/null +++ b/third_party/rust/dbus/examples/properties_msgitem.rs @@ -0,0 +1,10 @@ +extern crate dbus; + +use dbus::{Connection, BusType, Props}; + +fn main() { + let c = Connection::get_private(BusType::System).unwrap(); + let p = Props::new(&c, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", + "org.freedesktop.PolicyKit1.Authority", 10000); + println!("BackendVersion: {:?}", p.get("BackendVersion").unwrap()) +} diff --git a/third_party/rust/dbus/examples/rtkit.rs b/third_party/rust/dbus/examples/rtkit.rs new file mode 100644 index 0000000000..2717d417b9 --- /dev/null +++ b/third_party/rust/dbus/examples/rtkit.rs @@ -0,0 +1,67 @@ +/* This example asks the rtkit service to make our thread realtime priority. + Rtkit puts a few limitations on us to let us become realtime, such as setting + RLIMIT_RTTIME correctly, hence the syscalls. */ + +extern crate dbus; +extern crate libc; + +use std::cmp; + +use dbus::{Connection, BusType, Props, MessageItem, Message}; + +fn item_as_i64(i: MessageItem) -> Result<i64, Box<std::error::Error>> { + match i { + MessageItem::Int32(i) => Ok(i as i64), + MessageItem::Int64(i) => Ok(i), + _ => Err(Box::from(&*format!("Property is not integer ({:?})", i))) + } +} + +fn rtkit_set_realtime(c: &Connection, thread: u64, prio: u32) -> Result<(), ::dbus::Error> { + let mut m = Message::new_method_call("org.freedesktop.RealtimeKit1", "/org/freedesktop/RealtimeKit1", + "org.freedesktop.RealtimeKit1", "MakeThreadRealtime").unwrap(); + m.append_items(&[thread.into(), prio.into()]); + let mut r = try!(c.send_with_reply_and_block(m, 10000)); + r.as_result().map(|_| ()) +} + +fn make_realtime(prio: u32) -> Result<u32, Box<std::error::Error>> { + let c = try!(Connection::get_private(BusType::System)); + + let p = Props::new(&c, "org.freedesktop.RealtimeKit1", "/org/freedesktop/RealtimeKit1", + "org.freedesktop.RealtimeKit1", 10000); + + // Make sure we don't fail by wanting too much + let max_prio = try!(item_as_i64(try!(p.get("MaxRealtimePriority")))) as u32; + let prio = cmp::min(prio, max_prio); + + // Enforce RLIMIT_RTPRIO, also a must before asking rtkit for rtprio + let max_rttime = try!(item_as_i64(try!(p.get("RTTimeUSecMax")))) as u64; + let new_limit = libc::rlimit64 { rlim_cur: max_rttime, rlim_max: max_rttime }; + let mut old_limit = new_limit; + if unsafe { libc::getrlimit64(libc::RLIMIT_RTTIME, &mut old_limit) } < 0 { + return Err(Box::from("getrlimit failed")); + } + if unsafe { libc::setrlimit64(libc::RLIMIT_RTTIME, &new_limit) } < 0 { + return Err(Box::from("setrlimit failed")); + } + + // Finally, let's ask rtkit to make us realtime + let thread_id = unsafe { libc::syscall(libc::SYS_gettid) }; + let r = rtkit_set_realtime(&c, thread_id as u64, prio); + + if r.is_err() { + unsafe { libc::setrlimit64(libc::RLIMIT_RTTIME, &old_limit) }; + } + + try!(r); + Ok(prio) +} + + +fn main() { + match make_realtime(5) { + Ok(n) => println!("Got rtprio, level {}", n), + Err(e) => println!("No rtprio: {}", e), + } +} diff --git a/third_party/rust/dbus/examples/server.rs b/third_party/rust/dbus/examples/server.rs new file mode 100644 index 0000000000..105593fccc --- /dev/null +++ b/third_party/rust/dbus/examples/server.rs @@ -0,0 +1,72 @@ +/* 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. +*/ + + +extern crate dbus; + +use std::sync::Arc; +use dbus::{Connection, BusType, NameFlag}; +use dbus::tree::Factory; + +fn main() { + // Let's start by starting up a connection to the session bus and register a name. + let c = Connection::get_private(BusType::Session).unwrap(); + c.register_name("com.example.dbustest", NameFlag::ReplaceExisting as u32).unwrap(); + + // 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) + )); + + // We register all object paths in the tree. + tree.set_registered(&c, true).unwrap(); + + // We add the tree to the connection so that incoming method calls will be handled + // automatically during calls to "incoming". + c.add_handler(tree); + + // Serve other peers forever. + loop { c.incoming(1000).next(); } +} diff --git a/third_party/rust/dbus/examples/unity_focused_window.rs b/third_party/rust/dbus/examples/unity_focused_window.rs new file mode 100644 index 0000000000..a0e07d0372 --- /dev/null +++ b/third_party/rust/dbus/examples/unity_focused_window.rs @@ -0,0 +1,25 @@ +extern crate dbus; + +// Tracks currently focused window under the Unity desktop by listening to the +// FocusedWindowChanged signal. The signal contains "window_id", "app_id" and "stage", +// we print only "app_id". + +use dbus::{Connection, BusType, ConnectionItem}; + +fn focus_msg(ci: &ConnectionItem) -> Option<&str> { + let m = if let &ConnectionItem::Signal(ref s) = ci { s } else { return None }; + if &*m.interface().unwrap() != "com.canonical.Unity.WindowStack" { return None }; + if &*m.member().unwrap() != "FocusedWindowChanged" { return None }; + let (_, app) = m.get2::<u32, &str>(); + app +} + +fn main() { + let c = Connection::get_private(BusType::Session).unwrap(); + c.add_match("interface='com.canonical.Unity.WindowStack',member='FocusedWindowChanged'").unwrap(); + + for i in c.iter(1000) { + if let Some(app) = focus_msg(&i) { println!("{} has now focus.", app) }; + } +} + diff --git a/third_party/rust/dbus/src/arg/array_impl.rs b/third_party/rust/dbus/src/arg/array_impl.rs new file mode 100644 index 0000000000..8bccf347a4 --- /dev/null +++ b/third_party/rust/dbus/src/arg/array_impl.rs @@ -0,0 +1,483 @@ +use super::*; +use {Signature, Path, Message, ffi, OwnedFd}; +use std::marker::PhantomData; +use std::{ptr, mem, any, fmt}; +use super::check; +use std::ffi::{CString}; +use std::os::raw::{c_void, c_int}; +use std::collections::HashMap; +use std::hash::Hash; + +// Map DBus-Type -> Alignment. Copied from _dbus_marshal_write_fixed_multi in +// http://dbus.freedesktop.org/doc/api/html/dbus-marshal-basic_8c_source.html#l01020 +// Note that Rust booleans are one byte, dbus booleans are four bytes! +const FIXED_ARRAY_ALIGNMENTS: [(ArgType, usize); 9] = [ + (ArgType::Byte, 1), + (ArgType::Int16, 2), + (ArgType::UInt16, 2), + (ArgType::UInt32, 4), + (ArgType::Int32, 4), + (ArgType::Boolean, 4), + (ArgType::Int64, 8), + (ArgType::UInt64, 8), + (ArgType::Double, 8) +]; + +/// Represents a D-Bus array. +impl<'a, T: Arg> Arg for &'a [T] { + const ARG_TYPE: ArgType = ArgType::Array; + fn signature() -> Signature<'static> { Signature::from(format!("a{}", T::signature())) } +} + +fn array_append<T: Arg, F: FnMut(&T, &mut IterAppend)>(z: &[T], i: &mut IterAppend, mut f: F) { + let zptr = z.as_ptr(); + let zlen = z.len() as i32; + + // Can we do append_fixed_array? + let a = (T::ARG_TYPE, mem::size_of::<T>()); + let can_fixed_array = (zlen > 1) && (z.len() == zlen as usize) && FIXED_ARRAY_ALIGNMENTS.iter().any(|&v| v == a); + + i.append_container(ArgType::Array, Some(T::signature().as_cstr()), |s| + if can_fixed_array { unsafe { check("dbus_message_iter_append_fixed_array", + ffi::dbus_message_iter_append_fixed_array(&mut s.0, a.0 as c_int, &zptr as *const _ as *const c_void, zlen)) }} + else { for arg in z { f(arg, s); }} + ); +} + +/// Appends a D-Bus array. Note: In case you have a large array of a type that implements FixedArray, +/// using this method will be more efficient than using an Array. +impl<'a, T: Arg + Append + Clone> Append for &'a [T] { + fn append(self, i: &mut IterAppend) { + array_append(self, i, |arg, s| arg.clone().append(s)); + } +} + +impl<'a, T: Arg + RefArg> RefArg for &'a [T] { + fn arg_type(&self) -> ArgType { ArgType::Array } + fn signature(&self) -> Signature<'static> { Signature::from(format!("a{}", <T as Arg>::signature())) } + fn append(&self, i: &mut IterAppend) { + array_append(self, i, |arg, s| (arg as &RefArg).append(s)); + } + #[inline] + fn as_any(&self) -> &any::Any where Self: 'static { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } + + fn box_clone(&self) -> Box<RefArg + 'static> { + Box::new(InternalArray { + inner_sig: <T as Arg>::signature(), + data: self.iter().map(|x| x.box_clone()).collect(), + }) + } +} + +impl<T: Arg + RefArg> RefArg for Vec<T> { + fn arg_type(&self) -> ArgType { ArgType::Array } + fn signature(&self) -> Signature<'static> { Signature::from(format!("a{}", <T as Arg>::signature())) } + fn append(&self, i: &mut IterAppend) { + array_append(&self, i, |arg, s| (arg as &RefArg).append(s)); + } + #[inline] + fn as_any(&self) -> &any::Any where Self: 'static { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } + fn as_iter<'a>(&'a self) -> Option<Box<Iterator<Item=&'a RefArg> + 'a>> { + Some(Box::new(self.iter().map(|b| b as &RefArg))) + } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { (&**self).box_clone() } +} + + +impl<'a, T: FixedArray> Get<'a> for &'a [T] { + fn get(i: &mut Iter<'a>) -> Option<&'a [T]> { + debug_assert!(FIXED_ARRAY_ALIGNMENTS.iter().any(|&v| v == (T::ARG_TYPE, mem::size_of::<T>()))); + i.recurse(Self::ARG_TYPE).and_then(|mut si| unsafe { + let etype = ffi::dbus_message_iter_get_element_type(&mut i.0); + + if etype != T::ARG_TYPE as c_int { return None }; + + let mut v = ptr::null_mut(); + let mut i = 0; + ffi::dbus_message_iter_get_fixed_array(&mut si.0, &mut v as *mut _ as *mut c_void, &mut i); + if v == ptr::null_mut() { + assert_eq!(i, 0); + Some(&[][..]) + } else { + Some(::std::slice::from_raw_parts(v, i as usize)) + } + }) + } +} + + +#[derive(Copy, Clone, Debug)] +/// Append a D-Bus dict type (i e, an array of dict entries). +/// +/// See the argument guide and module level documentation for details and alternatives. +pub struct Dict<'a, K: DictKey, V: Arg, I>(I, PhantomData<(&'a Message, *const K, *const V)>); + +impl<'a, K: DictKey, V: Arg, I> Dict<'a, K, V, I> { + fn entry_sig() -> String { format!("{{{}{}}}", K::signature(), V::signature()) } +} + +impl<'a, K: 'a + DictKey, V: 'a + Append + Arg, I: Iterator<Item=(K, V)>> Dict<'a, K, V, I> { + /// Creates a new Dict from an iterator. The iterator is consumed when appended. + pub fn new<J: IntoIterator<IntoIter=I, Item=(K, V)>>(j: J) -> Dict<'a, K, V, I> { Dict(j.into_iter(), PhantomData) } +} + +impl<'a, K: DictKey, V: Arg, I> Arg for Dict<'a, K, V, I> { + const ARG_TYPE: ArgType = ArgType::Array; + fn signature() -> Signature<'static> { + Signature::from(format!("a{}", Self::entry_sig())) } +} + +impl<'a, K: 'a + DictKey + Append, V: 'a + Append + Arg, I: Iterator<Item=(K, V)>> Append for Dict<'a, K, V, I> { + fn append(self, i: &mut IterAppend) { + let z = self.0; + i.append_container(Self::ARG_TYPE, Some(&CString::new(Self::entry_sig()).unwrap()), |s| for (k, v) in z { + s.append_container(ArgType::DictEntry, None, |ss| { + k.append(ss); + v.append(ss); + }) + }); + } +} + + +impl<'a, K: DictKey + Get<'a>, V: Arg + Get<'a>> Get<'a> for Dict<'a, K, V, Iter<'a>> { + fn get(i: &mut Iter<'a>) -> Option<Self> { + i.recurse(Self::ARG_TYPE).map(|si| Dict(si, PhantomData)) + // TODO: Verify full element signature? + } +} + +impl<'a, K: DictKey + Get<'a>, V: Arg + Get<'a>> Iterator for Dict<'a, K, V, Iter<'a>> { + type Item = (K, V); + fn next(&mut self) -> Option<(K, V)> { + let i = self.0.recurse(ArgType::DictEntry).and_then(|mut si| { + let k = si.get(); + if k.is_none() { return None }; + assert!(si.next()); + let v = si.get(); + if v.is_none() { return None }; + Some((k.unwrap(), v.unwrap())) + }); + self.0.next(); + i + } +} + +impl<K: DictKey, V: Arg> Arg for HashMap<K, V> { + const ARG_TYPE: ArgType = ArgType::Array; + fn signature() -> Signature<'static> { + Signature::from(format!("a{{{}{}}}", K::signature(), V::signature())) } +} + +impl<K: DictKey + Append + Eq + Hash, V: Arg + Append> Append for HashMap<K, V> { + fn append(self, i: &mut IterAppend) { + Dict::new(self.into_iter()).append(i); + } +} + +impl<'a, K: DictKey + Get<'a> + Eq + Hash, V: Arg + Get<'a>> Get<'a> for HashMap<K, V> { + fn get(i: &mut Iter<'a>) -> Option<Self> { + // TODO: Full element signature is not verified. + Dict::get(i).map(|d| d.into_iter().collect()) + } +} + +impl<K: DictKey + RefArg + Eq + Hash, V: RefArg + Arg> RefArg for HashMap<K, V> { + fn arg_type(&self) -> ArgType { ArgType::Array } + fn signature(&self) -> Signature<'static> { format!("a{{{}{}}}", <K as Arg>::signature(), <V as Arg>::signature()).into() } + fn append(&self, i: &mut IterAppend) { + let sig = CString::new(format!("{{{}{}}}", <K as Arg>::signature(), <V as Arg>::signature())).unwrap(); + i.append_container(ArgType::Array, Some(&sig), |s| for (k, v) in self { + s.append_container(ArgType::DictEntry, None, |ss| { + k.append(ss); + v.append(ss); + }) + }); + } + #[inline] + fn as_any(&self) -> &any::Any where Self: 'static { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } + fn as_iter<'b>(&'b self) -> Option<Box<Iterator<Item=&'b RefArg> + 'b>> { + Some(Box::new(self.iter().flat_map(|(k, v)| vec![k as &RefArg, v as &RefArg].into_iter()))) + } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { + Box::new(InternalDict { + outer_sig: self.signature(), + data: self.iter().map(|(k, v)| (k.box_clone(), v.box_clone())).collect(), + }) + } +} + +impl<T: Arg> Arg for Vec<T> { + const ARG_TYPE: ArgType = ArgType::Array; + fn signature() -> Signature<'static> { Signature::from(format!("a{}", T::signature())) } +} + +impl<T: Arg + Append> Append for Vec<T> { + fn append(self, i: &mut IterAppend) { + Array::new(self).append(i); + } +} + +impl<'a, T: Arg + Get<'a>> Get<'a> for Vec<T> { + fn get(i: &mut Iter<'a>) -> Option<Self> { + <Array<T, Iter<'a>>>::get(i).map(|a| a.collect()) + } +} + + +#[derive(Copy, Clone, Debug)] +/// Represents a D-Bus Array. Maximum flexibility (wraps an iterator of items to append). +/// +/// See the argument guide and module level documentation for details and alternatives. +pub struct Array<'a, T, I>(I, PhantomData<(*const T, &'a Message)>); + +impl<'a, T: 'a, I: Iterator<Item=T>> Array<'a, T, I> { + /// Creates a new Array from an iterator. The iterator is consumed when appending. + pub fn new<J: IntoIterator<IntoIter=I, Item=T>>(j: J) -> Array<'a, T, I> { Array(j.into_iter(), PhantomData) } +} + +impl<'a, T: Arg, I> Arg for Array<'a, T, I> { + const ARG_TYPE: ArgType = ArgType::Array; + fn signature() -> Signature<'static> { Signature::from(format!("a{}", T::signature())) } +} + +impl<'a, T: 'a + Arg + Append, I: Iterator<Item=T>> Append for Array<'a, T, I> { + fn append(self, i: &mut IterAppend) { + let z = self.0; + i.append_container(ArgType::Array, Some(T::signature().as_cstr()), |s| for arg in z { arg.append(s) }); + } +} + +impl<'a, T: Arg + Get<'a>> Get<'a> for Array<'a, T, Iter<'a>> { + fn get(i: &mut Iter<'a>) -> Option<Array<'a, T, Iter<'a>>> { + i.recurse(Self::ARG_TYPE).map(|si| Array(si, PhantomData)) + // TODO: Verify full element signature? + } +} + +impl<'a, T: Get<'a>> Iterator for Array<'a, T, Iter<'a>> { + type Item = T; + fn next(&mut self) -> Option<T> { + let i = self.0.get(); + self.0.next(); + i + } +} + +// Due to the strong typing here; RefArg is implemented only for T's that are both Arg and RefArg. +// We need Arg for this to work for empty arrays (we can't get signature from first element if there is no elements). +// We need RefArg for non-consuming append. +impl<'a, T: 'a + Arg + fmt::Debug + RefArg, I: fmt::Debug + Clone + Iterator<Item=&'a T>> RefArg for Array<'static, T, I> { + fn arg_type(&self) -> ArgType { ArgType::Array } + fn signature(&self) -> Signature<'static> { Signature::from(format!("a{}", <T as Arg>::signature())) } + fn append(&self, i: &mut IterAppend) { + let z = self.0.clone(); + i.append_container(ArgType::Array, Some(<T as Arg>::signature().as_cstr()), |s| + for arg in z { (arg as &RefArg).append(s) } + ); + } + #[inline] + fn as_any(&self) -> &any::Any where Self: 'static { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } + + fn box_clone(&self) -> Box<RefArg + 'static> { + Box::new(InternalArray { + inner_sig: <T as Arg>::signature(), + data: self.0.clone().map(|x| x.box_clone()).collect(), + }) + } +} + +fn get_fixed_array_refarg<'a, T: FixedArray + Clone + RefArg>(i: &mut Iter<'a>) -> Box<RefArg> { + let s = <&[T]>::get(i).unwrap(); + Box::new(s.to_vec()) +} + +fn get_var_array_refarg<'a, T: 'static + RefArg + Arg, F: FnMut(&mut Iter<'a>) -> Option<T>> + (i: &mut Iter<'a>, mut f: F) -> Box<RefArg> { + let mut v: Vec<T> = vec!(); // dbus_message_iter_get_element_count might be O(n), better not use it + let mut si = i.recurse(ArgType::Array).unwrap(); + while let Some(q) = f(&mut si) { v.push(q); si.next(); } + Box::new(v) +} + + +#[derive(Debug)] +struct InternalDict<K> { + data: Vec<(K, Box<RefArg>)>, + outer_sig: Signature<'static>, +} + +fn get_dict_refarg<'a, K, F: FnMut(&mut Iter<'a>) -> Option<K>>(i: &mut Iter<'a>, mut f: F) -> Box<RefArg> + where K: DictKey + 'static + RefArg + Clone + { + let mut data = vec!(); + let outer_sig = i.signature(); + let mut si = i.recurse(ArgType::Array).unwrap(); + while let Some(mut d) = si.recurse(ArgType::DictEntry) { + let k = f(&mut d).unwrap(); + d.next(); + data.push((k, d.get_refarg().unwrap())); + si.next(); + } + Box::new(InternalDict { data, outer_sig }) +} + +// This only happens from box_clone +impl RefArg for InternalDict<Box<RefArg>> { + fn arg_type(&self) -> ArgType { ArgType::Array } + fn signature(&self) -> Signature<'static> { self.outer_sig.clone() } + fn append(&self, i: &mut IterAppend) { + let inner_sig = &self.outer_sig.as_cstr().to_bytes_with_nul()[1..]; + let inner_sig = CStr::from_bytes_with_nul(inner_sig).unwrap(); + i.append_container(ArgType::Array, Some(inner_sig), |s| for (k, v) in &self.data { + s.append_container(ArgType::DictEntry, None, |ss| { + k.append(ss); + v.append(ss); + }) + }); + } + #[inline] + fn as_any(&self) -> &any::Any where Self: 'static { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } + fn as_iter<'b>(&'b self) -> Option<Box<Iterator<Item=&'b RefArg> + 'b>> { + Some(Box::new(self.data.iter().flat_map(|(k, v)| vec![k as &RefArg, v as &RefArg].into_iter()))) + } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { + Box::new(InternalDict { + data: self.data.iter().map(|(k, v)| (k.box_clone(), v.box_clone())).collect(), + outer_sig: self.outer_sig.clone(), + }) + } +} + + +impl<K: DictKey + RefArg + Clone + 'static> RefArg for InternalDict<K> { + fn arg_type(&self) -> ArgType { ArgType::Array } + fn signature(&self) -> Signature<'static> { self.outer_sig.clone() } + fn append(&self, i: &mut IterAppend) { + let inner_sig = &self.outer_sig.as_cstr().to_bytes_with_nul()[1..]; + let inner_sig = CStr::from_bytes_with_nul(inner_sig).unwrap(); + i.append_container(ArgType::Array, Some(inner_sig), |s| for (k, v) in &self.data { + s.append_container(ArgType::DictEntry, None, |ss| { + k.append(ss); + v.append(ss); + }) + }); + } + #[inline] + fn as_any(&self) -> &any::Any where Self: 'static { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } + fn as_iter<'b>(&'b self) -> Option<Box<Iterator<Item=&'b RefArg> + 'b>> { + Some(Box::new(self.data.iter().flat_map(|(k, v)| vec![k as &RefArg, v as &RefArg].into_iter()))) + } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { + Box::new(InternalDict { + data: self.data.iter().map(|(k, v)| (k.clone(), v.box_clone())).collect(), + outer_sig: self.outer_sig.clone(), + }) + } +} + + +// Fallback for Arrays of Arrays and Arrays of Structs. +// We store the signature manually here and promise that it is correct for all elements +// has that signature. +#[derive(Debug)] +struct InternalArray { + data: Vec<Box<RefArg>>, + inner_sig: Signature<'static>, +} + +fn get_internal_array<'a>(i: &mut Iter<'a>) -> Box<RefArg> { + let mut si = i.recurse(ArgType::Array).unwrap(); + let inner_sig = si.signature(); + let data = si.collect::<Vec<_>>(); + Box::new(InternalArray { data, inner_sig }) +} + +impl RefArg for InternalArray { + fn arg_type(&self) -> ArgType { ArgType::Array } + fn signature(&self) -> Signature<'static> { Signature::from(format!("a{}", self.inner_sig)) } + fn append(&self, i: &mut IterAppend) { + i.append_container(ArgType::Array, Some(self.inner_sig.as_cstr()), |s| + for arg in &self.data { (arg as &RefArg).append(s) } + ); + } + #[inline] + fn as_any(&self) -> &any::Any where Self: 'static { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } + fn as_iter<'a>(&'a self) -> Option<Box<Iterator<Item=&'a RefArg> + 'a>> { + Some(Box::new(self.data.iter().map(|b| b as &RefArg))) + } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { + Box::new(InternalArray { + data: self.data.iter().map(|x| x.box_clone()).collect(), + inner_sig: self.inner_sig.clone(), + }) + } +} + +pub fn get_array_refarg<'a>(i: &mut Iter<'a>) -> Box<RefArg> { + debug_assert!(i.arg_type() == ArgType::Array); + let etype = ArgType::from_i32(unsafe { ffi::dbus_message_iter_get_element_type(&mut i.0) } as i32).unwrap(); + + let x = match etype { + ArgType::Byte => get_fixed_array_refarg::<u8>(i), + ArgType::Int16 => get_fixed_array_refarg::<i16>(i), + ArgType::UInt16 => get_fixed_array_refarg::<u16>(i), + ArgType::Int32 => get_fixed_array_refarg::<i32>(i), + ArgType::UInt32 => get_fixed_array_refarg::<u32>(i), + ArgType::Int64 => get_fixed_array_refarg::<i64>(i), + ArgType::UInt64 => get_fixed_array_refarg::<u64>(i), + ArgType::Double => get_fixed_array_refarg::<f64>(i), + ArgType::String => get_var_array_refarg::<String, _>(i, |si| si.get()), + ArgType::ObjectPath => get_var_array_refarg::<Path<'static>, _>(i, |si| si.get::<Path>().map(|s| s.into_static())), + ArgType::Signature => get_var_array_refarg::<Signature<'static>, _>(i, |si| si.get::<Signature>().map(|s| s.into_static())), + ArgType::Variant => get_var_array_refarg::<Variant<Box<RefArg>>, _>(i, |si| Variant::new_refarg(si)), + ArgType::Boolean => get_var_array_refarg::<bool, _>(i, |si| si.get()), + ArgType::Invalid => panic!("Array with Invalid ArgType"), + ArgType::Array => get_internal_array(i), + ArgType::DictEntry => { + let key = ArgType::from_i32(i.signature().as_bytes()[2] as i32).unwrap(); // The third character, after "a{", is our key. + match key { + ArgType::Byte => get_dict_refarg::<u8, _>(i, |si| si.get()), + ArgType::Int16 => get_dict_refarg::<i16, _>(i, |si| si.get()), + ArgType::UInt16 => get_dict_refarg::<u16, _>(i, |si| si.get()), + ArgType::Int32 => get_dict_refarg::<i32, _>(i, |si| si.get()), + ArgType::UInt32 => get_dict_refarg::<u32, _>(i, |si| si.get()), + ArgType::Int64 => get_dict_refarg::<i64, _>(i, |si| si.get()), + ArgType::UInt64 => get_dict_refarg::<u64, _>(i, |si| si.get()), + ArgType::Double => get_dict_refarg::<f64, _>(i, |si| si.get()), + ArgType::Boolean => get_dict_refarg::<bool, _>(i, |si| si.get()), + // ArgType::UnixFd => get_dict_refarg::<OwnedFd, _>(i, |si| si.get()), + ArgType::String => get_dict_refarg::<String, _>(i, |si| si.get()), + ArgType::ObjectPath => get_dict_refarg::<Path<'static>, _>(i, |si| si.get::<Path>().map(|s| s.into_static())), + ArgType::Signature => get_dict_refarg::<Signature<'static>, _>(i, |si| si.get::<Signature>().map(|s| s.into_static())), + _ => panic!("Array with invalid dictkey ({:?})", key), + } + } + ArgType::UnixFd => get_var_array_refarg::<OwnedFd, _>(i, |si| si.get()), + ArgType::Struct => get_internal_array(i), + }; + + debug_assert_eq!(i.signature(), x.signature()); + x +} + + diff --git a/third_party/rust/dbus/src/arg/basic_impl.rs b/third_party/rust/dbus/src/arg/basic_impl.rs new file mode 100644 index 0000000000..66d2a0ee74 --- /dev/null +++ b/third_party/rust/dbus/src/arg/basic_impl.rs @@ -0,0 +1,308 @@ +use ffi; +use super::*; +use super::check; +use {Signature, Path, OwnedFd}; +use std::{ptr, any, mem}; +use std::ffi::CStr; +use std::os::raw::{c_void, c_char, c_int}; + + +fn arg_append_basic<T>(i: *mut ffi::DBusMessageIter, arg_type: ArgType, v: T) { + let p = &v as *const _ as *const c_void; + unsafe { + check("dbus_message_iter_append_basic", ffi::dbus_message_iter_append_basic(i, arg_type as c_int, p)); + }; +} + +fn arg_get_basic<T>(i: *mut ffi::DBusMessageIter, arg_type: ArgType) -> Option<T> { + unsafe { + let mut c: T = mem::zeroed(); + if ffi::dbus_message_iter_get_arg_type(i) != arg_type as c_int { return None }; + ffi::dbus_message_iter_get_basic(i, &mut c as *mut _ as *mut c_void); + Some(c) + } +} + +fn arg_append_f64(i: *mut ffi::DBusMessageIter, arg_type: ArgType, v: f64) { + let p = &v as *const _ as *const c_void; + unsafe { + check("dbus_message_iter_append_basic", ffi::dbus_message_iter_append_basic(i, arg_type as c_int, p)); + }; +} + +fn arg_get_f64(i: *mut ffi::DBusMessageIter, arg_type: ArgType) -> Option<f64> { + let mut c = 0f64; + unsafe { + if ffi::dbus_message_iter_get_arg_type(i) != arg_type as c_int { return None }; + ffi::dbus_message_iter_get_basic(i, &mut c as *mut _ as *mut c_void); + } + Some(c) +} + +fn arg_append_str(i: *mut ffi::DBusMessageIter, arg_type: ArgType, v: &CStr) { + let p = v.as_ptr(); + let q = &p as *const _ as *const c_void; + unsafe { + check("dbus_message_iter_append_basic", ffi::dbus_message_iter_append_basic(i, arg_type as c_int, q)); + }; +} + +unsafe fn arg_get_str<'a>(i: *mut ffi::DBusMessageIter, arg_type: ArgType) -> Option<&'a CStr> { + if ffi::dbus_message_iter_get_arg_type(i) != arg_type as c_int { return None }; + let mut p = ptr::null_mut(); + ffi::dbus_message_iter_get_basic(i, &mut p as *mut _ as *mut c_void); + Some(CStr::from_ptr(p as *const c_char)) +} + + + + +// Implementation for basic types. + +macro_rules! integer_impl { + ($t: ident, $s: ident, $f: expr, $i: ident, $ii: expr, $u: ident, $uu: expr, $fff: ident, $ff: expr) => { + +impl Arg for $t { + const ARG_TYPE: ArgType = ArgType::$s; + #[inline] + fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked($f) } } +} + +impl Append for $t { + fn append(self, i: &mut IterAppend) { arg_append_basic(&mut i.0, ArgType::$s, self) } +} + +impl<'a> Get<'a> for $t { + fn get(i: &mut Iter) -> Option<Self> { arg_get_basic(&mut i.0, ArgType::$s) } +} + +impl RefArg for $t { + #[inline] + fn arg_type(&self) -> ArgType { ArgType::$s } + #[inline] + fn signature(&self) -> Signature<'static> { unsafe { Signature::from_slice_unchecked($f) } } + #[inline] + fn append(&self, i: &mut IterAppend) { arg_append_basic(&mut i.0, ArgType::$s, *self) } + #[inline] + fn as_any(&self) -> &any::Any { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any { self } + #[inline] + fn as_i64(&self) -> Option<i64> { let $i = *self; $ii } + #[inline] + fn as_u64(&self) -> Option<u64> { let $u = *self; $uu } + #[inline] + fn as_f64(&self) -> Option<f64> { let $fff = *self; $ff } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { Box::new(self.clone()) } +} + +impl DictKey for $t {} +unsafe impl FixedArray for $t {} + +}} // End of macro_rules + +integer_impl!(u8, Byte, b"y\0", i, Some(i as i64), u, Some(u as u64), f, Some(f as f64)); +integer_impl!(i16, Int16, b"n\0", i, Some(i as i64), _u, None, f, Some(f as f64)); +integer_impl!(u16, UInt16, b"q\0", i, Some(i as i64), u, Some(u as u64), f, Some(f as f64)); +integer_impl!(i32, Int32, b"i\0", i, Some(i as i64), _u, None, f, Some(f as f64)); +integer_impl!(u32, UInt32, b"u\0", i, Some(i as i64), u, Some(u as u64), f, Some(f as f64)); +integer_impl!(i64, Int64, b"x\0", i, Some(i), _u, None, _f, None); +integer_impl!(u64, UInt64, b"t\0", _i, None, u, Some(u as u64), _f, None); + + +macro_rules! refarg_impl { + ($t: ty, $i: ident, $ii: expr, $ss: expr, $uu: expr, $ff: expr) => { + +impl RefArg for $t { + #[inline] + fn arg_type(&self) -> ArgType { <$t as Arg>::ARG_TYPE } + #[inline] + fn signature(&self) -> Signature<'static> { <$t as Arg>::signature() } + #[inline] + fn append(&self, i: &mut IterAppend) { <$t as Append>::append(self.clone(), i) } + #[inline] + fn as_any(&self) -> &any::Any { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any { self } + #[inline] + fn as_i64(&self) -> Option<i64> { let $i = self; $ii } + #[inline] + fn as_u64(&self) -> Option<u64> { let $i = self; $uu } + #[inline] + fn as_f64(&self) -> Option<f64> { let $i = self; $ff } + #[inline] + fn as_str(&self) -> Option<&str> { let $i = self; $ss } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { Box::new(self.clone()) } +} + + } +} + + +impl Arg for bool { + const ARG_TYPE: ArgType = ArgType::Boolean; + fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"b\0") } } +} +impl Append for bool { + fn append(self, i: &mut IterAppend) { arg_append_basic(&mut i.0, ArgType::Boolean, if self {1} else {0}) } +} +impl DictKey for bool {} +impl<'a> Get<'a> for bool { + fn get(i: &mut Iter) -> Option<Self> { arg_get_basic::<u32>(&mut i.0, ArgType::Boolean).map(|q| q != 0) } +} + +refarg_impl!(bool, _i, Some(if *_i { 1 } else { 0 }), None, Some(if *_i { 1 as u64 } else { 0 as u64 }), Some(if *_i { 1 as f64 } else { 0 as f64 })); + +impl Arg for f64 { + const ARG_TYPE: ArgType = ArgType::Double; + fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"d\0") } } +} +impl Append for f64 { + fn append(self, i: &mut IterAppend) { arg_append_f64(&mut i.0, ArgType::Double, self) } +} +impl DictKey for f64 {} +impl<'a> Get<'a> for f64 { + fn get(i: &mut Iter) -> Option<Self> { arg_get_f64(&mut i.0, ArgType::Double) } +} +unsafe impl FixedArray for f64 {} + +refarg_impl!(f64, _i, None, None, None, Some(*_i)); + +/// Represents a D-Bus string. +impl<'a> Arg for &'a str { + const ARG_TYPE: ArgType = ArgType::String; + fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"s\0") } } +} + +impl<'a> Append for &'a str { + fn append(self, i: &mut IterAppend) { + use std::borrow::Cow; + let b: &[u8] = self.as_bytes(); + let v: Cow<[u8]> = if b.len() > 0 && b[b.len()-1] == 0 { Cow::Borrowed(b) } + else { + let mut bb: Vec<u8> = b.into(); + bb.push(0); + Cow::Owned(bb) + }; + let z = unsafe { CStr::from_ptr(v.as_ptr() as *const c_char) }; + arg_append_str(&mut i.0, ArgType::String, &z) + } +} +impl<'a> DictKey for &'a str {} +impl<'a> Get<'a> for &'a str { + fn get(i: &mut Iter<'a>) -> Option<&'a str> { unsafe { arg_get_str(&mut i.0, ArgType::String) } + .and_then(|s| s.to_str().ok()) } +} + +impl<'a> Arg for String { + const ARG_TYPE: ArgType = ArgType::String; + fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"s\0") } } +} +impl<'a> Append for String { + fn append(mut self, i: &mut IterAppend) { + self.push_str("\0"); + let s: &str = &self; + s.append(i) + } +} +impl<'a> DictKey for String {} +impl<'a> Get<'a> for String { + fn get(i: &mut Iter<'a>) -> Option<String> { <&str>::get(i).map(|s| String::from(s)) } +} + +refarg_impl!(String, _i, None, Some(&_i), None, None); + +/// Represents a D-Bus string. +impl<'a> Arg for &'a CStr { + const ARG_TYPE: ArgType = ArgType::String; + fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"s\0") } } +} + +/* +/// Note: Will give D-Bus errors in case the CStr is not valid UTF-8. +impl<'a> Append for &'a CStr { + fn append(self, i: &mut IterAppend) { + arg_append_str(&mut i.0, Self::arg_type(), &self) + } +} +*/ + +impl<'a> DictKey for &'a CStr {} +impl<'a> Get<'a> for &'a CStr { + fn get(i: &mut Iter<'a>) -> Option<&'a CStr> { unsafe { arg_get_str(&mut i.0, Self::ARG_TYPE) }} +} + +impl Arg for OwnedFd { + const ARG_TYPE: ArgType = ArgType::UnixFd; + fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"h\0") } } +} +impl Append for OwnedFd { + fn append(self, i: &mut IterAppend) { + use std::os::unix::io::AsRawFd; + arg_append_basic(&mut i.0, ArgType::UnixFd, self.as_raw_fd()) + } +} +impl DictKey for OwnedFd {} +impl<'a> Get<'a> for OwnedFd { + fn get(i: &mut Iter) -> Option<Self> { + arg_get_basic(&mut i.0, ArgType::UnixFd).map(|q| OwnedFd::new(q)) + } +} + +refarg_impl!(OwnedFd, _i, { use std::os::unix::io::AsRawFd; Some(_i.as_raw_fd() as i64) }, None, None, None); + +macro_rules! string_impl { + ($t: ident, $s: ident, $f: expr) => { + +impl<'a> Arg for $t<'a> { + const ARG_TYPE: ArgType = ArgType::$s; + fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked($f) } } +} + +impl RefArg for $t<'static> { + fn arg_type(&self) -> ArgType { ArgType::$s } + fn signature(&self) -> Signature<'static> { unsafe { Signature::from_slice_unchecked($f) } } + fn append(&self, i: &mut IterAppend) { arg_append_str(&mut i.0, ArgType::$s, self.as_cstr()) } + #[inline] + fn as_any(&self) -> &any::Any { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any { self } + #[inline] + fn as_str(&self) -> Option<&str> { Some(self) } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { Box::new(self.clone().into_static()) } +} + +impl<'a> DictKey for $t<'a> {} + +impl<'a> Append for $t<'a> { + fn append(self, i: &mut IterAppend) { + arg_append_str(&mut i.0, ArgType::$s, self.as_cstr()) + } +} + +/* + +Unfortunately, this does not work because it conflicts with getting a $t<'static>. + +impl<'a> Get<'a> for $t<'a> { + fn get(i: &mut Iter<'a>) -> Option<$t<'a>> { unsafe { arg_get_str(&mut i.0, ArgType::$s) } + .map(|s| unsafe { $t::from_slice_unchecked(s.to_bytes_with_nul()) } ) } +} +*/ + +impl<'a> Get<'a> for $t<'static> { + fn get(i: &mut Iter<'a>) -> Option<$t<'static>> { unsafe { + arg_get_str(&mut i.0, ArgType::$s).map(|s| $t::from_slice_unchecked(s.to_bytes_with_nul()).into_static()) + }} +} + + + } +} + +string_impl!(Path, ObjectPath, b"o\0"); +string_impl!(Signature, Signature, b"g\0"); + diff --git a/third_party/rust/dbus/src/arg/mod.rs b/third_party/rust/dbus/src/arg/mod.rs new file mode 100644 index 0000000000..737960b4b8 --- /dev/null +++ b/third_party/rust/dbus/src/arg/mod.rs @@ -0,0 +1,440 @@ +//! Types and traits for easily getting a message's arguments, or appening a message with arguments. +//! +//! Also see the arguments guide (in the examples directory). +//! +//! A message has `read1`, `read2` etc, and `append1`, `append2` etc, which is your +//! starting point into this module's types. +//! +//! **Append a**: +//! +//! `bool, u8, u16, u32, u64, i16, i32, i64, f64` - the corresponding D-Bus basic type +//! +//! `&str` - a D-Bus string. D-Bus strings do not allow null characters, so +//! if the string contains null characters, it will be cropped +//! to only include the data before the null character. (Tip: This allows for skipping an +//! allocation by writing a string literal which ends with a null character.) +//! +//! `&[T] where T: Append` - a D-Bus array. Note: can use an efficient fast-path in case of +//! T being an FixedArray type. +//! +//! `Array<T, I> where T: Append, I: Iterator<Item=T>` - a D-Bus array, maximum flexibility. +//! +//! `Variant<T> where T: Append` - a D-Bus variant. +//! +//! `(T1, T2) where T1: Append, T2: Append` - tuples are D-Bus structs. Implemented up to 12. +//! +//! `Dict<K, V, I> where K: Append + DictKey, V: Append, I: Iterator<Item=(&K, &V)>` - A D-Bus dict (array of dict entries). +//! +//! `Path` - a D-Bus object path. +//! +//! `Signature` - a D-Bus signature. +//! +//! `OwnedFd` - shares the file descriptor with the remote side. +//! +//! **Get / read a**: +//! +//! `bool, u8, u16, u32, u64, i16, i32, i64, f64` - the corresponding D-Bus basic type +//! +//! `&str`, `&CStr` - a D-Bus string. D-Bus strings are always UTF-8 and do not contain null characters. +//! +//! `&[T] where T: FixedArray` - a D-Bus array of integers or f64. +//! +//! `Array<T, Iter> where T: Get` - a D-Bus array, maximum flexibility. Implements Iterator so you can easily +//! collect it into, e g, a `Vec`. +//! +//! `Variant<T> where T: Get` - a D-Bus variant. Use this type of Variant if you know the inner type. +//! +//! `Variant<Iter>` - a D-Bus variant. This type of Variant allows you to examine the inner type. +//! +//! `(T1, T2) where T1: Get, T2: Get` - tuples are D-Bus structs. Implemented up to 12. +//! +//! `Dict<K, V, Iter> where K: Get + DictKey, V: Get` - A D-Bus dict (array of dict entries). Implements Iterator so you can easily +//! collect it into, e g, a `HashMap`. +//! +//! `Path` - a D-Bus object path. +//! +//! `Signature` - a D-Bus signature. +//! +//! `OwnedFd` - a file descriptor sent from the remote side. +//! + +mod msgarg; +mod basic_impl; +mod variantstruct_impl; +mod array_impl; + +pub use self::msgarg::{Arg, FixedArray, Get, DictKey, Append, RefArg, AppendAll, ReadAll, cast, cast_mut}; +pub use self::array_impl::{Array, Dict}; +pub use self::variantstruct_impl::Variant; + +use std::{fmt, mem, ptr, error}; +use {ffi, Message, Signature, Path, OwnedFd}; +use std::ffi::{CStr, CString}; +use std::os::raw::{c_void, c_int}; + + +fn check(f: &str, i: u32) { if i == 0 { panic!("D-Bus error: '{}' failed", f) }} + +fn ffi_iter() -> ffi::DBusMessageIter { unsafe { mem::zeroed() }} + +#[derive(Clone, Copy)] +/// Helper struct for appending one or more arguments to a Message. +pub struct IterAppend<'a>(ffi::DBusMessageIter, &'a Message); + +impl<'a> IterAppend<'a> { + /// Creates a new IterAppend struct. + pub fn new(m: &'a mut Message) -> IterAppend<'a> { + let mut i = ffi_iter(); + unsafe { ffi::dbus_message_iter_init_append(m.ptr(), &mut i) }; + IterAppend(i, m) + } + + /// Appends the argument. + pub fn append<T: Append>(&mut self, a: T) { a.append(self) } + + fn append_container<F: FnOnce(&mut IterAppend<'a>)>(&mut self, arg_type: ArgType, sig: Option<&CStr>, f: F) { + let mut s = IterAppend(ffi_iter(), self.1); + let p = sig.map(|s| s.as_ptr()).unwrap_or(ptr::null()); + check("dbus_message_iter_open_container", + unsafe { ffi::dbus_message_iter_open_container(&mut self.0, arg_type as c_int, p, &mut s.0) }); + f(&mut s); + check("dbus_message_iter_close_container", + unsafe { ffi::dbus_message_iter_close_container(&mut self.0, &mut s.0) }); + } + + /// Low-level function to append a variant. + /// + /// Use in case the `Variant` struct is not flexible enough - + /// the easier way is to just call e g "append1" on a message and supply a `Variant` parameter. + /// + /// In order not to get D-Bus errors: during the call to "f" you need to call "append" on + /// the supplied `IterAppend` exactly once, + /// and with a value which has the same signature as inner_sig. + pub fn append_variant<F: FnOnce(&mut IterAppend<'a>)>(&mut self, inner_sig: &Signature, f: F) { + self.append_container(ArgType::Variant, Some(inner_sig.as_cstr()), f) + } + + /// Low-level function to append an array. + /// + /// Use in case the `Array` struct is not flexible enough - + /// the easier way is to just call e g "append1" on a message and supply an `Array` parameter. + /// + /// In order not to get D-Bus errors: during the call to "f", you should only call "append" on + /// the supplied `IterAppend` with values which has the same signature as inner_sig. + pub fn append_array<F: FnOnce(&mut IterAppend<'a>)>(&mut self, inner_sig: &Signature, f: F) { + self.append_container(ArgType::Array, Some(inner_sig.as_cstr()), f) + } + + /// Low-level function to append a struct. + /// + /// Use in case tuples are not flexible enough - + /// the easier way is to just call e g "append1" on a message and supply a tuple parameter. + pub fn append_struct<F: FnOnce(&mut IterAppend<'a>)>(&mut self, f: F) { + self.append_container(ArgType::Struct, None, f) + } + + /// Low-level function to append a dict entry. + /// + /// Use in case the `Dict` struct is not flexible enough - + /// the easier way is to just call e g "append1" on a message and supply a `Dict` parameter. + /// + /// In order not to get D-Bus errors: during the call to "f", you should call "append" once + /// for the key, then once for the value. You should only call this function for a subiterator + /// you got from calling "append_dict", and signatures need to match what you specified in "append_dict". + pub fn append_dict_entry<F: FnOnce(&mut IterAppend<'a>)>(&mut self, f: F) { + self.append_container(ArgType::DictEntry, None, f) + } + + /// Low-level function to append a dict. + /// + /// Use in case the `Dict` struct is not flexible enough - + /// the easier way is to just call e g "append1" on a message and supply a `Dict` parameter. + /// + /// In order not to get D-Bus errors: during the call to "f", you should only call "append_dict_entry" + /// for the subiterator - do this as many times as the number of dict entries. + pub fn append_dict<F: FnOnce(&mut IterAppend<'a>)>(&mut self, key_sig: &Signature, value_sig: &Signature, f: F) { + let sig = format!("{{{}{}}}", key_sig, value_sig); + self.append_container(Array::<bool,()>::ARG_TYPE, Some(&CString::new(sig).unwrap()), f); + } +} + + + +#[derive(Clone, Copy)] +/// Helper struct for retrieve one or more arguments from a Message. +pub struct Iter<'a>(ffi::DBusMessageIter, &'a Message, u32); + +impl<'a> Iter<'a> { + /// Creates a new struct for iterating over the arguments of a message, starting with the first argument. + pub fn new(m: &'a Message) -> Iter<'a> { + let mut i = ffi_iter(); + unsafe { ffi::dbus_message_iter_init(m.ptr(), &mut i) }; + Iter(i, m, 0) + } + + /// Returns the current argument, if T is the argument type. Otherwise returns None. + pub fn get<T: Get<'a>>(&mut self) -> Option<T> { + T::get(self) + } + + /// Returns the current argument as a trait object (experimental). + /// + /// Note: For the more complex arguments (arrays / dicts / structs, and especially + /// combinations thereof), their internal representations are still a bit in flux. + /// Instead, use as_iter() to read the values of those. + /// + /// The rest are unlikely to change - Variants are `Variant<Box<RefArg>>`, strings are `String`, + /// paths are `Path<'static>`, signatures are `Signature<'static>`, Int32 are `i32s` and so on. + pub fn get_refarg(&mut self) -> Option<Box<RefArg + 'static>> { + Some(match self.arg_type() { + ArgType::Array => array_impl::get_array_refarg(self), + ArgType::Variant => Box::new(Variant::new_refarg(self).unwrap()), + ArgType::Boolean => Box::new(self.get::<bool>().unwrap()), + ArgType::Invalid => return None, + ArgType::String => Box::new(self.get::<String>().unwrap()), + ArgType::DictEntry => unimplemented!(), + ArgType::Byte => Box::new(self.get::<u8>().unwrap()), + ArgType::Int16 => Box::new(self.get::<i16>().unwrap()), + ArgType::UInt16 => Box::new(self.get::<u16>().unwrap()), + ArgType::Int32 => Box::new(self.get::<i32>().unwrap()), + ArgType::UInt32 => Box::new(self.get::<u32>().unwrap()), + ArgType::Int64 => Box::new(self.get::<i64>().unwrap()), + ArgType::UInt64 => Box::new(self.get::<u64>().unwrap()), + ArgType::Double => Box::new(self.get::<f64>().unwrap()), + ArgType::UnixFd => Box::new(self.get::<OwnedFd>().unwrap()), + ArgType::Struct => Box::new(self.recurse(ArgType::Struct).unwrap().collect::<Vec<_>>()), + ArgType::ObjectPath => Box::new(self.get::<Path>().unwrap().into_static()), + ArgType::Signature => Box::new(self.get::<Signature>().unwrap().into_static()), + }) + } + + /// Returns the type signature for the current argument. + pub fn signature(&mut self) -> Signature<'static> { + unsafe { + let c = ffi::dbus_message_iter_get_signature(&mut self.0); + assert!(c != ptr::null_mut()); + let cc = CStr::from_ptr(c); + let r = Signature::new(cc.to_bytes()); + ffi::dbus_free(c as *mut c_void); + r.unwrap() + } + } + + /// The raw arg_type for the current item. + /// + /// Unlike Arg::arg_type, this requires access to self and is not a static method. + /// You can match this against Arg::arg_type for different types to understand what type the current item is. + /// In case you're past the last argument, this function will return 0. + pub fn arg_type(&mut self) -> ArgType { + let s = unsafe { ffi::dbus_message_iter_get_arg_type(&mut self.0) }; + ArgType::from_i32(s as i32).unwrap() + } + + /// Returns false if there are no more items. + pub fn next(&mut self) -> bool { + self.2 += 1; + unsafe { ffi::dbus_message_iter_next(&mut self.0) != 0 } + } + + /// Wrapper around `get` and `next`. Calls `get`, and then `next` if `get` succeeded. + /// + /// Also returns a `Result` rather than an `Option` to give an error if successful. + /// + /// # Example + /// ```ignore + /// struct ServiceBrowserItemNew { + /// interface: i32, + /// protocol: i32, + /// name: String, + /// item_type: String, + /// domain: String, + /// flags: u32, + /// } + /// + /// fn service_browser_item_new_msg(m: &Message) -> Result<ServiceBrowserItemNew, TypeMismatchError> { + /// let mut iter = m.iter_init(); + /// Ok(ServiceBrowserItemNew { + /// interface: iter.read()?, + /// protocol: iter.read()?, + /// name: iter.read()?, + /// item_type: iter.read()?, + /// domain: iter.read()?, + /// flags: iter.read()?, + /// }) + /// } + /// ``` + pub fn read<T: Arg + Get<'a>>(&mut self) -> Result<T, TypeMismatchError> { + let r = try!(self.get().ok_or_else(|| + TypeMismatchError { expected: T::ARG_TYPE, found: self.arg_type(), position: self.2 })); + self.next(); + Ok(r) + } + + /// If the current argument is a container of the specified arg_type, then a new + /// Iter is returned which is for iterating over the contents inside the container. + /// + /// Primarily for internal use (the "get" function is more ergonomic), but could be + /// useful for recursing into containers with unknown types. + pub fn recurse(&mut self, arg_type: ArgType) -> Option<Iter<'a>> { + let containers = [ArgType::Array, ArgType::DictEntry, ArgType::Struct, ArgType::Variant]; + if !containers.iter().any(|&t| t == arg_type) { return None; } + + let mut subiter = ffi_iter(); + unsafe { + if ffi::dbus_message_iter_get_arg_type(&mut self.0) != arg_type as c_int { return None }; + ffi::dbus_message_iter_recurse(&mut self.0, &mut subiter) + } + Some(Iter(subiter, self.1, 0)) + } +} + +impl<'a> fmt::Debug for Iter<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut z = self.clone(); + let mut t = f.debug_tuple("Iter"); + loop { + t.field(&z.arg_type()); + if !z.next() { break } + } + t.finish() + } +} + +impl<'a> Iterator for Iter<'a> { + type Item = Box<RefArg + 'static>; + fn next(&mut self) -> Option<Self::Item> { + let r = self.get_refarg(); + if r.is_some() { self.next(); } + r + } +} + +/// Type of Argument +/// +/// use this to figure out, e g, which type of argument is at the current position of Iter. +#[repr(u8)] +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] +pub enum ArgType { + /// Dicts are Arrays of dict entries, so Dict types will have Array as ArgType. + Array = ffi::DBUS_TYPE_ARRAY as u8, + /// Variant + Variant = ffi::DBUS_TYPE_VARIANT as u8, + /// bool + Boolean = ffi::DBUS_TYPE_BOOLEAN as u8, + /// Invalid arg type - this is also the ArgType returned when there are no more arguments available. + Invalid = ffi::DBUS_TYPE_INVALID as u8, + /// String + String = ffi::DBUS_TYPE_STRING as u8, + /// Dict entry; you'll usually not encounter this one as dicts are arrays of dict entries. + DictEntry = ffi::DBUS_TYPE_DICT_ENTRY as u8, + /// u8 + Byte = ffi::DBUS_TYPE_BYTE as u8, + /// i16 + Int16 = ffi::DBUS_TYPE_INT16 as u8, + /// u16 + UInt16 = ffi::DBUS_TYPE_UINT16 as u8, + /// i32 + Int32 = ffi::DBUS_TYPE_INT32 as u8, + /// u32 + UInt32 = ffi::DBUS_TYPE_UINT32 as u8, + /// i64 + Int64 = ffi::DBUS_TYPE_INT64 as u8, + /// u64 + UInt64 = ffi::DBUS_TYPE_UINT64 as u8, + /// f64 + Double = ffi::DBUS_TYPE_DOUBLE as u8, + /// OwnedFd + UnixFd = ffi::DBUS_TYPE_UNIX_FD as u8, + /// Use tuples or Vec<Box<RefArg>> to read/write structs. + Struct = ffi::DBUS_TYPE_STRUCT as u8, + /// Path + ObjectPath = ffi::DBUS_TYPE_OBJECT_PATH as u8, + /// Signature + Signature = ffi::DBUS_TYPE_SIGNATURE as u8, +} + +const ALL_ARG_TYPES: [(ArgType, &'static str); 18] = + [(ArgType::Variant, "Variant"), + (ArgType::Array, "Array/Dict"), + (ArgType::Struct, "Struct"), + (ArgType::String, "String"), + (ArgType::DictEntry, "Dict entry"), + (ArgType::ObjectPath, "Path"), + (ArgType::Signature, "Signature"), + (ArgType::UnixFd, "OwnedFd"), + (ArgType::Boolean, "bool"), + (ArgType::Byte, "u8"), + (ArgType::Int16, "i16"), + (ArgType::Int32, "i32"), + (ArgType::Int64, "i64"), + (ArgType::UInt16, "u16"), + (ArgType::UInt32, "u32"), + (ArgType::UInt64, "u64"), + (ArgType::Double, "f64"), + (ArgType::Invalid, "nothing")]; + +impl ArgType { + /// A str corresponding to the name of a Rust type. + pub fn as_str(self) -> &'static str { + ALL_ARG_TYPES.iter().skip_while(|a| a.0 != self).next().unwrap().1 + } + + /// Converts an i32 to an ArgType (or an error). + pub fn from_i32(i: i32) -> Result<ArgType, String> { + for &(a, _) in &ALL_ARG_TYPES { + if a as i32 == i { return Ok(a); } + } + Err(format!("Invalid ArgType {} ({})", i, i as u8 as char)) + } +} + + +/// Error struct to indicate a D-Bus argument type mismatch. +/// +/// Might be returned from `iter::read()`. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct TypeMismatchError { + expected: ArgType, + found: ArgType, + position: u32, +} + +impl TypeMismatchError { + /// The ArgType we were trying to read, but failed + pub fn expected_arg_type(&self) -> ArgType { self.expected } + + /// The ArgType we should have been trying to read, if we wanted the read to succeed + pub fn found_arg_type(&self) -> ArgType { self.found } + + /// At what argument was the error found? + /// + /// Returns 0 for first argument, 1 for second argument, etc. + pub fn pos(&self) -> u32 { self.position } +} + +impl error::Error for TypeMismatchError { + fn description(&self) -> &str { "D-Bus argument type mismatch" } + fn cause(&self) -> Option<&error::Error> { None } +} + +impl fmt::Display for TypeMismatchError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{} at position {}: expected {}, found {}", + (self as &error::Error).description(), + self.position, self.expected.as_str(), + if self.expected == self.found { "same but still different somehow" } else { self.found.as_str() } + ) + } +} + + +#[allow(dead_code)] +fn test_compile() { + let mut q = IterAppend::new(unsafe { mem::transmute(0usize) }); + + q.append(5u8); + q.append(Array::new(&[5u8, 6, 7])); + q.append((8u8, &[9u8, 6, 7][..])); + q.append(Variant((6u8, 7u8))); +} + diff --git a/third_party/rust/dbus/src/arg/msgarg.rs b/third_party/rust/dbus/src/arg/msgarg.rs new file mode 100644 index 0000000000..3309c35e65 --- /dev/null +++ b/third_party/rust/dbus/src/arg/msgarg.rs @@ -0,0 +1,426 @@ +#![allow(dead_code)] + +use {Signature, Message, arg::TypeMismatchError}; +use std::{fmt, any}; +use std::sync::Arc; +use std::rc::Rc; + +use super::{Iter, IterAppend, ArgType}; + +/// Types that can represent a D-Bus message argument implement this trait. +/// +/// Types should also implement either Append or Get to be useful. +pub trait Arg { + /// The corresponding D-Bus argument type code. + const ARG_TYPE: ArgType; + /// The corresponding D-Bus argument type code; just returns ARG_TYPE. + /// + /// For backwards compatibility. + #[deprecated(note = "Use associated constant ARG_TYPE instead")] + fn arg_type() -> ArgType { return Self::ARG_TYPE; } + /// The corresponding D-Bus type signature for this type. + fn signature() -> Signature<'static>; +} + +/// Types that can be appended to a message as arguments implement this trait. +pub trait Append: Sized { + /// Performs the append operation. + fn append(self, &mut IterAppend); +} + +/// Helper trait to append many arguments to a message. +pub trait AppendAll: Sized { + /// Performs the append operation. + fn append(self, &mut IterAppend); +} + +/// Types that can be retrieved from a message as arguments implement this trait. +pub trait Get<'a>: Sized { + /// Performs the get operation. + fn get(i: &mut Iter<'a>) -> Option<Self>; +} + +/// Helper trait to read all arguments from a message. +pub trait ReadAll: Sized { + /// Performs the read operation. + fn read(i: &mut Iter) -> Result<Self, TypeMismatchError>; +} + + +/// Object safe version of Arg + Append + Get. +pub trait RefArg: fmt::Debug { + /// The corresponding D-Bus argument type code. + fn arg_type(&self) -> ArgType; + /// The corresponding D-Bus type signature for this type. + fn signature(&self) -> Signature<'static>; + /// Performs the append operation. + fn append(&self, &mut IterAppend); + /// Transforms this argument to Any (which can be downcasted to read the current value). + /// + /// Note: The internal representation of complex types (Array, Dict, Struct) is unstable + /// and as_any should not be relied upon for these types. Use as_iter instead. + fn as_any(&self) -> &any::Any where Self: 'static; + /// Transforms this argument to Any (which can be downcasted to read the current value). + /// + /// Note: The internal representation of complex types (Array, Dict, Struct) is unstable + /// and as_any should not be relied upon for these types. Use as_iter instead. + /// + /// # Panic + /// Will panic if the interior cannot be made mutable, e g, if encapsulated + /// inside a Rc with a reference count > 1. + fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static; + /// Try to read the argument as an i64. + /// + /// Works for: Boolean, Byte, Int16, UInt16, Int32, UInt32, Int64, UnixFd. + #[inline] + fn as_i64(&self) -> Option<i64> { None } + /// Try to read the argument as an u64. + /// + /// Works for: Boolean, Byte, Int16, UInt16, Int32, UInt32, UInt64. + #[inline] + fn as_u64(&self) -> Option<u64> { None } + /// Try to read the argument as an f64. + /// + /// Works for: Boolean, Byte, Int16, UInt16, Int32, UInt32, Double. + #[inline] + fn as_f64(&self) -> Option<f64> { None } + /// Try to read the argument as a str. + /// + /// Works for: String, ObjectPath, Signature. + #[inline] + fn as_str(&self) -> Option<&str> { None } + /// Try to read the argument as an iterator. + /// + /// Works for: Array/Dict, Struct, Variant. + #[inline] + fn as_iter<'a>(&'a self) -> Option<Box<Iterator<Item=&'a RefArg> + 'a>> { None } + /// Deep clone of the RefArg, causing the result to be 'static. + /// + /// Usable as an escape hatch in case of lifetime problems with RefArg. + /// + /// In case of complex types (Array, Dict, Struct), the clone is not guaranteed + /// to have the same internal representation as the original. + fn box_clone(&self) -> Box<RefArg + 'static> { unimplemented!() /* Needed for backwards comp */ } +} + +impl<'a> Get<'a> for Box<RefArg> { + fn get(i: &mut Iter<'a>) -> Option<Self> { i.get_refarg() } +} + +/// Cast a RefArg as a specific type (shortcut for any + downcast) +#[inline] +pub fn cast<'a, T: 'static>(a: &'a (RefArg + 'static)) -> Option<&'a T> { a.as_any().downcast_ref() } + +/// Cast a RefArg as a specific type (shortcut for any_mut + downcast_mut) +/// +/// # Panic +/// Will panic if the interior cannot be made mutable, e g, if encapsulated +/// inside a Rc with a reference count > 1. +#[inline] +pub fn cast_mut<'a, T: 'static>(a: &'a mut (RefArg + 'static)) -> Option<&'a mut T> { a.as_any_mut().downcast_mut() } + +/// If a type implements this trait, it means the size and alignment is the same +/// as in D-Bus. This means that you can quickly append and get slices of this type. +/// +/// Note: Booleans do not implement this trait because D-Bus booleans are 4 bytes and Rust booleans are 1 byte. +pub unsafe trait FixedArray: Arg + 'static + Clone + Copy {} + +/// Types that can be used as keys in a dict type implement this trait. +pub trait DictKey: Arg {} + + + +/// Simple lift over reference to value - this makes some iterators more ergonomic to use +impl<'a, T: Arg> Arg for &'a T { + const ARG_TYPE: ArgType = T::ARG_TYPE; + fn signature() -> Signature<'static> { T::signature() } +} +impl<'a, T: Append + Clone> Append for &'a T { + fn append(self, i: &mut IterAppend) { self.clone().append(i) } +} +impl<'a, T: DictKey> DictKey for &'a T {} + +impl<'a, T: RefArg + ?Sized> RefArg for &'a T { + #[inline] + fn arg_type(&self) -> ArgType { (&**self).arg_type() } + #[inline] + fn signature(&self) -> Signature<'static> { (&**self).signature() } + #[inline] + fn append(&self, i: &mut IterAppend) { (&**self).append(i) } + #[inline] + fn as_any(&self) -> &any::Any where T: 'static { (&**self).as_any() } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any where T: 'static { unreachable!() } + #[inline] + fn as_i64(&self) -> Option<i64> { (&**self).as_i64() } + #[inline] + fn as_u64(&self) -> Option<u64> { (&**self).as_u64() } + #[inline] + fn as_f64(&self) -> Option<f64> { (&**self).as_f64() } + #[inline] + fn as_str(&self) -> Option<&str> { (&**self).as_str() } + #[inline] + fn as_iter<'b>(&'b self) -> Option<Box<Iterator<Item=&'b RefArg> + 'b>> { (&**self).as_iter() } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { (&**self).box_clone() } +} + + + +macro_rules! deref_impl { + ($t: ident, $ss: ident, $make_mut: expr) => { + +impl<T: RefArg + ?Sized> RefArg for $t<T> { + #[inline] + fn arg_type(&self) -> ArgType { (&**self).arg_type() } + #[inline] + fn signature(&self) -> Signature<'static> { (&**self).signature() } + #[inline] + fn append(&self, i: &mut IterAppend) { (&**self).append(i) } + #[inline] + fn as_any(&self) -> &any::Any where T: 'static { (&**self).as_any() } + #[inline] + fn as_any_mut<'a>(&'a mut $ss) -> &'a mut any::Any where T: 'static { $make_mut.as_any_mut() } + #[inline] + fn as_i64(&self) -> Option<i64> { (&**self).as_i64() } + #[inline] + fn as_u64(&self) -> Option<u64> { (&**self).as_u64() } + #[inline] + fn as_f64(&self) -> Option<f64> { (&**self).as_f64() } + #[inline] + fn as_str(&self) -> Option<&str> { (&**self).as_str() } + #[inline] + fn as_iter<'a>(&'a self) -> Option<Box<Iterator<Item=&'a RefArg> + 'a>> { (&**self).as_iter() } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { (&**self).box_clone() } +} +impl<T: DictKey> DictKey for $t<T> {} + +impl<T: Arg> Arg for $t<T> { + const ARG_TYPE: ArgType = T::ARG_TYPE; + fn signature() -> Signature<'static> { T::signature() } +} +impl<'a, T: Get<'a>> Get<'a> for $t<T> { + fn get(i: &mut Iter<'a>) -> Option<Self> { T::get(i).map(|v| $t::new(v)) } +} + + } +} + +impl<T: Append> Append for Box<T> { + fn append(self, i: &mut IterAppend) { let q: T = *self; q.append(i) } +} + +deref_impl!(Box, self, &mut **self ); +deref_impl!(Rc, self, Rc::get_mut(self).unwrap()); +deref_impl!(Arc, self, Arc::get_mut(self).unwrap()); + +/// Internal trait to help generics. Implemented for (), (A1), (A1, A2) and so on (where A1: Arg, A2: Arg etc). +/// +/// You would probably not use this trait directly, instead use generic functions which +/// take ArgBuilder as an argument. It helps reading and appending multiple arguments +/// to/from a message in one go. +pub trait ArgBuilder: Sized { + /// A tuple of &static str. Used for introspection. + type strs; + /// Low-level introspection helper method. + fn strs_sig<F: FnMut(&'static str, Signature<'static>)>(a: Self::strs, f: F); + /// Low-level method to read arguments from a message. + fn read(msg: &Message) -> Result<Self, TypeMismatchError>; + /// Low-level method to append arguments to a message. + fn append(self, msg: &mut Message); +} + +impl ArgBuilder for () { + type strs = (); + fn strs_sig<F: FnMut(&'static str, Signature<'static>)>(_: Self::strs, _: F) {} + fn read(_: &Message) -> Result<Self, TypeMismatchError> { Ok(()) } + fn append(self, _: &mut Message) {} +} + +macro_rules! argbuilder_impl { + ($($n: ident $t: ident $s: ty,)+) => { + +impl<$($t: Arg + Append + for<'z> Get<'z>),*> ArgBuilder for ($($t,)*) { + type strs = ($(&'static $s,)*); + fn strs_sig<Q: FnMut(&'static str, Signature<'static>)>(z: Self::strs, mut q: Q) { + let ( $($n,)*) = z; + $( q($n, $t::signature()); )* + } + + fn read(msg: &Message) -> Result<Self, TypeMismatchError> { + let mut ii = msg.iter_init(); + $( let $n = ii.read()?; )* + Ok(($( $n, )* )) + } + + fn append(self, msg: &mut Message) { + let ( $($n,)*) = self; + let mut ia = IterAppend::new(msg); + $( ia.append($n); )* + } +} + +impl<$($t: Append),*> AppendAll for ($($t,)*) { + fn append(self, ia: &mut IterAppend) { + let ( $($n,)*) = self; + $( ia.append($n); )* + } +} + +impl<$($t: Arg + for<'z> Get<'z>),*> ReadAll for ($($t,)*) { + fn read(ii: &mut Iter) -> Result<Self, TypeMismatchError> { + $( let $n = ii.read()?; )* + Ok(($( $n, )* )) + } +} + + + } +} + +argbuilder_impl!(a A str,); +argbuilder_impl!(a A str, b B str,); +argbuilder_impl!(a A str, b B str, c C str,); +argbuilder_impl!(a A str, b B str, c C str, d D str,); +argbuilder_impl!(a A str, b B str, c C str, d D str, e E str,); +argbuilder_impl!(a A str, b B str, c C str, d D str, e E str, f F str,); +argbuilder_impl!(a A str, b B str, c C str, d D str, e E str, f F str, g G str,); +argbuilder_impl!(a A str, b B str, c C str, d D str, e E str, f F str, g G str, h H str,); +argbuilder_impl!(a A str, b B str, c C str, d D str, e E str, f F str, g G str, h H str, i I str,); +argbuilder_impl!(a A str, b B str, c C str, d D str, e E str, f F str, g G str, h H str, i I str, j J str,); + + + +#[cfg(test)] +mod test { + extern crate tempdir; + + use {Connection, ConnectionItem, Message, BusType, Path, Signature}; + use arg::{Array, Variant, Dict, Iter, ArgType, TypeMismatchError, RefArg, cast}; + + use std::collections::HashMap; + + #[test] + fn refarg() { + let c = Connection::get_private(BusType::Session).unwrap(); + c.register_object_path("/mooh").unwrap(); + let m = Message::new_method_call(&c.unique_name(), "/mooh", "com.example.hello", "Hello").unwrap(); + + let mut vv: Vec<Variant<Box<RefArg>>> = vec!(); + vv.push(Variant(Box::new(5i32))); + vv.push(Variant(Box::new(String::from("Hello world")))); + let m = m.append_ref(&vv); + + let (f1, f2) = (false, 7u64); + let mut v: Vec<&RefArg> = vec!(); + v.push(&f1); + v.push(&f2); + let m = m.append_ref(&v); + let vi32 = vec![7i32, 9i32]; + let vstr: Vec<String> = ["This", "is", "dbus", "rs"].iter().map(|&s| s.into()).collect(); + let m = m.append_ref(&[&vi32 as &RefArg, &vstr as &RefArg]); + let mut map = HashMap::new(); + map.insert(true, String::from("Yes")); + map.insert(false, String::from("No")); + let m = m.append_ref(&[&map as &RefArg, &1.5f64 as &RefArg]); + + c.send(m).unwrap(); + + for n in c.iter(1000) { + if let ConnectionItem::MethodCall(m) = n { + let rv: Vec<Box<RefArg + 'static>> = m.iter_init().collect(); + println!("Receiving {:?}", rv); + let rv0: &Variant<Box<RefArg>> = cast(&rv[0]).unwrap(); + let rv00: &i32 = cast(&rv0.0).unwrap(); + assert_eq!(rv00, &5i32); + assert_eq!(Some(&false), rv[2].as_any().downcast_ref::<bool>()); + assert_eq!(Some(&vi32), rv[4].as_any().downcast_ref::<Vec<i32>>()); + assert_eq!(Some(&vstr), rv[5].as_any().downcast_ref::<Vec<String>>()); + let mut diter = rv[6].as_iter().unwrap(); + { + let mut mmap: HashMap<bool, String> = HashMap::new(); + while let Some(k) = diter.next() { + let x: String = diter.next().unwrap().as_str().unwrap().into(); + mmap.insert(*cast::<bool>(&k.box_clone()).unwrap(), x); + } + assert_eq!(mmap[&true], "Yes"); + } + let mut iter = rv[6].as_iter().unwrap(); + assert!(iter.next().unwrap().as_i64().is_some()); + assert!(iter.next().unwrap().as_str().is_some()); + assert!(iter.next().unwrap().as_str().is_none()); + assert!(iter.next().unwrap().as_i64().is_none()); + assert!(iter.next().is_none()); + assert!(rv[7].as_f64().unwrap() > 1.0); + assert!(rv[7].as_f64().unwrap() < 2.0); + break; + } + } + } + + #[test] + fn message_types() { + let c = Connection::get_private(BusType::Session).unwrap(); + c.register_object_path("/hello").unwrap(); + let m = Message::new_method_call(&c.unique_name(), "/hello", "com.example.hello", "Hello").unwrap(); + let m = m.append1(2000u16); + let m = m.append1(Array::new(&vec![129u8, 5, 254])); + let m = m.append2(Variant(&["Hello", "world"][..]), &[32768u16, 16u16, 12u16][..]); + let m = m.append3(-1i32, &*format!("Hello world"), -3.14f64); + let m = m.append1((256i16, Variant(18_446_744_073_709_551_615u64))); + let m = m.append2(Path::new("/a/valid/path").unwrap(), Signature::new("a{sv}").unwrap()); + let mut z = HashMap::new(); + z.insert(123543u32, true); + z.insert(0u32, false); + let m = m.append1(Dict::new(&z)); + let sending = format!("{:?}", m.iter_init()); + println!("Sending {}", sending); + c.send(m).unwrap(); + + for n in c.iter(1000) { + match n { + ConnectionItem::MethodCall(m) => { + use super::Arg; + let receiving = format!("{:?}", m.iter_init()); + println!("Receiving {}", receiving); + assert_eq!(sending, receiving); + + assert_eq!(2000u16, m.get1().unwrap()); + assert_eq!(m.get2(), (Some(2000u16), Some(&[129u8, 5, 254][..]))); + assert_eq!(m.read2::<u16, bool>().unwrap_err(), + TypeMismatchError { position: 1, found: ArgType::Array, expected: ArgType::Boolean }); + + let mut g = m.iter_init(); + let e = g.read::<u32>().unwrap_err(); + assert_eq!(e.pos(), 0); + assert_eq!(e.expected_arg_type(), ArgType::UInt32); + assert_eq!(e.found_arg_type(), ArgType::UInt16); + + assert!(g.next() && g.next()); + let v: Variant<Iter> = g.get().unwrap(); + let mut viter = v.0; + assert_eq!(viter.arg_type(), Array::<&str,()>::ARG_TYPE); + let a: Array<&str, _> = viter.get().unwrap(); + assert_eq!(a.collect::<Vec<&str>>(), vec!["Hello", "world"]); + + assert!(g.next()); + assert_eq!(g.get::<u16>(), None); // It's an array, not a single u16 + assert!(g.next() && g.next() && g.next() && g.next()); + + assert_eq!(g.get(), Some((256i16, Variant(18_446_744_073_709_551_615u64)))); + assert!(g.next()); + assert_eq!(g.get(), Some(Path::new("/a/valid/path").unwrap())); + assert!(g.next()); + assert_eq!(g.get(), Some(Signature::new("a{sv}").unwrap())); + assert!(g.next()); + let d: Dict<u32, bool, _> = g.get().unwrap(); + let z2: HashMap<_, _> = d.collect(); + assert_eq!(z, z2); + break; + } + _ => println!("Got {:?}", n), + } + } + } +} diff --git a/third_party/rust/dbus/src/arg/variantstruct_impl.rs b/third_party/rust/dbus/src/arg/variantstruct_impl.rs new file mode 100644 index 0000000000..d58c99e098 --- /dev/null +++ b/third_party/rust/dbus/src/arg/variantstruct_impl.rs @@ -0,0 +1,242 @@ +use super::*; +use {message, Signature}; +use std::any; + +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] +/// A simple wrapper to specify a D-Bus variant. +/// +/// See the argument guide and module level documentation for details and examples. +pub struct Variant<T>(pub T); + +impl Variant<Box<RefArg>> { + /// Creates a new refarg from an Iter. Mainly for internal use. + pub fn new_refarg<'a>(i: &mut Iter<'a>) -> Option<Self> { + i.recurse(ArgType::Variant).and_then(|mut si| si.get_refarg()).map(|v| Variant(v)) + } +} + +impl Default for Variant<Box<RefArg>> { + // This is a bit silly, because there is no such thing as a default argument. + // Unfortunately due to a design mistake while making the SignalArgs trait, we'll + // have to work around that by adding a default implementation here. + // https://github.com/diwic/dbus-rs/issues/136 + fn default() -> Self { Variant(Box::new(0u8) as Box<RefArg>) } +} + +impl<T:Default> Default for Variant<T> { + fn default() -> Self { Variant(T::default()) } +} + + +impl<T> Arg for Variant<T> { + const ARG_TYPE: ArgType = ArgType::Variant; + fn signature() -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"v\0") } } +} + +impl<T: Arg + Append> Append for Variant<T> { + fn append(self, i: &mut IterAppend) { + let z = self.0; + i.append_container(ArgType::Variant, Some(T::signature().as_cstr()), |s| z.append(s)); + } +} + +impl Append for Variant<message::MessageItem> { + fn append(self, i: &mut IterAppend) { + let z = self.0; + let asig = z.signature(); + let sig = asig.as_cstr(); + i.append_container(ArgType::Variant, Some(&sig), |s| z.append(s)); + } +} + +impl Append for Variant<Box<RefArg>> { + fn append(self, i: &mut IterAppend) { + let z = self.0; + i.append_container(ArgType::Variant, Some(z.signature().as_cstr()), |s| z.append(s)); + } +} + +impl<'a, T: Get<'a>> Get<'a> for Variant<T> { + fn get(i: &mut Iter<'a>) -> Option<Variant<T>> { + i.recurse(ArgType::Variant).and_then(|mut si| si.get().map(|v| Variant(v))) + } +} + +impl<'a> Get<'a> for Variant<Iter<'a>> { + fn get(i: &mut Iter<'a>) -> Option<Variant<Iter<'a>>> { + i.recurse(ArgType::Variant).map(|v| Variant(v)) + } +} +/* +impl<'a> Get<'a> for Variant<Box<RefArg>> { + fn get(i: &mut Iter<'a>) -> Option<Variant<Box<RefArg>>> { + i.recurse(ArgType::Variant).and_then(|mut si| si.get_refarg().map(|v| Variant(v))) + } +} +*/ +impl<T: RefArg> RefArg for Variant<T> { + fn arg_type(&self) -> ArgType { ArgType::Variant } + fn signature(&self) -> Signature<'static> { unsafe { Signature::from_slice_unchecked(b"v\0") } } + fn append(&self, i: &mut IterAppend) { + let z = &self.0; + i.append_container(ArgType::Variant, Some(z.signature().as_cstr()), |s| z.append(s)); + } + #[inline] + fn as_any(&self) -> &any::Any where T: 'static { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any where T: 'static { self } + #[inline] + fn as_i64(&self) -> Option<i64> { self.0.as_i64() } + #[inline] + fn as_u64(&self) -> Option<u64> { self.0.as_u64() } + #[inline] + fn as_f64(&self) -> Option<f64> { self.0.as_f64() } + #[inline] + fn as_str(&self) -> Option<&str> { self.0.as_str() } + #[inline] + fn as_iter<'a>(&'a self) -> Option<Box<Iterator<Item=&'a RefArg> + 'a>> { + use std::iter; + let z: &RefArg = &self.0; + Some(Box::new(iter::once(z))) + } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { Box::new(Variant(self.0.box_clone())) } +} + +macro_rules! struct_impl { + ( $($n: ident $t: ident,)+ ) => { + +/// Tuples are represented as D-Bus structs. +impl<$($t: Arg),*> Arg for ($($t,)*) { + const ARG_TYPE: ArgType = ArgType::Struct; + fn signature() -> Signature<'static> { + let mut s = String::from("("); + $( s.push_str(&$t::signature()); )* + s.push_str(")"); + Signature::from(s) + } +} + +impl<$($t: Append),*> Append for ($($t,)*) { + fn append(self, i: &mut IterAppend) { + let ( $($n,)*) = self; + i.append_container(ArgType::Struct, None, |s| { $( $n.append(s); )* }); + } +} + +impl<'a, $($t: Get<'a>),*> Get<'a> for ($($t,)*) { + fn get(i: &mut Iter<'a>) -> Option<Self> { + let si = i.recurse(ArgType::Struct); + if si.is_none() { return None; } + let mut si = si.unwrap(); + let mut _valid_item = true; + $( + if !_valid_item { return None; } + let $n: Option<$t> = si.get(); + if $n.is_none() { return None; } + _valid_item = si.next(); + )* + Some(($( $n.unwrap(), )* )) + } +} + +impl<$($t: RefArg),*> RefArg for ($($t,)*) { + fn arg_type(&self) -> ArgType { ArgType::Struct } + fn signature(&self) -> Signature<'static> { + let &( $(ref $n,)*) = self; + let mut s = String::from("("); + $( s.push_str(&$n.signature()); )* + s.push_str(")"); + Signature::from(s) + } + fn append(&self, i: &mut IterAppend) { + let &( $(ref $n,)*) = self; + i.append_container(ArgType::Struct, None, |s| { $( $n.append(s); )* }); + } + fn as_any(&self) -> &any::Any where Self: 'static { self } + fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } + fn as_iter<'a>(&'a self) -> Option<Box<Iterator<Item=&'a RefArg> + 'a>> { + let &( $(ref $n,)*) = self; + let v = vec!( + $( $n as &RefArg, )* + ); + Some(Box::new(v.into_iter())) + } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { + let &( $(ref $n,)*) = self; + let mut z = vec!(); + $( z.push($n.box_clone()); )* + Box::new(z) + } +} + + +}} // macro_rules end + +struct_impl!(a A,); +struct_impl!(a A, b B,); +struct_impl!(a A, b B, c C,); +struct_impl!(a A, b B, c C, d D,); +struct_impl!(a A, b B, c C, d D, e E,); +struct_impl!(a A, b B, c C, d D, e E, f F,); +struct_impl!(a A, b B, c C, d D, e E, f F, g G,); +struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H,); +struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H, i I,); +struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H, i I, j J,); +struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H, i I, j J, k K,); +struct_impl!(a A, b B, c C, d D, e E, f F, g G, h H, i I, j J, k K, l L,); + +impl RefArg for Vec<Box<RefArg>> { + fn arg_type(&self) -> ArgType { ArgType::Struct } + fn signature(&self) -> Signature<'static> { + let mut s = String::from("("); + for z in self { + s.push_str(&z.signature()); + } + s.push_str(")"); + Signature::from(s) + } + fn append(&self, i: &mut IterAppend) { + i.append_container(ArgType::Struct, None, |s| { + for z in self { z.append(s); } + }); + } + #[inline] + fn as_any(&self) -> &any::Any where Self: 'static { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } + fn as_iter<'a>(&'a self) -> Option<Box<Iterator<Item=&'a RefArg> + 'a>> { + Some(Box::new(self.iter().map(|b| &**b))) + } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { + let t: Vec<Box<RefArg + 'static>> = self.iter().map(|x| x.box_clone()).collect(); + Box::new(t) + } +} + +impl Append for message::MessageItem { + fn append(self, i: &mut IterAppend) { + message::append_messageitem(&mut i.0, &self) + } +} + +impl<'a> Get<'a> for message::MessageItem { + fn get(i: &mut Iter<'a>) -> Option<Self> { + message::get_messageitem(&mut i.0) + } +} + +impl RefArg for message::MessageItem { + fn arg_type(&self) -> ArgType { ArgType::from_i32(self.array_type()).unwrap() } + fn signature(&self) -> Signature<'static> { message::MessageItem::signature(&self) } + fn append(&self, i: &mut IterAppend) { message::append_messageitem(&mut i.0, self) } + #[inline] + fn as_any(&self) -> &any::Any where Self: 'static { self } + #[inline] + fn as_any_mut(&mut self) -> &mut any::Any where Self: 'static { self } + #[inline] + fn box_clone(&self) -> Box<RefArg + 'static> { Box::new(self.clone()) } +} + diff --git a/third_party/rust/dbus/src/connection.rs b/third_party/rust/dbus/src/connection.rs new file mode 100644 index 0000000000..18b8374da8 --- /dev/null +++ b/third_party/rust/dbus/src/connection.rs @@ -0,0 +1,733 @@ +use super::{Error, ffi, to_c_str, c_str_to_slice, Watch, Message, MessageType, BusName, Path, ConnPath}; +use super::{RequestNameReply, ReleaseNameReply, BusType}; +use super::watch::WatchList; +use std::{fmt, mem, ptr, thread, panic, ops}; +use std::collections::VecDeque; +use std::cell::{Cell, RefCell}; +use std::os::unix::io::RawFd; +use std::os::raw::{c_void, c_char, c_int, c_uint}; + +/// The type of function to use for replacing the message callback. +/// +/// See the documentation for Connection::replace_message_callback for more information. +pub type MessageCallback = Box<FnMut(&Connection, Message) -> bool + 'static>; + +#[repr(C)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] +/// Flags to use for Connection::register_name. +/// +/// More than one flag can be specified, if so just add their values. +pub enum DBusNameFlag { + /// Allow another service to become the primary owner if requested + AllowReplacement = ffi::DBUS_NAME_FLAG_ALLOW_REPLACEMENT as isize, + /// Request to replace the current primary owner + ReplaceExisting = ffi::DBUS_NAME_FLAG_REPLACE_EXISTING as isize, + /// If we can not become the primary owner do not place us in the queue + DoNotQueue = ffi::DBUS_NAME_FLAG_DO_NOT_QUEUE as isize, +} + +impl DBusNameFlag { + /// u32 value of flag. + pub fn value(self) -> u32 { self as u32 } +} + +/// When listening for incoming events on the D-Bus, this enum will tell you what type +/// of incoming event has happened. +#[derive(Debug)] +pub enum ConnectionItem { + /// No event between now and timeout + Nothing, + /// Incoming method call + MethodCall(Message), + /// Incoming signal + Signal(Message), + /// Incoming method return, including method return errors (mostly used for Async I/O) + MethodReturn(Message), +} + +impl From<Message> for ConnectionItem { + fn from(m: Message) -> Self { + let mtype = m.msg_type(); + match mtype { + MessageType::Signal => ConnectionItem::Signal(m), + MessageType::MethodReturn => ConnectionItem::MethodReturn(m), + MessageType::Error => ConnectionItem::MethodReturn(m), + MessageType::MethodCall => ConnectionItem::MethodCall(m), + _ => panic!("unknown message type {:?} received from D-Bus", mtype), + } + } +} + + +/// ConnectionItem iterator +pub struct ConnectionItems<'a> { + c: &'a Connection, + timeout_ms: Option<i32>, + end_on_timeout: bool, + handlers: MsgHandlerList, +} + +impl<'a> ConnectionItems<'a> { + /// Builder method that adds a new msg handler. + /// + /// Note: Likely to changed/refactored/removed in next release + pub fn with<H: 'static + MsgHandler>(mut self, h: H) -> Self { + self.handlers.push(Box::new(h)); self + } + + // Returns true if processed, false if not + fn process_handlers(&mut self, ci: &ConnectionItem) -> bool { + let m = match *ci { + ConnectionItem::MethodReturn(ref msg) => msg, + ConnectionItem::Signal(ref msg) => msg, + ConnectionItem::MethodCall(ref msg) => msg, + ConnectionItem::Nothing => return false, + }; + + msghandler_process(&mut self.handlers, m, &self.c) + } + + /// Access and modify message handlers + /// + /// Note: Likely to changed/refactored/removed in next release + pub fn msg_handlers(&mut self) -> &mut Vec<Box<MsgHandler>> { &mut self.handlers } + + /// Creates a new ConnectionItems iterator + /// + /// For io_timeout, setting None means the fds will not be read/written. I e, only pending + /// items in libdbus's internal queue will be processed. + /// + /// For end_on_timeout, setting false will means that the iterator will never finish (unless + /// the D-Bus server goes down). Instead, ConnectionItem::Nothing will be returned in case no + /// items are in queue. + pub fn new(conn: &'a Connection, io_timeout: Option<i32>, end_on_timeout: bool) -> Self { + ConnectionItems { + c: conn, + timeout_ms: io_timeout, + end_on_timeout: end_on_timeout, + handlers: Vec::new(), + } + } +} + +impl<'a> Iterator for ConnectionItems<'a> { + type Item = ConnectionItem; + fn next(&mut self) -> Option<ConnectionItem> { + loop { + if self.c.i.filter_cb.borrow().is_none() { panic!("ConnectionItems::next called recursively or with a MessageCallback set to None"); } + let i: Option<ConnectionItem> = self.c.next_msg().map(|x| x.into()); + if let Some(ci) = i { + if !self.process_handlers(&ci) { return Some(ci); } + } + + if let Some(t) = self.timeout_ms { + let r = unsafe { ffi::dbus_connection_read_write_dispatch(self.c.conn(), t as c_int) }; + self.c.check_panic(); + if !self.c.i.pending_items.borrow().is_empty() { continue }; + if r == 0 { return None; } + } + + let r = unsafe { ffi::dbus_connection_dispatch(self.c.conn()) }; + self.c.check_panic(); + + if !self.c.i.pending_items.borrow().is_empty() { continue }; + if r == ffi::DBusDispatchStatus::DataRemains { continue }; + if r == ffi::DBusDispatchStatus::Complete { return if self.end_on_timeout { None } else { Some(ConnectionItem::Nothing) } }; + panic!("dbus_connection_dispatch failed"); + } + } +} + +/// Iterator over incoming messages on a connection. +#[derive(Debug, Clone)] +pub struct ConnMsgs<C> { + /// The connection or some reference to it. + pub conn: C, + /// How many ms dbus should block, waiting for incoming messages until timing out. + /// + /// If set to None, the dbus library will not read/write from file descriptors at all. + /// Instead the iterator will end when there's nothing currently in the queue. + pub timeout_ms: Option<u32>, +} + +impl<C: ops::Deref<Target = Connection>> Iterator for ConnMsgs<C> { + type Item = Message; + fn next(&mut self) -> Option<Self::Item> { + + loop { + let iconn = &self.conn.i; + if iconn.filter_cb.borrow().is_none() { panic!("ConnMsgs::next called recursively or with a MessageCallback set to None"); } + let i = self.conn.next_msg(); + if let Some(ci) = i { return Some(ci); } + + if let Some(t) = self.timeout_ms { + let r = unsafe { ffi::dbus_connection_read_write_dispatch(self.conn.conn(), t as c_int) }; + self.conn.check_panic(); + if !iconn.pending_items.borrow().is_empty() { continue }; + if r == 0 { return None; } + } + + let r = unsafe { ffi::dbus_connection_dispatch(self.conn.conn()) }; + self.conn.check_panic(); + + if !iconn.pending_items.borrow().is_empty() { continue }; + if r == ffi::DBusDispatchStatus::DataRemains { continue }; + if r == ffi::DBusDispatchStatus::Complete { return None } + panic!("dbus_connection_dispatch failed"); + } + } +} + +/* Since we register callbacks with userdata pointers, + we need to make sure the connection pointer does not move around. + Hence this extra indirection. */ +struct IConnection { + conn: Cell<*mut ffi::DBusConnection>, + pending_items: RefCell<VecDeque<Message>>, + watches: Option<Box<WatchList>>, + handlers: RefCell<MsgHandlerList>, + + filter_cb: RefCell<Option<MessageCallback>>, + filter_cb_panic: RefCell<thread::Result<()>>, +} + +/// A D-Bus connection. Start here if you want to get on the D-Bus! +pub struct Connection { + i: Box<IConnection>, +} + +pub fn conn_handle(c: &Connection) -> *mut ffi::DBusConnection { + c.i.conn.get() +} + +extern "C" fn filter_message_cb(conn: *mut ffi::DBusConnection, msg: *mut ffi::DBusMessage, + user_data: *mut c_void) -> ffi::DBusHandlerResult { + + let i: &IConnection = unsafe { mem::transmute(user_data) }; + let connref: panic::AssertUnwindSafe<&Connection> = unsafe { mem::transmute(&i) }; + if i.conn.get() != conn || i.filter_cb_panic.try_borrow().is_err() { + // This should never happen, but let's be extra sure + // process::abort(); ?? + return ffi::DBusHandlerResult::Handled; + } + if i.filter_cb_panic.borrow().is_err() { + // We're in panic mode. Let's quit this ASAP + return ffi::DBusHandlerResult::Handled; + } + + let fcb = panic::AssertUnwindSafe(&i.filter_cb); + let r = panic::catch_unwind(|| { + let m = Message::from_ptr(msg, true); + let mut cb = fcb.borrow_mut().take().unwrap(); // Take the callback out while we call it. + let r = cb(connref.0, m); + let mut cb2 = fcb.borrow_mut(); // If the filter callback has not been replaced, put it back in. + if cb2.is_none() { *cb2 = Some(cb) }; + r + }); + + match r { + Ok(false) => ffi::DBusHandlerResult::NotYetHandled, + Ok(true) => ffi::DBusHandlerResult::Handled, + Err(e) => { + *i.filter_cb_panic.borrow_mut() = Err(e); + ffi::DBusHandlerResult::Handled + } + } +} + +fn default_filter_callback(c: &Connection, m: Message) -> bool { + let b = m.msg_type() == MessageType::Signal; + c.i.pending_items.borrow_mut().push_back(m); + b +} + +extern "C" fn object_path_message_cb(_conn: *mut ffi::DBusConnection, _msg: *mut ffi::DBusMessage, + _user_data: *mut c_void) -> ffi::DBusHandlerResult { + /* Already pushed in filter_message_cb, so we just set the handled flag here to disable the + "default" handler. */ + ffi::DBusHandlerResult::Handled +} + +impl Connection { + #[inline(always)] + fn conn(&self) -> *mut ffi::DBusConnection { + self.i.conn.get() + } + + fn conn_from_ptr(conn: *mut ffi::DBusConnection) -> Result<Connection, Error> { + let mut c = Connection { i: Box::new(IConnection { + conn: Cell::new(conn), + pending_items: RefCell::new(VecDeque::new()), + watches: None, + handlers: RefCell::new(vec!()), + filter_cb: RefCell::new(Some(Box::new(default_filter_callback))), + filter_cb_panic: RefCell::new(Ok(())), + })}; + + /* No, we don't want our app to suddenly quit if dbus goes down */ + unsafe { ffi::dbus_connection_set_exit_on_disconnect(conn, 0) }; + assert!(unsafe { + ffi::dbus_connection_add_filter(c.conn(), Some(filter_message_cb), mem::transmute(&*c.i), None) + } != 0); + + c.i.watches = Some(WatchList::new(&c, Box::new(|_| {}))); + Ok(c) + } + + /// Creates a new D-Bus connection. + pub fn get_private(bus: BusType) -> Result<Connection, Error> { + let mut e = Error::empty(); + let conn = unsafe { ffi::dbus_bus_get_private(bus, e.get_mut()) }; + if conn == ptr::null_mut() { + return Err(e) + } + Self::conn_from_ptr(conn) + } + + /// Creates a new D-Bus connection to a remote address. + /// + /// Note: for all common cases (System / Session bus) you probably want "get_private" instead. + pub fn open_private(address: &str) -> Result<Connection, Error> { + let mut e = Error::empty(); + let conn = unsafe { ffi::dbus_connection_open_private(to_c_str(address).as_ptr(), e.get_mut()) }; + if conn == ptr::null_mut() { + return Err(e) + } + Self::conn_from_ptr(conn) + } + + /// Registers a new D-Bus connection with the bus. + /// + /// Note: `get_private` does this automatically, useful with `open_private` + pub fn register(&self) -> Result<(), Error> { + let mut e = Error::empty(); + if unsafe { ffi::dbus_bus_register(self.conn(), e.get_mut()) == 0 } { + Err(e) + } else { + Ok(()) + } + } + + /// Gets whether the connection is currently open. + pub fn is_connected(&self) -> bool { + unsafe { ffi::dbus_connection_get_is_connected(self.conn()) != 0 } + } + + /// Sends a message over the D-Bus and waits for a reply. + /// This is usually used for method calls. + pub fn send_with_reply_and_block(&self, msg: Message, timeout_ms: i32) -> Result<Message, Error> { + let mut e = Error::empty(); + let response = unsafe { + ffi::dbus_connection_send_with_reply_and_block(self.conn(), msg.ptr(), + timeout_ms as c_int, e.get_mut()) + }; + if response == ptr::null_mut() { + return Err(e); + } + Ok(Message::from_ptr(response, false)) + } + + /// Sends a message over the D-Bus without waiting. Useful for sending signals and method call replies. + pub fn send(&self, msg: Message) -> Result<u32,()> { + let mut serial = 0u32; + let r = unsafe { ffi::dbus_connection_send(self.conn(), msg.ptr(), &mut serial) }; + if r == 0 { return Err(()); } + unsafe { ffi::dbus_connection_flush(self.conn()) }; + Ok(serial) + } + + /// Sends a message over the D-Bus, returning a MessageReply. + /// + /// Call add_handler on the result to start waiting for reply. This should be done before next call to `incoming` or `iter`. + pub fn send_with_reply<'a, F: FnOnce(Result<&Message, Error>) + 'a>(&self, msg: Message, f: F) -> Result<MessageReply<F>, ()> { + let serial = self.send(msg)?; + Ok(MessageReply(Some(f), serial)) + } + + /// Adds a message handler to the connection. + /// + /// # Example + /// + /// ``` + /// use std::{cell, rc}; + /// use dbus::{Connection, Message, BusType}; + /// + /// let c = Connection::get_private(BusType::Session).unwrap(); + /// let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap(); + /// + /// let done: rc::Rc<cell::Cell<bool>> = Default::default(); + /// let done2 = done.clone(); + /// c.add_handler(c.send_with_reply(m, move |reply| { + /// let v: Vec<&str> = reply.unwrap().read1().unwrap(); + /// println!("The names on the D-Bus are: {:?}", v); + /// done2.set(true); + /// }).unwrap()); + /// while !done.get() { c.incoming(100).next(); } + /// ``` + pub fn add_handler<H: MsgHandler + 'static>(&self, h: H) { + let h = Box::new(h); + self.i.handlers.borrow_mut().push(h); + } + + /// Removes a MsgHandler from the connection. + /// + /// If there are many MsgHandlers, it is not specified which one will be returned. + /// + /// There might be more methods added later on, which give better ways to deal + /// with the list of MsgHandler currently on the connection. If this would help you, + /// please [file an issue](https://github.com/diwic/dbus-rs/issues). + pub fn extract_handler(&self) -> Option<Box<MsgHandler>> { + self.i.handlers.borrow_mut().pop() + } + + /// Get the connection's unique name. + pub fn unique_name(&self) -> String { + let c = unsafe { ffi::dbus_bus_get_unique_name(self.conn()) }; + c_str_to_slice(&c).unwrap_or("").to_string() + } + + /// Check if there are new incoming events + /// + /// If there are no incoming events, ConnectionItems::Nothing will be returned. + /// See ConnectionItems::new if you want to customize this behaviour. + pub fn iter(&self, timeout_ms: i32) -> ConnectionItems { + ConnectionItems::new(self, Some(timeout_ms), false) + } + + /// Check if there are new incoming events + /// + /// Supersedes "iter". + pub fn incoming(&self, timeout_ms: u32) -> ConnMsgs<&Self> { + ConnMsgs { conn: &self, timeout_ms: Some(timeout_ms) } + } + + /// Register an object path. + pub fn register_object_path(&self, path: &str) -> Result<(), Error> { + let mut e = Error::empty(); + let p = to_c_str(path); + let vtable = ffi::DBusObjectPathVTable { + unregister_function: None, + message_function: Some(object_path_message_cb), + dbus_internal_pad1: None, + dbus_internal_pad2: None, + dbus_internal_pad3: None, + dbus_internal_pad4: None, + }; + let r = unsafe { + let user_data: *mut c_void = mem::transmute(&*self.i); + ffi::dbus_connection_try_register_object_path(self.conn(), p.as_ptr(), &vtable, user_data, e.get_mut()) + }; + if r == 0 { Err(e) } else { Ok(()) } + } + + /// Unregister an object path. + pub fn unregister_object_path(&self, path: &str) { + let p = to_c_str(path); + let r = unsafe { ffi::dbus_connection_unregister_object_path(self.conn(), p.as_ptr()) }; + if r == 0 { panic!("Out of memory"); } + } + + /// List registered object paths. + pub fn list_registered_object_paths(&self, path: &str) -> Vec<String> { + let p = to_c_str(path); + let mut clist: *mut *mut c_char = ptr::null_mut(); + let r = unsafe { ffi::dbus_connection_list_registered(self.conn(), p.as_ptr(), &mut clist) }; + if r == 0 { panic!("Out of memory"); } + let mut v = Vec::new(); + let mut i = 0; + loop { + let s = unsafe { + let citer = clist.offset(i); + if *citer == ptr::null_mut() { break }; + mem::transmute(citer) + }; + v.push(format!("{}", c_str_to_slice(s).unwrap())); + i += 1; + } + unsafe { ffi::dbus_free_string_array(clist) }; + v + } + + /// Register a name. + pub fn register_name(&self, name: &str, flags: u32) -> Result<RequestNameReply, Error> { + let mut e = Error::empty(); + let n = to_c_str(name); + let r = unsafe { ffi::dbus_bus_request_name(self.conn(), n.as_ptr(), flags, e.get_mut()) }; + if r == -1 { Err(e) } else { Ok(unsafe { mem::transmute(r) }) } + } + + /// Release a name. + pub fn release_name(&self, name: &str) -> Result<ReleaseNameReply, Error> { + let mut e = Error::empty(); + let n = to_c_str(name); + let r = unsafe { ffi::dbus_bus_release_name(self.conn(), n.as_ptr(), e.get_mut()) }; + if r == -1 { Err(e) } else { Ok(unsafe { mem::transmute(r) }) } + } + + /// Add a match rule to match messages on the message bus. + /// + /// See the `unity_focused_window` example for how to use this to catch signals. + /// (The syntax of the "rule" string is specified in the [D-Bus specification](https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules).) + pub fn add_match(&self, rule: &str) -> Result<(), Error> { + let mut e = Error::empty(); + let n = to_c_str(rule); + unsafe { ffi::dbus_bus_add_match(self.conn(), n.as_ptr(), e.get_mut()) }; + if e.name().is_some() { Err(e) } else { Ok(()) } + } + + /// Remove a match rule to match messages on the message bus. + pub fn remove_match(&self, rule: &str) -> Result<(), Error> { + let mut e = Error::empty(); + let n = to_c_str(rule); + unsafe { ffi::dbus_bus_remove_match(self.conn(), n.as_ptr(), e.get_mut()) }; + if e.name().is_some() { Err(e) } else { Ok(()) } + } + + /// Async I/O: Get an up-to-date list of file descriptors to watch. + /// + /// See the `Watch` struct for an example. + pub fn watch_fds(&self) -> Vec<Watch> { + self.i.watches.as_ref().unwrap().get_enabled_fds() + } + + /// Async I/O: Call this function whenever you detected an event on the Fd, + /// Flags are a set of WatchEvent bits. + /// The returned iterator will return pending items only, never block for new events. + /// + /// See the `Watch` struct for an example. + pub fn watch_handle(&self, fd: RawFd, flags: c_uint) -> ConnectionItems { + self.i.watches.as_ref().unwrap().watch_handle(fd, flags); + ConnectionItems::new(self, None, true) + } + + + /// Create a convenience struct for easier calling of many methods on the same destination and path. + pub fn with_path<'a, D: Into<BusName<'a>>, P: Into<Path<'a>>>(&'a self, dest: D, path: P, timeout_ms: i32) -> + ConnPath<'a, &'a Connection> { + ConnPath { conn: self, dest: dest.into(), path: path.into(), timeout: timeout_ms } + } + + /// Replace the default message callback. Returns the previously set callback. + /// + /// By default, when you call ConnectionItems::next, all relevant incoming messages + /// are returned through the ConnectionItems iterator, and + /// irrelevant messages are passed on to libdbus's default handler. + /// If you need to customize this behaviour (i e, to handle all incoming messages yourself), + /// you can set this message callback yourself. A few caveats apply: + /// + /// Return true from the callback to disable libdbus's internal handling of the message, or + /// false to allow it. In other words, true and false correspond to + /// `DBUS_HANDLER_RESULT_HANDLED` and `DBUS_HANDLER_RESULT_NOT_YET_HANDLED` respectively. + /// + /// Be sure to call the previously set callback from inside your callback, + /// if you want, e.g. ConnectionItems::next to yield the message. + /// + /// You can unset the message callback (might be useful to satisfy the borrow checker), but + /// you will get a panic if you call ConnectionItems::next while the message callback is unset. + /// The message callback will be temporary unset while inside a message callback, so calling + /// ConnectionItems::next recursively will also result in a panic. + /// + /// If your message callback panics, ConnectionItems::next will panic, too. + /// + /// # Examples + /// + /// Replace the default callback with our own: + /// + /// ```ignore + /// use dbus::{Connection, BusType}; + /// let c = Connection::get_private(BusType::Session).unwrap(); + /// // Set our callback + /// c.replace_message_callback(Some(Box::new(move |conn, msg| { + /// println!("Got message: {:?}", msg.get_items()); + /// // Let libdbus handle some things by default, + /// // like "nonexistent object" error replies to method calls + /// false + /// }))); + /// + /// for _ in c.iter(1000) { + /// // Only `ConnectionItem::Nothing` would be ever yielded here. + /// } + /// ``` + /// + /// Chain our callback to filter out some messages before `iter().next()`: + /// + /// ``` + /// use dbus::{Connection, BusType, MessageType}; + /// let c = Connection::get_private(BusType::Session).unwrap(); + /// // Take the previously set callback + /// let mut old_cb = c.replace_message_callback(None).unwrap(); + /// // Set our callback + /// c.replace_message_callback(Some(Box::new(move |conn, msg| { + /// // Handle all signals on the spot + /// if msg.msg_type() == MessageType::Signal { + /// println!("Got signal: {:?}", msg.get_items()); + /// // Stop all further processing of the message + /// return true; + /// } + /// // Delegate the rest of the messages to the previous callback + /// // in chain, e.g. to have them yielded by `iter().next()` + /// old_cb(conn, msg) + /// }))); + /// + /// # if false { + /// for _ in c.iter(1000) { + /// // `ConnectionItem::Signal` would never be yielded here. + /// } + /// # } + /// ``` + pub fn replace_message_callback(&self, f: Option<MessageCallback>) -> Option<MessageCallback> { + mem::replace(&mut *self.i.filter_cb.borrow_mut(), f) + } + + /// Sets a callback to be called if a file descriptor status changes. + /// + /// For async I/O. In rare cases, the number of fds to poll for read/write can change. + /// If this ever happens, you'll get a callback. The watch changed is provided as a parameter. + /// + /// In rare cases this might not even happen in the thread calling anything on the connection, + /// so the callback needs to be `Send`. + /// A mutex is held during the callback. If you try to call set_watch_callback from a callback, + /// you will deadlock. + /// + /// (Previously, this was instead put in a ConnectionItem queue, but this was not working correctly. + /// see https://github.com/diwic/dbus-rs/issues/99 for additional info.) + pub fn set_watch_callback(&self, f: Box<Fn(Watch) + Send>) { self.i.watches.as_ref().unwrap().set_on_update(f); } + + fn check_panic(&self) { + let p = mem::replace(&mut *self.i.filter_cb_panic.borrow_mut(), Ok(())); + if let Err(perr) = p { panic::resume_unwind(perr); } + } + + fn next_msg(&self) -> Option<Message> { + while let Some(msg) = self.i.pending_items.borrow_mut().pop_front() { + let mut v: MsgHandlerList = mem::replace(&mut *self.i.handlers.borrow_mut(), vec!()); + let b = msghandler_process(&mut v, &msg, self); + let mut v2 = self.i.handlers.borrow_mut(); + v.append(&mut *v2); + *v2 = v; + if !b { return Some(msg) }; + }; + None + } + +} + +impl Drop for Connection { + fn drop(&mut self) { + unsafe { + ffi::dbus_connection_close(self.conn()); + ffi::dbus_connection_unref(self.conn()); + } + } +} + +impl fmt::Debug for Connection { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "D-Bus Connection({})", self.unique_name()) + } +} + +#[derive(Clone, Debug)] +/// Type of messages to be handled by a MsgHandler. +/// +/// Note: More variants can be added in the future; but unless you're writing your own D-Bus engine +/// you should not have to match on these anyway. +pub enum MsgHandlerType { + /// Handle all messages + All, + /// Handle only messages of a specific type + MsgType(MessageType), + /// Handle only method replies with this serial number + Reply(u32), +} + +impl MsgHandlerType { + fn matches_msg(&self, m: &Message) -> bool { + match *self { + MsgHandlerType::All => true, + MsgHandlerType::MsgType(t) => m.msg_type() == t, + MsgHandlerType::Reply(serial) => { + let t = m.msg_type(); + ((t == MessageType::MethodReturn) || (t == MessageType::Error)) && (m.get_reply_serial() == Some(serial)) + } + } + } +} + +/// A trait for handling incoming messages. +pub trait MsgHandler { + /// Type of messages for which the handler will be called + /// + /// Note: The return value of this function might be cached, so it must return the same value all the time. + fn handler_type(&self) -> MsgHandlerType; + + /// Function to be called if the message matches the MsgHandlerType + fn handle_msg(&mut self, _msg: &Message) -> Option<MsgHandlerResult> { None } +} + +/// The result from MsgHandler::handle. +#[derive(Debug, Default)] +pub struct MsgHandlerResult { + /// Indicates that the message has been dealt with and should not be processed further. + pub handled: bool, + /// Indicates that this MsgHandler no longer wants to receive messages and should be removed. + pub done: bool, + /// Messages to send (e g, a reply to a method call) + pub reply: Vec<Message>, +} + +type MsgHandlerList = Vec<Box<MsgHandler>>; + +fn msghandler_process(v: &mut MsgHandlerList, m: &Message, c: &Connection) -> bool { + let mut ii: isize = -1; + loop { + ii += 1; + let i = ii as usize; + if i >= v.len() { return false }; + + if !v[i].handler_type().matches_msg(m) { continue; } + if let Some(r) = v[i].handle_msg(m) { + for msg in r.reply.into_iter() { c.send(msg).unwrap(); } + if r.done { v.remove(i); ii -= 1; } + if r.handled { return true; } + } + } +} + +/// The struct returned from `Connection::send_and_reply`. +/// +/// It implements the `MsgHandler` trait so you can use `Connection::add_handler`. +pub struct MessageReply<F>(Option<F>, u32); + +impl<'a, F: FnOnce(Result<&Message, Error>) + 'a> MsgHandler for MessageReply<F> { + fn handler_type(&self) -> MsgHandlerType { MsgHandlerType::Reply(self.1) } + fn handle_msg(&mut self, msg: &Message) -> Option<MsgHandlerResult> { + let e = match msg.msg_type() { + MessageType::MethodReturn => Ok(msg), + MessageType::Error => Err(msg.set_error_from_msg().unwrap_err()), + _ => unreachable!(), + }; + debug_assert_eq!(msg.get_reply_serial(), Some(self.1)); + self.0.take().unwrap()(e); + return Some(MsgHandlerResult { handled: true, done: true, reply: Vec::new() }) + } +} + + +#[test] +fn message_reply() { + use std::{cell, rc}; + let c = Connection::get_private(BusType::Session).unwrap(); + assert!(c.is_connected()); + let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap(); + let quit = rc::Rc::new(cell::Cell::new(false)); + let quit2 = quit.clone(); + let reply = c.send_with_reply(m, move |result| { + let r = result.unwrap(); + let _: ::arg::Array<&str, _> = r.get1().unwrap(); + quit2.set(true); + }).unwrap(); + for _ in c.iter(1000).with(reply) { if quit.get() { return; } } + assert!(false); +} + diff --git a/third_party/rust/dbus/src/connection2.rs b/third_party/rust/dbus/src/connection2.rs new file mode 100644 index 0000000000..e318e921ee --- /dev/null +++ b/third_party/rust/dbus/src/connection2.rs @@ -0,0 +1,214 @@ +use crate::{BusType, Error, Message, to_c_str, Watch}; +use std::{ptr, str}; +use std::ffi::CStr; +use std::os::raw::{c_void}; + +#[derive(Debug)] +pub struct ConnHandle(*mut ffi::DBusConnection); + +unsafe impl Send for ConnHandle {} +unsafe impl Sync for ConnHandle {} + +impl Drop for ConnHandle { + fn drop(&mut self) { + unsafe { + ffi::dbus_connection_close(self.0); + ffi::dbus_connection_unref(self.0); + } + } +} + +/// Experimental rewrite of Connection [unstable / experimental] +/// +/// Slightly lower level, with better support for async operations. +/// Also, this struct is Send + Sync. +/// +/// Blocking operations should be clearly marked as such, although if you +/// try to access the connection from several threads at the same time, +/// blocking might occur due to an internal mutex inside the dbus library. +/// +/// This version avoids dbus_connection_dispatch, and thus avoids +/// callbacks from that function. Instead the same functionality needs to be +/// implemented by these bindings somehow - this is not done yet. +#[derive(Debug)] +pub struct TxRx { + handle: ConnHandle, +} + +impl TxRx { + #[inline(always)] + pub (crate) fn conn(&self) -> *mut ffi::DBusConnection { + self.handle.0 + } + + fn conn_from_ptr(ptr: *mut ffi::DBusConnection) -> Result<TxRx, Error> { + let handle = ConnHandle(ptr); + + /* No, we don't want our app to suddenly quit if dbus goes down */ + unsafe { ffi::dbus_connection_set_exit_on_disconnect(ptr, 0) }; + + let c = TxRx { handle }; + + Ok(c) + } + + + /// Creates a new D-Bus connection. + /// + /// Blocking: until the connection is up and running. + pub fn get_private(bus: BusType) -> Result<TxRx, Error> { + let mut e = Error::empty(); + let conn = unsafe { ffi::dbus_bus_get_private(bus, e.get_mut()) }; + if conn == ptr::null_mut() { + return Err(e) + } + Self::conn_from_ptr(conn) + } + + /// Creates a new D-Bus connection to a remote address. + /// + /// Note: for all common cases (System / Session bus) you probably want "get_private" instead. + /// + /// Blocking: until the connection is established. + pub fn open_private(address: &str) -> Result<TxRx, Error> { + let mut e = Error::empty(); + let conn = unsafe { ffi::dbus_connection_open_private(to_c_str(address).as_ptr(), e.get_mut()) }; + if conn == ptr::null_mut() { + return Err(e) + } + Self::conn_from_ptr(conn) + } + + /// Registers a new D-Bus connection with the bus. + /// + /// Note: `get_private` does this automatically, useful with `open_private` + /// + /// Blocking: until a "Hello" response is received from the server. + pub fn register(&mut self) -> Result<(), Error> { + // This function needs to take &mut self, because it changes unique_name and unique_name takes a &self + let mut e = Error::empty(); + if unsafe { ffi::dbus_bus_register(self.conn(), e.get_mut()) == 0 } { + Err(e) + } else { + Ok(()) + } + } + + /// Gets whether the connection is currently open. + pub fn is_connected(&self) -> bool { + unsafe { ffi::dbus_connection_get_is_connected(self.conn()) != 0 } + } + + /// Get the connection's unique name. + /// + /// It's usually something like ":1.54" + pub fn unique_name(&self) -> Option<&str> { + let c = unsafe { ffi::dbus_bus_get_unique_name(self.conn()) }; + if c == ptr::null_mut() { return None; } + let s = unsafe { CStr::from_ptr(c) }; + str::from_utf8(s.to_bytes()).ok() + } + + + /// Puts a message into libdbus out queue. Use "flush" or "read_write" to make sure it is sent over the wire. + /// + /// Returns a serial number than can be used to match against a reply. + pub fn send(&self, msg: Message) -> Result<u32, ()> { + let mut serial = 0u32; + let r = unsafe { ffi::dbus_connection_send(self.conn(), msg.ptr(), &mut serial) }; + if r == 0 { return Err(()); } + Ok(serial) + } + + /// Flush the queue of outgoing messages. + /// + /// Blocking: until the outgoing queue is empty. + pub fn flush(&self) { unsafe { ffi::dbus_connection_flush(self.conn()) } } + + /// Read and write to the connection. + /// + /// Incoming messages are put in the internal queue, outgoing messages are written. + /// + /// Blocking: If there are no messages, for up to timeout_ms milliseconds, or forever if timeout_ms is None. + /// For non-blocking behaviour, set timeout_ms to Some(0). + pub fn read_write(&self, timeout_ms: Option<i32>) -> Result<(), ()> { + let t = timeout_ms.unwrap_or(-1); + if unsafe { ffi::dbus_connection_read_write(self.conn(), t) == 0 } { + Err(()) + } else { + Ok(()) + } + } + + /// Removes a message from the incoming queue, or returns None if the queue is empty. + /// + /// Use "read_write" first, so that messages are put into the incoming queue. + /// For unhandled messages, please call MessageDispatcher::default_dispatch to return + /// default replies for method calls. + pub fn pop_message(&self) -> Option<Message> { + let mptr = unsafe { ffi::dbus_connection_pop_message(self.conn()) }; + if mptr == ptr::null_mut() { + None + } else { + Some(Message::from_ptr(mptr, false)) + } + } + + /// Get an up-to-date list of file descriptors to watch. + /// + /// Might be changed into something that allows for callbacks when the watch list is changed. + pub fn watch_fds(&mut self) -> Result<Vec<Watch>, ()> { + extern "C" fn add_watch_cb(watch: *mut ffi::DBusWatch, data: *mut c_void) -> u32 { + unsafe { + let wlist: &mut Vec<Watch> = &mut *(data as *mut _); + wlist.push(Watch::from_raw(watch)); + } + 1 + } + let mut r = vec!(); + if unsafe { ffi::dbus_connection_set_watch_functions(self.conn(), + Some(add_watch_cb), None, None, &mut r as *mut _ as *mut _, None) } == 0 { return Err(()) } + assert!(unsafe { ffi::dbus_connection_set_watch_functions(self.conn(), + None, None, None, ptr::null_mut(), None) } != 0); + Ok(r) + } +} + +#[test] +fn test_txrx_send_sync() { + fn is_send<T: Send>(_: &T) {} + fn is_sync<T: Sync>(_: &T) {} + let c = TxRx::get_private(BusType::Session).unwrap(); + is_send(&c); + is_sync(&c); +} + +#[test] +fn txrx_simple_test() { + let mut c = TxRx::get_private(BusType::Session).unwrap(); + assert!(c.is_connected()); + let fds = c.watch_fds().unwrap(); + println!("{:?}", fds); + assert!(fds.len() > 0); + let m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "ListNames").unwrap(); + let reply = c.send(m).unwrap(); + let my_name = c.unique_name().unwrap(); + loop { + while let Some(mut msg) = c.pop_message() { + println!("{:?}", msg); + if msg.get_reply_serial() == Some(reply) { + let r = msg.as_result().unwrap(); + let z: ::arg::Array<&str, _> = r.get1().unwrap(); + for n in z { + println!("{}", n); + if n == my_name { return; } // Hooray, we found ourselves! + } + assert!(false); + } else if let Some(r) = crate::MessageDispatcher::<()>::default_dispatch(&msg) { + c.send(r).unwrap(); + } + } + c.read_write(Some(100)).unwrap(); + } +} + diff --git a/third_party/rust/dbus/src/crossroads/crossroads.rs b/third_party/rust/dbus/src/crossroads/crossroads.rs new file mode 100644 index 0000000000..a856a6597c --- /dev/null +++ b/third_party/rust/dbus/src/crossroads/crossroads.rs @@ -0,0 +1,245 @@ +use std::collections::BTreeMap; +use std::any::{TypeId, Any}; +use std::ffi::{CString, CStr}; +use std::fmt; +use crate::{Path as PathName, Interface as IfaceName, Member as MemberName, Signature, Message, MessageType}; +use super::info::{IfaceInfo, MethodInfo, PropInfo, IfaceInfoBuilder}; +use super::handlers::{Handlers, Par, ParInfo, Mut, MutCtx, MutMethods}; +use super::stdimpl::DBusProperties; + +// The key is an IfaceName, but if we have that we bump into https://github.com/rust-lang/rust/issues/59732 +// so we use CString as a workaround. +#[derive(Default, Debug)] +struct IfaceReg<H: Handlers>(BTreeMap<CString, (TypeId, IfaceInfo<'static, H>)>); + +#[derive(Default)] +pub struct PathData<H: Handlers>(Vec<(TypeId, H::Iface)>); + +impl PathData<Par> { + pub fn insert_par<I: Any + 'static + Send + Sync>(&mut self, i: I) { + let id = TypeId::of::<I>(); + let t = Box::new(i); + self.0.push((id, t)); + } +} + +impl PathData<Mut> { + pub fn insert_mut<I: Any + 'static>(&mut self, i: I) { + let id = TypeId::of::<I>(); + let t = Box::new(i); + self.0.push((id, t)); + } +} + +impl<H: Handlers> fmt::Debug for PathData<H> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "...") } +} + +impl<H: Handlers> PathData<H> { + fn new() -> Self { PathData(vec!()) } +} + +#[derive(Debug)] +struct IfacePaths<H: Handlers>(BTreeMap<CString, PathData<H>>); + +impl<H: Handlers> Default for IfacePaths<H> { + fn default() -> Self { IfacePaths(BTreeMap::new()) } +} + +struct MsgHeaders<'a> { + m: MemberName<'a>, + i: IfaceName<'a>, + p: PathName<'a>, +} + +fn msg_headers(msg: &Message) -> Option<MsgHeaders> { + if msg.msg_type() != MessageType::MethodCall { return None }; + let p = msg.path()?; + let i = msg.interface()?; + let m = msg.member()?; + Some(MsgHeaders { m, i, p }) +} + +#[derive(Debug)] +pub (super) struct MLookup<'a, H: Handlers> { + pub (super) cr: &'a Crossroads<H>, + pub (super) data: &'a PathData<H>, + pub (super) iface: &'a H::Iface, + pub (super) iinfo: &'a IfaceInfo<'static, H>, +// pub (super) minfo: Option<&'a MethodInfo<'static, H>>, +// pub (super) pinfo: Option<&'a PropInfo<'static, H>>, +} + +#[derive(Debug)] +pub struct Crossroads<H: Handlers> { + reg: IfaceReg<H>, + paths: IfacePaths<H>, +} + +impl<H: Handlers> Crossroads<H> { + + pub fn register_custom<I: 'static>(&mut self, info: IfaceInfo<'static, H>) -> Option<IfaceInfo<'static, H>> { + self.reg.0.insert(info.name.clone().into_cstring(), (TypeId::of::<I>(), info)).map(|x| x.1) + } + pub fn insert<N: Into<PathName<'static>>>(&mut self, name: N, data: PathData<H>) { + self.paths.0.insert(name.into().into_cstring(), data); + } + pub fn get_data<N: Into<PathName<'static>>>(&self, name: N) -> Option<&PathData<H>> { + self.paths.0.get(name.into().as_cstr()) + } + + pub fn register<'a, I: 'static, N: Into<IfaceName<'static>>>(&'a mut self, name: N) -> IfaceInfoBuilder<'a, I, H> { + IfaceInfoBuilder::new(Some(self), name.into()) + } + + fn reg_lookup(&self, headers: &MsgHeaders) -> Option<(MLookup<H>, &MethodInfo<'static, H>)> { + let (typeid, iinfo) = self.reg.0.get(headers.i.as_cstr())?; + let minfo = iinfo.methods.iter().find(|x| x.name() == &headers.m)?; + let data = self.paths.0.get(headers.p.as_cstr())?; + let (_, iface) = data.0.iter().find(|x| x.0 == *typeid)?; + Some((MLookup { cr: self, data, iface, iinfo }, minfo)) + } + + pub (super) fn reg_prop_lookup<'a>(&'a self, data: &'a PathData<H>, iname: &CStr, propname: &CStr) -> + Option<(MLookup<'a, H>, &PropInfo<'static, H>)> { + let (typeid, iinfo) = self.reg.0.get(iname)?; + let pinfo = iinfo.props.iter().find(|x| x.name.as_cstr() == propname)?; + let (_, iface) = data.0.iter().find(|x| x.0 == *typeid)?; + Some((MLookup { cr: self, data, iface, iinfo}, pinfo)) + } +} + +impl Crossroads<Par> { + pub fn dispatch_par(&self, msg: &Message) -> Option<Vec<Message>> { + let headers = msg_headers(msg)?; + let (lookup, minfo) = self.reg_lookup(&headers)?; + let handler = minfo.handler(); + let iface = &**lookup.iface; + let mut info = ParInfo::new(msg, lookup); + let r = (handler)(iface, &mut info); + Some(r.into_iter().collect()) + } + + pub fn new_par() -> Self { + let mut cr = Crossroads { + reg: IfaceReg(BTreeMap::new()), + paths: IfacePaths(BTreeMap::new()), + }; + DBusProperties::register(&mut cr); + cr + } +} + +impl Crossroads<Mut> { + pub fn dispatch_mut(&mut self, msg: &Message) -> Option<Vec<Message>> { + let headers = msg_headers(msg)?; + let (typeid, iinfo) = self.reg.0.get_mut(headers.i.as_cstr())?; + let minfo = iinfo.methods.iter_mut().find(|x| x.name() == &headers.m)?; + let ctx = MutCtx::new(msg); + let r = match minfo.handler_mut().0 { + MutMethods::MutIface(ref mut f) => { + let data = self.paths.0.get_mut(headers.p.as_cstr())?; + let (_, iface) = data.0.iter_mut().find(|x| x.0 == *typeid)?; + let iface = &mut **iface; + f(iface, &ctx) + } + }; + Some(r.into_iter().collect()) + } + + pub fn new_mut() -> Self { + let cr = Crossroads { + reg: IfaceReg(BTreeMap::new()), + paths: IfacePaths(BTreeMap::new()), + }; + // DBusProperties::register(&mut cr); + cr + } +} + + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_send_sync() { + fn is_send<T: Send>(_: &T) {} + fn is_sync<T: Sync>(_: &T) {} + let c = Crossroads::new_par(); + dbg!(&c); + is_send(&c); + is_sync(&c); + } + + #[test] + fn cr_mut() { + let mut cr = Crossroads::new_mut(); + + struct Score(u16); + + let mut call_times = 0u32; + cr.register::<Score,_>("com.example.dbusrs.crossroads.score") + .method_iface("UpdateScore", ("change",), ("new_score", "call_times"), move |score, _, (change,): (u16,)| { + score.0 += change; + call_times += 1; + Ok((score.0, call_times)) + }); + + let mut pdata = PathData::new(); + pdata.insert_mut(Score(7u16)); + cr.insert("/", pdata); + + let msg = Message::new_method_call("com.example.dbusrs.crossroads.score", "/", "com.example.dbusrs.crossroads.score", "UpdateScore").unwrap(); + let mut msg = msg.append1(5u16); + crate::message::message_set_serial(&mut msg, 57); + let mut r = cr.dispatch_mut(&msg).unwrap(); + assert_eq!(r.len(), 1); + r[0].as_result().unwrap(); + let (new_score, call_times): (u16, u32) = r[0].read2().unwrap(); + assert_eq!(new_score, 12); + assert_eq!(call_times, 1); + } + + + #[test] + fn cr_par() { + let mut cr = Crossroads::new_par(); + + struct Score(u16); + + cr.register::<Score,_>("com.example.dbusrs.crossroads.score") + .method("Hello", ("sender",), ("reply",), |score, _, (sender,): (String,)| { + assert_eq!(score.0, 7u16); + Ok((format!("Hello {}, my score is {}!", sender, score.0),)) + }) + .prop_ro("Score", |score, _| { + assert_eq!(score.0, 7u16); + Ok(score.0) + }) + .signal::<(u16,),_>("ScoreChanged", ("NewScore",)); + + let mut pdata = PathData::new(); + pdata.insert_par(Score(7u16)); + pdata.insert_par(DBusProperties); + cr.insert("/", pdata); + + let msg = Message::new_method_call("com.example.dbusrs.crossroads.score", "/", "com.example.dbusrs.crossroads.score", "Hello").unwrap(); + let mut msg = msg.append1("example"); + crate::message::message_set_serial(&mut msg, 57); + let mut r = cr.dispatch_par(&msg).unwrap(); + assert_eq!(r.len(), 1); + r[0].as_result().unwrap(); + let rr: String = r[0].read1().unwrap(); + assert_eq!(&rr, "Hello example, my score is 7!"); + + let msg = Message::new_method_call("com.example.dbusrs.crossroads.score", "/", "org.freedesktop.DBus.Properties", "Get").unwrap(); + let mut msg = msg.append2("com.example.dbusrs.crossroads.score", "Score"); + crate::message::message_set_serial(&mut msg, 57); + let mut r = cr.dispatch_par(&msg).unwrap(); + assert_eq!(r.len(), 1); + r[0].as_result().unwrap(); + let z: u16 = r[0].read1().unwrap(); + assert_eq!(z, 7u16); + } +} diff --git a/third_party/rust/dbus/src/crossroads/handlers.rs b/third_party/rust/dbus/src/crossroads/handlers.rs new file mode 100644 index 0000000000..2d466ba119 --- /dev/null +++ b/third_party/rust/dbus/src/crossroads/handlers.rs @@ -0,0 +1,138 @@ +use std::{fmt, cell}; +use std::any::Any; +use crate::arg::ArgBuilder; +use crate::{Path as PathName, Interface as IfaceName, Member as MemberName, Signature, Message, arg}; +use super::crossroads::{Crossroads, PathData, MLookup}; +use super::info::{MethodInfo, PropInfo}; +use super::MethodErr; + +pub struct DebugMethod<H: Handlers>(pub H::Method); +impl<H: Handlers> fmt::Debug for DebugMethod<H> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "...") } +} + +pub struct DebugProp<H: Handlers>(pub Option<H::GetProp>, pub Option<H::SetProp>); +impl<H: Handlers> fmt::Debug for DebugProp<H> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "...") } +} + +pub trait Handlers { + type Method; + type GetProp; + type SetProp; + type Iface; +} + +/// Parallel tree - Par +#[derive(Debug, Clone, Copy, Default)] +pub struct Par; + +impl Par { + pub fn typed_getprop<I: 'static, T: arg::Arg + arg::Append, G>(getf: G) -> <Par as Handlers>::GetProp + where G: Fn(&I, &ParInfo) -> Result<T, MethodErr> + Send + Sync + 'static { + Box::new(move |data, ia, info| { + let iface: &I = data.downcast_ref().unwrap(); + let t = getf(iface, info)?; + ia.append(t); + Ok(()) + }) + } + + pub fn typed_setprop<I: 'static, T: arg::Arg + for <'z> arg::Get<'z>, S>(setf: S) -> <Par as Handlers>::SetProp + where S: Fn(&I, &ParInfo, T) -> Result<(), MethodErr> + Send + Sync + 'static { + Box::new(move |data, ii, info| { + let iface: &I = data.downcast_ref().unwrap(); + let t: T = ii.read()?; + setf(iface, info, t) + }) + } +} + +#[derive(Debug)] +pub struct ParInfo<'a> { + lookup: MLookup<'a, Par>, + message: &'a Message, +} + +impl<'a> ParInfo<'a> { + pub fn msg(&self) -> &Message { self.message } + pub (super) fn new(msg: &'a Message, lookup: MLookup<'a, Par>) -> Self { + ParInfo { lookup, message: msg } + } + pub fn path_data(&self) -> &PathData<Par> { self.lookup.data } + pub fn crossroads(&self) -> &Crossroads<Par> { self.lookup.cr } +} + +impl Handlers for Par { + type Method = Box<Fn(&(dyn Any + Send + Sync), &ParInfo) -> Option<Message> + Send + Sync + 'static>; + type GetProp = Box<Fn(&(dyn Any + Send + Sync), &mut arg::IterAppend, &ParInfo) -> Result<(), MethodErr> + Send + Sync + 'static>; + type SetProp = Box<Fn(&(dyn Any + Send + Sync), &mut arg::Iter, &ParInfo) -> Result<(), MethodErr> + Send + Sync + 'static>; + type Iface = Box<dyn Any + 'static + Send + Sync>; +} + +impl MethodInfo<'_, Par> { + pub fn new_par<N, F, T>(name: N, f: F) -> Self where + F: Fn(&T, &ParInfo) -> Result<Option<Message>, MethodErr> + Send + Sync + 'static, + N: Into<MemberName<'static>>, + T: Any + Send + Sync + 'static, + { + Self::new(name.into(), Box::new(move |data, info| { + let x = data.downcast_ref().unwrap(); + f(x, info).unwrap_or_else(|e| { Some(e.to_message(info.message)) }) + })) + } +} + + +/// Mutable, non-Send tree +#[derive(Debug, Clone, Copy, Default)] +pub struct Mut; + +#[derive(Debug)] +pub struct MutCtx<'a> { + message: &'a Message, + send_extra: cell::RefCell<Vec<Message>>, +} + +impl<'a> MutCtx<'a> { + pub fn msg(&self) -> &Message { self.message } + pub fn send(&self, msg: Message) { self.send_extra.borrow_mut().push(msg); } + pub (super) fn new(msg: &'a Message) -> Self { MutCtx { message: msg, send_extra: Default::default() } } +} + +impl Handlers for Mut { + type Method = MutMethod; + type GetProp = Box<FnMut(&mut (dyn Any), &mut arg::IterAppend, &MutCtx) -> Result<(), MethodErr> + 'static>; + type SetProp = Box<FnMut(&mut (dyn Any), &mut arg::Iter, &MutCtx) -> Result<(), MethodErr> + 'static>; + type Iface = Box<dyn Any>; +} + + +pub struct MutMethod(pub (super) MutMethods); + +pub (super) enum MutMethods { + MutIface(Box<FnMut(&mut (dyn Any), &MutCtx) -> Option<Message> + 'static>), +// Info(Box<FnMut(&(dyn Any), &Message, &Path) -> Option<Message> + 'static>), +// MutCr(fn(&mut Crossroads<Mut>, &Message) -> Vec<Message>), +} + +impl Mut { + pub fn typed_method_iface<IA: ArgBuilder, OA: ArgBuilder, I: 'static, F>(mut f: F) -> <Mut as Handlers>::Method + where F: FnMut(&mut I, &MutCtx, IA) -> Result<OA, MethodErr> + 'static { + MutMethod(MutMethods::MutIface(Box::new(move |data, info| { + let iface: &mut I = data.downcast_mut().unwrap(); + let ia = match IA::read(info.msg()) { + Err(e) => return Some(MethodErr::from(e).to_message(info.msg())), + Ok(ia) => ia, + }; + match f(iface, info, ia) { + Err(e) => Some(e.to_message(info.msg())), + Ok(r) => { + let mut m = info.msg().method_return(); + OA::append(r, &mut m); + Some(m) + }, + } + }))) + } +} diff --git a/third_party/rust/dbus/src/crossroads/info.rs b/third_party/rust/dbus/src/crossroads/info.rs new file mode 100644 index 0000000000..655b3193de --- /dev/null +++ b/third_party/rust/dbus/src/crossroads/info.rs @@ -0,0 +1,220 @@ +use crate::{Path as PathName, Interface as IfaceName, Member as MemberName, Signature, Message}; +use std::borrow::Cow; +use std::collections::BTreeMap; +use std::any::Any; +use std::mem; +use crate::arg::{Arg, Append, Get, ArgBuilder, TypeMismatchError, IterAppend}; +use std::marker::PhantomData; +use super::MethodErr; +use super::handlers::{Handlers, DebugMethod, DebugProp, Par, ParInfo, Mut, MutCtx}; +use super::crossroads::{Crossroads, PathData}; + +fn build_argvec<A: ArgBuilder>(a: A::strs) -> Vec<Argument<'static>> { + let mut v = vec!(); + A::strs_sig(a, |name, sig| { + v.push(Argument { name: name.into(), sig }) + }); + v +} + + +#[derive(Default, Debug, Clone)] +struct Annotations(Option<BTreeMap<String, String>>); + +#[derive(Debug, Clone)] +pub struct Argument<'a> { + name: Cow<'a, str>, + sig: Signature<'a>, +} + +#[derive(Debug)] +pub struct IfaceInfo<'a, H: Handlers> { + pub (crate) name: IfaceName<'a>, + pub (crate) methods: Vec<MethodInfo<'a, H>>, + pub (crate) props: Vec<PropInfo<'a, H>>, + pub (crate) signals: Vec<SignalInfo<'a>>, +} + +#[derive(Debug)] +pub struct MethodInfo<'a, H: Handlers> { + name: MemberName<'a>, + handler: DebugMethod<H>, + i_args: Vec<Argument<'a>>, + o_args: Vec<Argument<'a>>, + anns: Annotations, +} + +impl<'a, H: Handlers> MethodInfo<'a, H> { + pub fn name(&self) -> &MemberName<'a> { &self.name } + pub fn handler(&self) -> &H::Method { &self.handler.0 } + pub fn handler_mut(&mut self) -> &mut H::Method { &mut self.handler.0 } +} + +#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)] +/// 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, +} + +#[derive(Debug)] +pub struct PropInfo<'a, H: Handlers> { + pub (crate) name: MemberName<'a>, + pub (crate) handlers: DebugProp<H>, + anns: Annotations, + sig: Signature<'a>, + emits: EmitsChangedSignal, + auto_emit: bool, + rw: Access, +} + +#[derive(Debug)] +pub struct SignalInfo<'a> { + name: MemberName<'a>, + args: Vec<Argument<'a>>, + anns: Annotations, +} + +#[derive(Debug)] +pub struct IfaceInfoBuilder<'a, I: 'static, H: Handlers> { + cr: Option<&'a mut Crossroads<H>>, + info: IfaceInfo<'static, H>, + _dummy: PhantomData<*const I>, +} + +impl<'a, I, H: Handlers> IfaceInfoBuilder<'a, I, H> { + pub fn new(cr: Option<&'a mut Crossroads<H>>, name: IfaceName<'static>) -> Self { + IfaceInfoBuilder { cr, _dummy: PhantomData, info: IfaceInfo::new_empty(name) } + } + + pub fn signal<A: ArgBuilder, N: Into<MemberName<'static>>>(mut self, name: N, args: A::strs) -> Self { + let s = SignalInfo { name: name.into(), args: build_argvec::<A>(args), anns: Default::default() }; + self.info.signals.push(s); + self + } +} + +impl<'a, I: 'static, H: Handlers> Drop for IfaceInfoBuilder<'a, I, H> { + fn drop(&mut self) { + if let Some(ref mut cr) = self.cr { + let info = IfaceInfo::new_empty(self.info.name.clone()); // workaround for not being able to consume self.info + cr.register_custom::<I>(mem::replace(&mut self.info, info)); + } + } +} + +impl<'a, I: 'static> IfaceInfoBuilder<'a, I, Par> { + pub fn method<IA: ArgBuilder, OA: ArgBuilder, N, F>(mut self, name: N, in_args: IA::strs, out_args: OA::strs, f: F) -> Self + where N: Into<MemberName<'static>>, F: Fn(&I, &ParInfo, IA) -> Result<OA, MethodErr> + Send + Sync + 'static { + let f: <Par as Handlers>::Method = Box::new(move |data, info| { + let iface: &I = data.downcast_ref().unwrap(); + let r = IA::read(info.msg()).map_err(From::from); + let r = r.and_then(|ia| f(iface, info, ia)); + match r { + Err(e) => Some(e.to_message(info.msg())), + Ok(r) => { + let mut m = info.msg().method_return(); + OA::append(r, &mut m); + Some(m) + }, + } + }); + + let m = MethodInfo { name: name.into(), handler: DebugMethod(f), + i_args: build_argvec::<IA>(in_args), o_args: build_argvec::<OA>(out_args), anns: Default::default() }; + self.info.methods.push(m); + self + } + + pub fn prop_rw<T, N, G, S>(mut self, name: N, getf: G, setf: S) -> Self + where T: Arg + Append + for<'z> Get<'z> + Send + Sync + 'static, + N: Into<MemberName<'static>>, + G: Fn(&I, &ParInfo) -> Result<T, MethodErr> + Send + Sync + 'static, + S: Fn(&I, &ParInfo, T) -> Result<(), MethodErr> + Send + Sync + 'static + { + let p = PropInfo::new(name.into(), T::signature(), Some(Par::typed_getprop(getf)), Some(Par::typed_setprop(setf))); + self.info.props.push(p); + self + } + + pub fn prop_ro<T, N, G>(mut self, name: N, getf: G) -> Self + where T: Arg + Append + Send + Sync + 'static, + N: Into<MemberName<'static>>, + G: Fn(&I, &ParInfo) -> Result<T, MethodErr> + Send + Sync + 'static, + { + let p = PropInfo::new(name.into(), T::signature(), Some(Par::typed_getprop(getf)), None); + self.info.props.push(p); + self + } + +} + +impl<'a, I: 'static> IfaceInfoBuilder<'a, I, Mut> { + pub fn method_iface<IA: ArgBuilder, OA: ArgBuilder, N, F>(mut self, name: N, in_args: IA::strs, out_args: OA::strs, f: F) -> Self + where N: Into<MemberName<'static>>, F: FnMut(&mut I, &MutCtx, IA) -> Result<OA, MethodErr> + Send + Sync + 'static { + let m = MethodInfo { name: name.into(), handler: DebugMethod(Mut::typed_method_iface(f)), + i_args: build_argvec::<IA>(in_args), o_args: build_argvec::<OA>(out_args), anns: Default::default() }; + self.info.methods.push(m); + self + } +} + +impl<H: Handlers> MethodInfo<'_, H> { + pub fn new(name: MemberName<'static>, f: H::Method) -> Self { + MethodInfo { name: name, handler: DebugMethod(f), + i_args: Default::default(), o_args: Default::default(), anns: Default::default() } + } +} + +impl<H: Handlers> PropInfo<'_, H> { + pub fn new(name: MemberName<'static>, sig: Signature<'static>, get: Option<H::GetProp>, + set: Option<H::SetProp>) -> Self { + let a = match (&get, &set) { + (Some(_), Some(_)) => Access::ReadWrite, + (Some(_), None) => Access::Read, + (None, Some(_)) => Access::Write, + _ => unimplemented!(), + }; + PropInfo { name, handlers: DebugProp(get, set), sig, auto_emit: true, rw: a, + emits: EmitsChangedSignal::True, anns: Default::default() } + } +} + +impl<'a, H: Handlers> IfaceInfo<'a, H> { + pub fn new_empty(name: IfaceName<'static>) -> Self { + IfaceInfo { name, methods: vec!(), props: vec!(), signals: vec!() } + } + + pub fn new<N, M, P, S>(name: N, methods: M, properties: P, signals: S) -> Self where + N: Into<IfaceName<'a>>, + M: IntoIterator<Item=MethodInfo<'a, H>>, + P: IntoIterator<Item=PropInfo<'a, H>>, + S: IntoIterator<Item=SignalInfo<'a>> + { + IfaceInfo { + name: name.into(), + methods: methods.into_iter().collect(), + props: properties.into_iter().collect(), + signals: signals.into_iter().collect() + } + } +} + diff --git a/third_party/rust/dbus/src/crossroads/mod.rs b/third_party/rust/dbus/src/crossroads/mod.rs new file mode 100644 index 0000000000..7915157bc1 --- /dev/null +++ b/third_party/rust/dbus/src/crossroads/mod.rs @@ -0,0 +1,15 @@ +//! Will eventually superseed the "tree" module. It's unstable and experimental for now. +#![allow(unused_imports, dead_code, missing_docs)] + +mod info; +mod handlers; +mod crossroads; +mod stdimpl; + +pub use crate::tree::MethodErr as MethodErr; + +pub use self::info::{IfaceInfo, MethodInfo, PropInfo}; + +pub use self::crossroads::{Crossroads, PathData}; + +pub use self::handlers::{Handlers, Par, ParInfo}; diff --git a/third_party/rust/dbus/src/crossroads/stdimpl.rs b/third_party/rust/dbus/src/crossroads/stdimpl.rs new file mode 100644 index 0000000000..10729da5b1 --- /dev/null +++ b/third_party/rust/dbus/src/crossroads/stdimpl.rs @@ -0,0 +1,53 @@ +use super::crossroads::Crossroads; +use super::handlers::{ParInfo, Par}; +use super::info::{IfaceInfo, MethodInfo, PropInfo}; +use crate::arg; +use super::MethodErr; + +pub struct DBusProperties; + +impl DBusProperties { + pub fn register(cr: &mut Crossroads<Par>) { + cr.register_custom::<Self>(IfaceInfo::new("org.freedesktop.DBus.Properties", + vec!(MethodInfo::new_par("Get", |_: &DBusProperties, info| { + let (iname, propname) = info.msg().read2()?; + let (lookup, pinfo) = info.crossroads().reg_prop_lookup(info.path_data(), iname, propname) + .ok_or_else(|| { MethodErr::no_property(&"Could not find property") })?; + let handler = &pinfo.handlers.0.as_ref() + .ok_or_else(|| { MethodErr::no_property(&"Property can not be read") })?; + let iface = &**lookup.iface; + let mut pinfo = ParInfo::new(info.msg(), lookup); + let mut mret = info.msg().method_return(); + { + let mut ia = arg::IterAppend::new(&mut mret); + (handler)(iface, &mut ia, &mut pinfo)?; + } + Ok(Some(mret)) + })), + vec!(), vec!() + )); + + } +} + +pub struct DBusIntrospectable; + +use crate::crossroads as cr; + +pub trait Introspectable { + fn introspect(&self, info: &cr::ParInfo) -> Result<String, cr::MethodErr>; +} + +pub fn introspectable_ifaceinfo<I>() -> cr::IfaceInfo<'static, cr::Par> +where I: Introspectable + Send + Sync + 'static { + cr::IfaceInfo::new("org.freedesktop.DBus.Introspectable", vec!( + MethodInfo::new_par("Introspect", |intf: &I, info| { + let xml_data = intf.introspect(info)?; + let rm = info.msg().method_return(); + let rm = rm.append1(xml_data); + Ok(Some(rm)) + }), + ), vec!(), vec!()) +} + + diff --git a/third_party/rust/dbus/src/dispatcher.rs b/third_party/rust/dbus/src/dispatcher.rs new file mode 100644 index 0000000000..eda78ed586 --- /dev/null +++ b/third_party/rust/dbus/src/dispatcher.rs @@ -0,0 +1,129 @@ +use crate::{Message, MessageType, Error, to_c_str, c_str_to_slice}; +use std::ptr; + +use std::collections::HashMap; + +/// [Unstable and Experimental] +pub trait MessageDispatcherConfig: Sized { + /// The type of method reply stored inside the dispatcher + type Reply; + + /// Called when a method call has received a reply. + fn on_reply(reply: Self::Reply, msg: Message, dispatcher: &mut MessageDispatcher<Self>); + + /// Called when a signal is received. + /// + /// Defaults to doing nothing. + #[allow(unused_variables)] + fn on_signal(msg: Message, dispatcher: &mut MessageDispatcher<Self>) {} + + /// Called when a method call is received. + /// + /// Defaults to calling default_dispatch. + fn on_method_call(msg: Message, dispatcher: &mut MessageDispatcher<Self>) { + if let Some(reply) = MessageDispatcher::<Self>::default_dispatch(&msg) { + Self::on_send(reply, dispatcher); + } + } + + /// Called in the other direction, i e, when a message should be sent over the connection. + fn on_send(msg: Message, dispatcher: &mut MessageDispatcher<Self>); +} + +/// Dummy implementation +impl MessageDispatcherConfig for () { + type Reply = (); + fn on_reply(_: Self::Reply, _: Message, _: &mut MessageDispatcher<Self>) { unreachable!() } + fn on_send(_: Message, _: &mut MessageDispatcher<Self>) { unreachable!() } +} + +/// [Unstable and Experimental] Meant for usage with RxTx. +pub struct MessageDispatcher<C: MessageDispatcherConfig> { + waiting_replies: HashMap<u32, C::Reply>, + inner: C, +} + +impl<C: MessageDispatcherConfig> MessageDispatcher<C> { + + /// Creates a new message dispatcher. + pub fn new(inner: C) -> Self { MessageDispatcher { + waiting_replies: HashMap::new(), + inner: inner, + } } + + /// "Inner" accessor + pub fn inner(&self) -> &C { &self.inner } + + /// "Inner" mutable accessor + pub fn inner_mut(&mut self) -> &mut C { &mut self.inner } + + /// Adds a waiting reply to a method call. func will be called when a method reply is dispatched. + pub fn add_reply(&mut self, serial: u32, func: C::Reply) { + if let Some(_) = self.waiting_replies.insert(serial, func) { + // panic because we're overwriting something else, or just ignore? + } + } + + /// Cancels a waiting reply. + pub fn cancel_reply(&mut self, serial: u32) -> Option<C::Reply> { + self.waiting_replies.remove(&serial) + } + + + /// Dispatch an incoming message. + pub fn dispatch(&mut self, msg: Message) { + if let Some(serial) = msg.get_reply_serial() { + if let Some(sender) = self.waiting_replies.remove(&serial) { + C::on_reply(sender, msg, self); + return; + } + } + match msg.msg_type() { + MessageType::Signal => C::on_signal(msg, self), + MessageType::MethodCall => C::on_method_call(msg, self), + MessageType::Error | MessageType::MethodReturn => {}, + MessageType::Invalid => unreachable!(), + } + } + + /// Handles what we need to be a good D-Bus citizen. + /// + /// Call this if you have not handled the message yourself: + /// * It handles calls to org.freedesktop.DBus.Peer. + /// * For other method calls, it sends an error reply back that the method was unknown. + pub fn default_dispatch(m: &Message) -> Option<Message> { + Self::peer(&m) + .or_else(|| Self::unknown_method(&m)) + } + + /// Replies if this is a call to org.freedesktop.DBus.Peer, otherwise returns None. + pub fn peer(m: &Message) -> Option<Message> { + if let Some(intf) = m.interface() { + if &*intf != "org.freedesktop.DBus.Peer" { return None; } + if let Some(method) = m.member() { + if &*method == "Ping" { return Some(m.method_return()) } + if &*method == "GetMachineId" { + let mut r = m.method_return(); + let mut e = Error::empty(); + unsafe { + let id = ffi::dbus_try_get_local_machine_id(e.get_mut()); + if id != ptr::null_mut() { + r = r.append1(c_str_to_slice(&(id as *const _)).unwrap()); + ffi::dbus_free(id as *mut _); + return Some(r) + } + } + } + } + Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Method does not exist"))) + } else { None } + } + + /// For method calls, it replies that the method was unknown, otherwise returns None. + pub fn unknown_method(m: &Message) -> Option<Message> { + if m.msg_type() != MessageType::MethodCall { return None; } + // if m.get_no_reply() { return None; } // The reference implementation does not do this? + Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Path, Interface, or Method does not exist"))) + } +} + diff --git a/third_party/rust/dbus/src/lib.rs b/third_party/rust/dbus/src/lib.rs new file mode 100644 index 0000000000..6a56671490 --- /dev/null +++ b/third_party/rust/dbus/src/lib.rs @@ -0,0 +1,284 @@ +//! D-Bus bindings for Rust +//! +//! [D-Bus](http://dbus.freedesktop.org/) is a message bus, and is mainly used in Linux +//! for communication between processes. It is present by default on almost every +//! Linux distribution out there, and runs in two instances - one per session, and one +//! system-wide. +//! +//! In addition to the API documentation, which you're currently reading, you might want to +//! look in the examples directory, which contains many examples and an argument guide. +//! README.md also contain a few quick "getting started" examples. +//! +//! In addition to this crate, there are two companion crates, dbus-codegen for generating Rust +//! code from D-Bus introspection data, and dbus-tokio for integrating D-Bus with [Tokio](http://tokio.rs). +//! However, at the time of this writing, these are far less mature than this crate. + +#![warn(missing_docs)] + +extern crate libc; + +pub use ffi::DBusBusType as BusType; +pub use connection::DBusNameFlag as NameFlag; +pub use ffi::DBusRequestNameReply as RequestNameReply; +pub use ffi::DBusReleaseNameReply as ReleaseNameReply; +pub use ffi::DBusMessageType as MessageType; + +pub use message::{Message, MessageItem, MessageItemArray, FromMessageItem, OwnedFd, ArrayError, ConnPath}; +pub use connection::{Connection, ConnectionItems, ConnectionItem, ConnMsgs, MsgHandler, MsgHandlerResult, MsgHandlerType, MessageCallback}; +pub use prop::PropHandler; +pub use prop::Props; +pub use watch::{Watch, WatchEvent}; +pub use signalargs::SignalArgs; + +/// A TypeSig describes the type of a MessageItem. +#[deprecated(note="Use Signature instead")] +pub type TypeSig<'a> = std::borrow::Cow<'a, str>; + +use std::ffi::{CString, CStr}; +use std::ptr; +use std::os::raw::c_char; + +#[allow(missing_docs)] +extern crate libdbus_sys as ffi; +mod message; +mod prop; +mod watch; +mod connection; +mod signalargs; + +mod matchrule; +pub use matchrule::MatchRule; + +mod strings; +pub use strings::{Signature, Path, Interface, Member, ErrorName, BusName}; + +pub mod arg; + +pub mod stdintf; + +pub mod tree; + +static INITDBUS: std::sync::Once = std::sync::ONCE_INIT; + +fn init_dbus() { + INITDBUS.call_once(|| { + if unsafe { ffi::dbus_threads_init_default() } == 0 { + panic!("Out of memory when trying to initialize D-Bus library!"); + } + }); +} + +/// D-Bus Error wrapper. +pub struct Error { + e: ffi::DBusError, +} + +unsafe impl Send for Error {} + +// Note! For this Sync impl to be safe, it requires that no functions that take &self, +// actually calls into FFI. All functions that call into FFI with a ffi::DBusError +// must take &mut self. + +unsafe impl Sync for Error {} + +fn c_str_to_slice(c: & *const c_char) -> Option<&str> { + if *c == ptr::null() { None } + else { std::str::from_utf8( unsafe { CStr::from_ptr(*c).to_bytes() }).ok() } +} + +fn to_c_str(n: &str) -> CString { CString::new(n.as_bytes()).unwrap() } + +impl Error { + + /// Create a new custom D-Bus Error. + pub fn new_custom(name: &str, message: &str) -> Error { + let n = to_c_str(name); + let m = to_c_str(&message.replace("%","%%")); + let mut e = Error::empty(); + + unsafe { ffi::dbus_set_error(e.get_mut(), n.as_ptr(), m.as_ptr()) }; + e + } + + fn empty() -> Error { + init_dbus(); + let mut e = ffi::DBusError { + name: ptr::null(), + message: ptr::null(), + dummy: 0, + padding1: ptr::null() + }; + unsafe { ffi::dbus_error_init(&mut e); } + Error{ e: e } + } + + /// Error name/type, e g 'org.freedesktop.DBus.Error.Failed' + pub fn name(&self) -> Option<&str> { + c_str_to_slice(&self.e.name) + } + + /// Custom message, e g 'Could not find a matching object path' + pub fn message(&self) -> Option<&str> { + c_str_to_slice(&self.e.message) + } + + fn get_mut(&mut self) -> &mut ffi::DBusError { &mut self.e } +} + +impl Drop for Error { + fn drop(&mut self) { + unsafe { ffi::dbus_error_free(&mut self.e); } + } +} + +impl std::fmt::Debug for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!(f, "D-Bus error: {} ({})", self.message().unwrap_or(""), + self.name().unwrap_or("")) + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { "D-Bus error" } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(),std::fmt::Error> { + if let Some(x) = self.message() { + write!(f, "{:?}", x.to_string()) + } else { Ok(()) } + } +} + +impl From<arg::TypeMismatchError> for Error { + fn from(t: arg::TypeMismatchError) -> Error { + Error::new_custom("org.freedesktop.DBus.Error.Failed", &format!("{}", t)) + } +} + +impl From<tree::MethodErr> for Error { + fn from(t: tree::MethodErr) -> Error { + Error::new_custom(t.errorname(), t.description()) + } +} + +#[cfg(test)] +mod test { + use super::{Connection, Message, BusType, MessageItem, ConnectionItem, NameFlag, + RequestNameReply, ReleaseNameReply}; + + #[test] + fn connection() { + let c = Connection::get_private(BusType::Session).unwrap(); + let n = c.unique_name(); + assert!(n.starts_with(":1.")); + println!("Connected to DBus, unique name: {}", n); + } + + #[test] + fn invalid_message() { + let c = Connection::get_private(BusType::Session).unwrap(); + let m = Message::new_method_call("foo.bar", "/", "foo.bar", "FooBar").unwrap(); + let e = c.send_with_reply_and_block(m, 2000).err().unwrap(); + assert!(e.name().unwrap() == "org.freedesktop.DBus.Error.ServiceUnknown"); + } + + #[test] + fn message_listnames() { + let c = Connection::get_private(BusType::Session).unwrap(); + let m = Message::method_call(&"org.freedesktop.DBus".into(), &"/".into(), + &"org.freedesktop.DBus".into(), &"ListNames".into()); + let r = c.send_with_reply_and_block(m, 2000).unwrap(); + let reply = r.get_items(); + println!("{:?}", reply); + } + + #[test] + fn message_namehasowner() { + let c = Connection::get_private(BusType::Session).unwrap(); + let mut m = Message::new_method_call("org.freedesktop.DBus", "/", "org.freedesktop.DBus", "NameHasOwner").unwrap(); + m.append_items(&[MessageItem::Str("org.freedesktop.DBus".to_string())]); + let r = c.send_with_reply_and_block(m, 2000).unwrap(); + let reply = r.get_items(); + println!("{:?}", reply); + assert_eq!(reply, vec!(MessageItem::Bool(true))); + } + + #[test] + fn object_path() { + use std::sync::mpsc; + let (tx, rx) = mpsc::channel(); + let thread = ::std::thread::spawn(move || { + let c = Connection::get_private(BusType::Session).unwrap(); + c.register_object_path("/hello").unwrap(); + // println!("Waiting..."); + tx.send(c.unique_name()).unwrap(); + for n in c.iter(1000) { + // println!("Found message... ({})", n); + match n { + ConnectionItem::MethodCall(ref m) => { + let reply = Message::new_method_return(m).unwrap(); + c.send(reply).unwrap(); + break; + } + _ => {} + } + } + c.unregister_object_path("/hello"); + }); + + let c = Connection::get_private(BusType::Session).unwrap(); + let n = rx.recv().unwrap(); + let m = Message::new_method_call(&n, "/hello", "com.example.hello", "Hello").unwrap(); + println!("Sending..."); + let r = c.send_with_reply_and_block(m, 8000).unwrap(); + let reply = r.get_items(); + println!("{:?}", reply); + thread.join().unwrap(); + + } + + #[test] + fn register_name() { + let c = Connection::get_private(BusType::Session).unwrap(); + let n = format!("com.example.hello.test.register_name"); + assert_eq!(c.register_name(&n, NameFlag::ReplaceExisting as u32).unwrap(), RequestNameReply::PrimaryOwner); + assert_eq!(c.release_name(&n).unwrap(), ReleaseNameReply::Released); + } + + #[test] + fn signal() { + let c = Connection::get_private(BusType::Session).unwrap(); + let iface = "com.example.signaltest"; + let mstr = format!("interface='{}',member='ThisIsASignal'", iface); + c.add_match(&mstr).unwrap(); + let m = Message::new_signal("/mysignal", iface, "ThisIsASignal").unwrap(); + let uname = c.unique_name(); + c.send(m).unwrap(); + for n in c.iter(1000) { + match n { + ConnectionItem::Signal(s) => { + let (_, p, i, m) = s.headers(); + match (&*p.unwrap(), &*i.unwrap(), &*m.unwrap()) { + ("/mysignal", "com.example.signaltest", "ThisIsASignal") => { + assert_eq!(&*s.sender().unwrap(), &*uname); + break; + }, + (_, _, _) => println!("Other signal: {:?}", s.headers()), + } + } + _ => {}, + } + } + c.remove_match(&mstr).unwrap(); + } + + + #[test] + fn watch() { + let c = Connection::get_private(BusType::Session).unwrap(); + let d = c.watch_fds(); + assert!(d.len() > 0); + println!("Fds to watch: {:?}", d); + } +} diff --git a/third_party/rust/dbus/src/matchrule.rs b/third_party/rust/dbus/src/matchrule.rs new file mode 100644 index 0000000000..fb42abe51b --- /dev/null +++ b/third_party/rust/dbus/src/matchrule.rs @@ -0,0 +1,77 @@ +use crate::{Message, MessageType, BusName, Path, Interface, Member}; + + +#[derive(Clone, Debug, Default)] +/// A "match rule", that can match Messages on its headers. +/// +/// A field set to "None" means no filter for that header, +/// a field set to "Some(_)" must match exactly. +pub struct MatchRule<'a> { + /// Match on message type (you typically want to do this) + pub msg_type: Option<MessageType>, + /// Match on message sender + pub sender: Option<BusName<'a>>, + /// Match on message object path + pub path: Option<Path<'a>>, + /// Match on message interface + pub interface: Option<Interface<'a>>, + /// Match on message member (signal or method name) + pub member: Option<Member<'a>>, + _more_fields_may_come: (), +} + +fn msg_type_str(m: MessageType) -> &'static str { + use MessageType::*; + match m { + Signal => "signal", + MethodCall => "method_call", + MethodReturn => "method_return", + Error => "error", + Invalid => unreachable!(), + } +} + + +impl<'a> MatchRule<'a> { + /// Make a string which you can use in the call to "add_match". + /// + /// Panics: if msg_type is set to Some(MessageType::Invalid) + pub fn match_str(&self) -> String { + let mut v = vec!(); + if let Some(x) = self.msg_type { v.push(("type", msg_type_str(x))) }; + if let Some(ref x) = self.sender { v.push(("sender", &x)) }; + if let Some(ref x) = self.path { v.push(("path", &x)) }; + if let Some(ref x) = self.interface { v.push(("interface", &x)) }; + if let Some(ref x) = self.member { v.push(("member", &x)) }; + + // For now we don't need to worry about internal quotes in strings as those are not valid names. + // If we start matching against arguments, we need to worry. + let v: Vec<_> = v.into_iter().map(|(k, v)| format!("{}='{}'", k, v)).collect(); + v.join(",") + } + + /// Returns whether or not the message matches the rule. + pub fn matches(&self, msg: &Message) -> bool { + if let Some(x) = self.msg_type { if x != msg.msg_type() { return false; }}; + if self.sender.is_some() && msg.sender() != self.sender { return false }; + if self.path.is_some() && msg.path() != self.path { return false }; + if self.interface.is_some() && msg.interface() != self.interface { return false }; + if self.member.is_some() && msg.member() != self.member { return false }; + true + } + + /// Create a new struct which matches every message. + pub fn new() -> Self { Default::default() } + + /// Returns a clone with no static references + pub fn into_static(&self) -> MatchRule<'static> { + MatchRule { + msg_type: self.msg_type, + sender: self.sender.as_ref().map(|x| x.clone().into_static()), + path: self.path.as_ref().map(|x| x.clone().into_static()), + interface: self.interface.as_ref().map(|x| x.clone().into_static()), + member: self.member.as_ref().map(|x| x.clone().into_static()), + _more_fields_may_come: (), + } + } +} diff --git a/third_party/rust/dbus/src/message.rs b/third_party/rust/dbus/src/message.rs new file mode 100644 index 0000000000..7a77dcd9a0 --- /dev/null +++ b/third_party/rust/dbus/src/message.rs @@ -0,0 +1,1152 @@ +use std::borrow::Cow; +use std::{fmt, mem, ptr, ops}; +use super::{ffi, Error, MessageType, Signature, libc, to_c_str, c_str_to_slice, init_dbus}; +use super::{BusName, Path, Interface, Member, ErrorName, Connection, SignalArgs}; +use std::os::unix::io::{RawFd, AsRawFd}; +use std::ffi::CStr; +use std::os::raw::{c_void, c_char, c_int}; + +use super::arg::{Append, IterAppend, Get, Iter, Arg, RefArg, TypeMismatchError}; + +#[derive(Debug,Copy,Clone)] +/// Errors that can happen when creating a MessageItem::Array. +pub enum ArrayError { + /// The array is empty. + EmptyArray, + /// The array is composed of different element types. + DifferentElementTypes, + /// The supplied signature is not a valid array signature + InvalidSignature, +} + +fn new_dbus_message_iter() -> ffi::DBusMessageIter { unsafe { mem::zeroed() }} + + +/// An RAII wrapper around Fd to ensure that file descriptor is closed +/// when the scope ends. +#[derive(Debug, PartialEq, PartialOrd)] +pub struct OwnedFd { + fd: RawFd +} + +impl OwnedFd { + /// Create a new OwnedFd from a RawFd. + pub fn new(fd: RawFd) -> OwnedFd { + OwnedFd { fd: fd } + } + + /// Convert an OwnedFD back into a RawFd. + pub fn into_fd(self) -> RawFd { + let s = self.fd; + ::std::mem::forget(self); + s + } +} + +impl Drop for OwnedFd { + fn drop(&mut self) { + unsafe { libc::close(self.fd); } + } +} + +impl Clone for OwnedFd { + fn clone(&self) -> OwnedFd { + OwnedFd::new(unsafe { libc::dup(self.fd) } ) // FIXME: handle errors + } +} + +impl AsRawFd for OwnedFd { + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +/// An array of MessageItem where every MessageItem is of the same type. +pub struct MessageItemArray { + v: Vec<MessageItem>, + // signature includes the "a"! + sig: Signature<'static>, +} + +impl MessageItemArray { + /// Creates a new array where every element has the supplied signature. + /// + /// Signature is the full array signature, not the signature of the element. + pub fn new(v: Vec<MessageItem>, sig: Signature<'static>) -> Result<MessageItemArray, ArrayError> { + let a = MessageItemArray {v: v, sig: sig }; + if a.sig.as_bytes()[0] != ffi::DBUS_TYPE_ARRAY as u8 { return Err(ArrayError::InvalidSignature) } + { + let esig = a.element_signature(); + for i in &a.v { + let b = if let MessageItem::DictEntry(ref k, ref v) = *i { + let s = format!("{{{}{}}}", k.signature(), v.signature()); + s.as_bytes() == esig.to_bytes() + } else { + i.signature().as_cstr() == esig + }; + if !b { return Err(ArrayError::DifferentElementTypes) } + } + } + Ok(a) + } + + fn element_signature(&self) -> &CStr { + let z = &self.sig.as_cstr().to_bytes_with_nul()[1..]; + unsafe { CStr::from_bytes_with_nul_unchecked(z) } + } + + fn make_sig(m: &MessageItem) -> Signature<'static> { + if let MessageItem::DictEntry(ref k, ref v) = *m { + Signature::new(format!("a{{{}{}}}", k.signature(), v.signature())).unwrap() + } else { + Signature::new(format!("a{}", m.signature())).unwrap() + } + } + + /// Signature of array (full array signature) + pub fn signature(&self) -> &Signature<'static> { &self.sig } + + /// Consumes the MessageItemArray in order to allow you to modify the individual items of the array. + pub fn into_vec(self) -> Vec<MessageItem> { self.v } +} + +impl ops::Deref for MessageItemArray { + type Target = [MessageItem]; + fn deref(&self) -> &Self::Target { &self.v } +} + + +/// MessageItem - used as parameters and return values from +/// method calls, or as data added to a signal (old, enum version). +/// +/// Note that the newer generic design (see `arg` module) is both faster +/// and less error prone than MessageItem, and should be your first hand choice +/// whenever applicable. +#[derive(Debug, PartialEq, PartialOrd, Clone)] +pub enum MessageItem { + /// A D-Bus array requires all elements to be of the same type. + /// All elements must match the Signature. + Array(MessageItemArray), + /// A D-Bus struct allows for values of different types. + Struct(Vec<MessageItem>), + /// A D-Bus variant is a wrapper around another `MessageItem`, which + /// can be of any type. + Variant(Box<MessageItem>), + /// A D-Bus dictionary entry. These are only allowed inside an array. + DictEntry(Box<MessageItem>, Box<MessageItem>), + /// A D-Bus objectpath requires its content to be a valid objectpath, + /// so this cannot be any string. + ObjectPath(Path<'static>), + /// A D-Bus String is zero terminated, so no \0 s in the String, please. + /// (D-Bus strings are also - like Rust strings - required to be valid UTF-8.) + Str(String), + /// A D-Bus boolean type. + Bool(bool), + /// A D-Bus unsigned 8 bit type. + Byte(u8), + /// A D-Bus signed 16 bit type. + Int16(i16), + /// A D-Bus signed 32 bit type. + Int32(i32), + /// A D-Bus signed 64 bit type. + Int64(i64), + /// A D-Bus unsigned 16 bit type. + UInt16(u16), + /// A D-Bus unsigned 32 bit type. + UInt32(u32), + /// A D-Bus unsigned 64 bit type. + UInt64(u64), + /// A D-Bus IEEE-754 double-precision floating point type. + Double(f64), + /// D-Bus allows for sending file descriptors, which can be used to + /// set up SHM, unix pipes, or other communication channels. + UnixFd(OwnedFd), +} + +fn iter_get_basic<T>(i: &mut ffi::DBusMessageIter) -> T { + unsafe { + let mut c: T = mem::zeroed(); + let p = &mut c as *mut _ as *mut c_void; + ffi::dbus_message_iter_get_basic(i, p); + c + } +} + +fn iter_append_array(i: &mut ffi::DBusMessageIter, a: &[MessageItem], t: &CStr) { + let mut subiter = new_dbus_message_iter(); + + assert!(unsafe { ffi::dbus_message_iter_open_container(i, ffi::DBUS_TYPE_ARRAY, t.as_ptr(), &mut subiter) } != 0); + for item in a.iter() { +// assert!(item.type_sig() == t); + item.iter_append(&mut subiter); + } + assert!(unsafe { ffi::dbus_message_iter_close_container(i, &mut subiter) } != 0); +} + +fn iter_append_struct(i: &mut ffi::DBusMessageIter, a: &[MessageItem]) { + let mut subiter = new_dbus_message_iter(); + let res = unsafe { ffi::dbus_message_iter_open_container(i, ffi::DBUS_TYPE_STRUCT, ptr::null(), &mut subiter) }; + assert!(res != 0); + for item in a.iter() { + item.iter_append(&mut subiter); + } + let res2 = unsafe { ffi::dbus_message_iter_close_container(i, &mut subiter) }; + assert!(res2 != 0); +} + +fn iter_append_variant(i: &mut ffi::DBusMessageIter, a: &MessageItem) { + let mut subiter = new_dbus_message_iter(); + let asig = a.signature(); + let atype = asig.as_cstr(); + assert!(unsafe { ffi::dbus_message_iter_open_container(i, ffi::DBUS_TYPE_VARIANT, atype.as_ptr(), &mut subiter) } != 0); + a.iter_append(&mut subiter); + assert!(unsafe { ffi::dbus_message_iter_close_container(i, &mut subiter) } != 0); +} + +fn iter_append_dict(i: &mut ffi::DBusMessageIter, k: &MessageItem, v: &MessageItem) { + let mut subiter = new_dbus_message_iter(); + assert!(unsafe { ffi::dbus_message_iter_open_container(i, ffi::DBUS_TYPE_DICT_ENTRY, ptr::null(), &mut subiter) } != 0); + k.iter_append(&mut subiter); + v.iter_append(&mut subiter); + assert!(unsafe { ffi::dbus_message_iter_close_container(i, &mut subiter) } != 0); +} + +impl MessageItem { + /// Get the D-Bus Signature for this MessageItem. + /// + /// Note: Since dictionary entries have no valid signature, calling this function for a dict entry will cause a panic. + pub fn signature(&self) -> Signature<'static> { + use arg::Variant; + match *self { + MessageItem::Str(_) => <String as Arg>::signature(), + MessageItem::Bool(_) => <bool as Arg>::signature(), + MessageItem::Byte(_) => <u8 as Arg>::signature(), + MessageItem::Int16(_) => <i16 as Arg>::signature(), + MessageItem::Int32(_) => <i32 as Arg>::signature(), + MessageItem::Int64(_) => <i64 as Arg>::signature(), + MessageItem::UInt16(_) => <u16 as Arg>::signature(), + MessageItem::UInt32(_) => <u32 as Arg>::signature(), + MessageItem::UInt64(_) => <u64 as Arg>::signature(), + MessageItem::Double(_) => <f64 as Arg>::signature(), + MessageItem::Array(ref a) => a.sig.clone(), + MessageItem::Struct(ref s) => Signature::new(format!("({})", s.iter().fold(String::new(), |s, i| s + &*i.signature()))).unwrap(), + MessageItem::Variant(_) => <Variant<u8> as Arg>::signature(), + MessageItem::DictEntry(_, _) => { panic!("Dict entries are only valid inside arrays, and therefore has no signature on their own") }, + MessageItem::ObjectPath(_) => <Path as Arg>::signature(), + MessageItem::UnixFd(_) => <OwnedFd as Arg>::signature(), + } + } + + /// Get the D-Bus ASCII type-code for this MessageItem. + #[deprecated(note="superseded by signature")] + #[allow(deprecated)] + pub fn type_sig(&self) -> super::TypeSig<'static> { + Cow::Owned(format!("{}", self.signature())) + } + + /// Get the integer value for this MessageItem's type-code. + pub fn array_type(&self) -> i32 { + let s = match self { + &MessageItem::Str(_) => ffi::DBUS_TYPE_STRING, + &MessageItem::Bool(_) => ffi::DBUS_TYPE_BOOLEAN, + &MessageItem::Byte(_) => ffi::DBUS_TYPE_BYTE, + &MessageItem::Int16(_) => ffi::DBUS_TYPE_INT16, + &MessageItem::Int32(_) => ffi::DBUS_TYPE_INT32, + &MessageItem::Int64(_) => ffi::DBUS_TYPE_INT64, + &MessageItem::UInt16(_) => ffi::DBUS_TYPE_UINT16, + &MessageItem::UInt32(_) => ffi::DBUS_TYPE_UINT32, + &MessageItem::UInt64(_) => ffi::DBUS_TYPE_UINT64, + &MessageItem::Double(_) => ffi::DBUS_TYPE_DOUBLE, + &MessageItem::Array(_) => ffi::DBUS_TYPE_ARRAY, + &MessageItem::Struct(_) => ffi::DBUS_TYPE_STRUCT, + &MessageItem::Variant(_) => ffi::DBUS_TYPE_VARIANT, + &MessageItem::DictEntry(_,_) => ffi::DBUS_TYPE_DICT_ENTRY, + &MessageItem::ObjectPath(_) => ffi::DBUS_TYPE_OBJECT_PATH, + &MessageItem::UnixFd(_) => ffi::DBUS_TYPE_UNIX_FD, + }; + s as i32 + } + + /// Creates a (String, Variant) dictionary from an iterator with Result passthrough (an Err will abort and return that Err) + pub fn from_dict<E, I: Iterator<Item=Result<(String, MessageItem),E>>>(i: I) -> Result<MessageItem, E> { + let mut v = Vec::new(); + for r in i { + let (s, vv) = try!(r); + v.push((s.into(), Box::new(vv).into()).into()); + } + Ok(MessageItem::Array(MessageItemArray::new(v, Signature::new("a{sv}").unwrap()).unwrap())) + } + + /// Creates an MessageItem::Array from a list of MessageItems. + /// + /// Note: This requires `v` to be non-empty. See also + /// `MessageItem::from(&[T])`, which can handle empty arrays as well. + pub fn new_array(v: Vec<MessageItem>) -> Result<MessageItem,ArrayError> { + if v.len() == 0 { + return Err(ArrayError::EmptyArray); + } + let s = MessageItemArray::make_sig(&v[0]); + Ok(MessageItem::Array(MessageItemArray::new(v, s)?)) + } + + + fn new_array2<D, I>(i: I) -> MessageItem + where D: Into<MessageItem>, D: Default, I: Iterator<Item=D> { + let v: Vec<MessageItem> = i.map(|ii| ii.into()).collect(); + let s = { + let d; + let t = if v.len() == 0 { d = D::default().into(); &d } else { &v[0] }; + MessageItemArray::make_sig(t) + }; + MessageItem::Array(MessageItemArray::new(v, s).unwrap()) + } + + fn new_array3<'b, D: 'b, I>(i: I) -> MessageItem + where D: Into<MessageItem> + Default + Clone, I: Iterator<Item=&'b D> { + MessageItem::new_array2(i.map(|ii| ii.clone())) + } + + fn from_iter_single(i: &mut ffi::DBusMessageIter) -> Option<MessageItem> { + let t = unsafe { ffi::dbus_message_iter_get_arg_type(i) }; + match t { + ffi::DBUS_TYPE_INVALID => { None }, + ffi::DBUS_TYPE_DICT_ENTRY => { + let mut subiter = new_dbus_message_iter(); + unsafe { ffi::dbus_message_iter_recurse(i, &mut subiter) }; + let a = MessageItem::from_iter(&mut subiter); + if a.len() != 2 { panic!("D-Bus dict entry error"); } + let mut a = a.into_iter(); + let key = Box::new(a.next().unwrap()); + let value = Box::new(a.next().unwrap()); + Some(MessageItem::DictEntry(key, value)) + } + ffi::DBUS_TYPE_VARIANT => { + let mut subiter = new_dbus_message_iter(); + unsafe { ffi::dbus_message_iter_recurse(i, &mut subiter) }; + let a = MessageItem::from_iter(&mut subiter); + if a.len() != 1 { panic!("D-Bus variant error"); } + Some(MessageItem::Variant(Box::new(a.into_iter().next().unwrap()))) + } + ffi::DBUS_TYPE_ARRAY => { + let mut subiter = new_dbus_message_iter(); + unsafe { ffi::dbus_message_iter_recurse(i, &mut subiter) }; + let c = unsafe { ffi::dbus_message_iter_get_signature(&mut subiter) }; + let s = format!("a{}", c_str_to_slice(&(c as *const c_char)).unwrap()); + unsafe { ffi::dbus_free(c as *mut c_void) }; + let t = Signature::new(s).unwrap(); + + let a = MessageItem::from_iter(&mut subiter); + Some(MessageItem::Array(MessageItemArray { v: a, sig: t })) + }, + ffi::DBUS_TYPE_STRUCT => { + let mut subiter = new_dbus_message_iter(); + unsafe { ffi::dbus_message_iter_recurse(i, &mut subiter) }; + Some(MessageItem::Struct(MessageItem::from_iter(&mut subiter))) + }, + ffi::DBUS_TYPE_STRING => { + let mut c: *const c_char = ptr::null(); + unsafe { + let p: *mut c_void = mem::transmute(&mut c); + ffi::dbus_message_iter_get_basic(i, p); + }; + Some(MessageItem::Str(c_str_to_slice(&c).expect("D-Bus string error").to_string())) + }, + ffi::DBUS_TYPE_OBJECT_PATH => { + let mut c: *const c_char = ptr::null(); + unsafe { + let p: *mut c_void = mem::transmute(&mut c); + ffi::dbus_message_iter_get_basic(i, p); + }; + let o = Path::new(c_str_to_slice(&c).expect("D-Bus object path error")).ok().expect("D-Bus object path error"); + Some(MessageItem::ObjectPath(o)) + }, + ffi::DBUS_TYPE_UNIX_FD => Some(MessageItem::UnixFd(OwnedFd::new(iter_get_basic(i)))), + ffi::DBUS_TYPE_BOOLEAN => Some(MessageItem::Bool(iter_get_basic::<u32>(i) != 0)), + ffi::DBUS_TYPE_BYTE => Some(MessageItem::Byte(iter_get_basic(i))), + ffi::DBUS_TYPE_INT16 => Some(MessageItem::Int16(iter_get_basic(i))), + ffi::DBUS_TYPE_INT32 => Some(MessageItem::Int32(iter_get_basic(i))), + ffi::DBUS_TYPE_INT64 => Some(MessageItem::Int64(iter_get_basic(i))), + ffi::DBUS_TYPE_UINT16 => Some(MessageItem::UInt16(iter_get_basic(i))), + ffi::DBUS_TYPE_UINT32 => Some(MessageItem::UInt32(iter_get_basic(i))), + ffi::DBUS_TYPE_UINT64 => Some(MessageItem::UInt64(iter_get_basic(i))), + ffi::DBUS_TYPE_DOUBLE => Some(MessageItem::Double(iter_get_basic(i))), + _ => { None /* Only the new msgarg module supports signatures */ } + } + } + + fn from_iter(i: &mut ffi::DBusMessageIter) -> Vec<MessageItem> { + let mut v = Vec::new(); + while let Some(m) = Self::from_iter_single(i) { + v.push(m); + unsafe { ffi::dbus_message_iter_next(i) }; + } + v + } + + fn iter_append_basic<T>(&self, i: &mut ffi::DBusMessageIter, v: T) { + let t = self.array_type() as c_int; + let p = &v as *const _ as *const c_void; + unsafe { + ffi::dbus_message_iter_append_basic(i, t, p); + } + } + + fn iter_append(&self, i: &mut ffi::DBusMessageIter) { + match self { + &MessageItem::Str(ref s) => unsafe { + let c = to_c_str(s); + let p = mem::transmute(&c); + ffi::dbus_message_iter_append_basic(i, ffi::DBUS_TYPE_STRING, p); + }, + &MessageItem::Bool(b) => self.iter_append_basic(i, if b { 1u32 } else { 0u32 }), + &MessageItem::Byte(b) => self.iter_append_basic(i, b), + &MessageItem::Int16(b) => self.iter_append_basic(i, b), + &MessageItem::Int32(b) => self.iter_append_basic(i, b), + &MessageItem::Int64(b) => self.iter_append_basic(i, b), + &MessageItem::UInt16(b) => self.iter_append_basic(i, b), + &MessageItem::UInt32(b) => self.iter_append_basic(i, b), + &MessageItem::UInt64(b) => self.iter_append_basic(i, b), + &MessageItem::UnixFd(ref b) => self.iter_append_basic(i, b.as_raw_fd()), + &MessageItem::Double(b) => self.iter_append_basic(i, b), + &MessageItem::Array(ref a) => iter_append_array(i, &a.v, a.element_signature()), + &MessageItem::Struct(ref v) => iter_append_struct(i, &**v), + &MessageItem::Variant(ref b) => iter_append_variant(i, &**b), + &MessageItem::DictEntry(ref k, ref v) => iter_append_dict(i, &**k, &**v), + &MessageItem::ObjectPath(ref s) => unsafe { + let c: *const libc::c_char = s.as_ref().as_ptr(); + let p = mem::transmute(&c); + ffi::dbus_message_iter_append_basic(i, ffi::DBUS_TYPE_OBJECT_PATH, p); + } + } + } + + fn copy_to_iter(i: &mut ffi::DBusMessageIter, v: &[MessageItem]) { + for item in v.iter() { + item.iter_append(i); + } + } + + /// Conveniently get the inner value of a `MessageItem` + /// + /// # Example + /// ``` + /// use dbus::MessageItem; + /// let m: MessageItem = 5i64.into(); + /// let s: i64 = m.inner().unwrap(); + /// assert_eq!(s, 5i64); + /// ``` + pub fn inner<'a, T: FromMessageItem<'a>>(&'a self) -> Result<T, ()> { + T::from(self) + } +} + + +// For use by the msgarg module +pub fn append_messageitem(i: &mut ffi::DBusMessageIter, m: &MessageItem) { + m.iter_append(i) +} + +// For use by the msgarg module +pub fn get_messageitem(i: &mut ffi::DBusMessageIter) -> Option<MessageItem> { + MessageItem::from_iter_single(i) +} + + +macro_rules! msgitem_convert { + ($t: ty, $s: ident) => { + impl From<$t> for MessageItem { fn from(i: $t) -> MessageItem { MessageItem::$s(i) } } + + impl<'a> FromMessageItem<'a> for $t { + fn from(i: &'a MessageItem) -> Result<$t,()> { + if let &MessageItem::$s(ref b) = i { Ok(*b) } else { Err(()) } + } + } + } +} + +msgitem_convert!(u8, Byte); +msgitem_convert!(u64, UInt64); +msgitem_convert!(u32, UInt32); +msgitem_convert!(u16, UInt16); +msgitem_convert!(i16, Int16); +msgitem_convert!(i32, Int32); +msgitem_convert!(i64, Int64); +msgitem_convert!(f64, Double); +msgitem_convert!(bool, Bool); + + +/// Create a `MessageItem::Array`. +impl<'a, T> From<&'a [T]> for MessageItem +where T: Into<MessageItem> + Clone + Default { + fn from(i: &'a [T]) -> MessageItem { + MessageItem::new_array3(i.iter()) + } +} + +impl<'a> From<&'a str> for MessageItem { fn from(i: &str) -> MessageItem { MessageItem::Str(i.to_string()) } } + +impl From<String> for MessageItem { fn from(i: String) -> MessageItem { MessageItem::Str(i) } } + +impl From<Path<'static>> for MessageItem { fn from(i: Path<'static>) -> MessageItem { MessageItem::ObjectPath(i) } } + +impl From<OwnedFd> for MessageItem { fn from(i: OwnedFd) -> MessageItem { MessageItem::UnixFd(i) } } + +/// Create a `MessageItem::Variant` +impl From<Box<MessageItem>> for MessageItem { + fn from(i: Box<MessageItem>) -> MessageItem { MessageItem::Variant(i) } +} + +/// Create a `MessageItem::DictEntry` +impl From<(MessageItem, MessageItem)> for MessageItem { + fn from(i: (MessageItem, MessageItem)) -> MessageItem { + MessageItem::DictEntry(Box::new(i.0), Box::new(i.1)) + } +} + +/// Helper trait for `MessageItem::inner()` +pub trait FromMessageItem<'a> :Sized { + /// Allows converting from a MessageItem into the type it contains. + fn from(i: &'a MessageItem) -> Result<Self, ()>; +} + +impl<'a> FromMessageItem<'a> for &'a str { + fn from(i: &'a MessageItem) -> Result<&'a str,()> { + match i { + &MessageItem::Str(ref b) => Ok(&b), + &MessageItem::ObjectPath(ref b) => Ok(&b), + _ => Err(()), + } + } +} + +impl<'a> FromMessageItem<'a> for &'a String { + fn from(i: &'a MessageItem) -> Result<&'a String,()> { if let &MessageItem::Str(ref b) = i { Ok(&b) } else { Err(()) } } +} + +impl<'a> FromMessageItem<'a> for &'a Path<'static> { + fn from(i: &'a MessageItem) -> Result<&'a Path<'static>,()> { if let &MessageItem::ObjectPath(ref b) = i { Ok(&b) } else { Err(()) } } +} + +impl<'a> FromMessageItem<'a> for &'a MessageItem { + fn from(i: &'a MessageItem) -> Result<&'a MessageItem,()> { if let &MessageItem::Variant(ref b) = i { Ok(&**b) } else { Err(()) } } +} + +impl<'a> FromMessageItem<'a> for &'a Vec<MessageItem> { + fn from(i: &'a MessageItem) -> Result<&'a Vec<MessageItem>,()> { + match i { + &MessageItem::Array(ref b) => Ok(&b.v), + &MessageItem::Struct(ref b) => Ok(&b), + _ => Err(()), + } + } +} + +impl<'a> FromMessageItem<'a> for &'a [MessageItem] { + fn from(i: &'a MessageItem) -> Result<&'a [MessageItem],()> { i.inner::<&Vec<MessageItem>>().map(|s| &**s) } +} + +impl<'a> FromMessageItem<'a> for &'a OwnedFd { + fn from(i: &'a MessageItem) -> Result<&'a OwnedFd,()> { if let &MessageItem::UnixFd(ref b) = i { Ok(b) } else { Err(()) } } +} + +impl<'a> FromMessageItem<'a> for (&'a MessageItem, &'a MessageItem) { + fn from(i: &'a MessageItem) -> Result<(&'a MessageItem, &'a MessageItem),()> { + if let &MessageItem::DictEntry(ref k, ref v) = i { Ok((&**k, &**v)) } else { Err(()) } + } +} + + +/// A D-Bus message. A message contains some headers (e g sender and destination address) +/// and a list of MessageItems. +pub struct Message { + msg: *mut ffi::DBusMessage, +} + +unsafe impl Send for Message {} + +impl Message { + /// Creates a new method call message. + pub fn new_method_call<'d, 'p, 'i, 'm, D, P, I, M>(destination: D, path: P, iface: I, method: M) -> Result<Message, String> + where D: Into<BusName<'d>>, P: Into<Path<'p>>, I: Into<Interface<'i>>, M: Into<Member<'m>> { + init_dbus(); + let (d, p, i, m) = (destination.into(), path.into(), iface.into(), method.into()); + let ptr = unsafe { + ffi::dbus_message_new_method_call(d.as_ref().as_ptr(), p.as_ref().as_ptr(), i.as_ref().as_ptr(), m.as_ref().as_ptr()) + }; + if ptr == ptr::null_mut() { Err("D-Bus error: dbus_message_new_method_call failed".into()) } + else { Ok(Message { msg: ptr}) } + } + + /// Creates a new method call message. + pub fn method_call(destination: &BusName, path: &Path, iface: &Interface, name: &Member) -> Message { + init_dbus(); + let ptr = unsafe { + ffi::dbus_message_new_method_call(destination.as_ref().as_ptr(), path.as_ref().as_ptr(), + iface.as_ref().as_ptr(), name.as_ref().as_ptr()) + }; + if ptr == ptr::null_mut() { panic!("D-Bus error: dbus_message_new_signal failed") } + Message { msg: ptr} + } + + /// Creates a new signal message. + pub fn new_signal<P, I, M>(path: P, iface: I, name: M) -> Result<Message, String> + where P: Into<Vec<u8>>, I: Into<Vec<u8>>, M: Into<Vec<u8>> { + init_dbus(); + + let p = try!(Path::new(path)); + let i = try!(Interface::new(iface)); + let m = try!(Member::new(name)); + + let ptr = unsafe { + ffi::dbus_message_new_signal(p.as_ref().as_ptr(), i.as_ref().as_ptr(), m.as_ref().as_ptr()) + }; + if ptr == ptr::null_mut() { Err("D-Bus error: dbus_message_new_signal failed".into()) } + else { Ok(Message { msg: ptr}) } + } + + /// Creates a new signal message. + pub fn signal(path: &Path, iface: &Interface, name: &Member) -> Message { + init_dbus(); + let ptr = unsafe { + ffi::dbus_message_new_signal(path.as_ref().as_ptr(), iface.as_ref().as_ptr(), name.as_ref().as_ptr()) + }; + if ptr == ptr::null_mut() { panic!("D-Bus error: dbus_message_new_signal failed") } + Message { msg: ptr} + } + + /// Creates a method reply for this method call. + pub fn new_method_return(m: &Message) -> Option<Message> { + let ptr = unsafe { ffi::dbus_message_new_method_return(m.msg) }; + if ptr == ptr::null_mut() { None } else { Some(Message { msg: ptr} ) } + } + + /// Creates a method return (reply) for this method call. + pub fn method_return(&self) -> Message { + let ptr = unsafe { ffi::dbus_message_new_method_return(self.msg) }; + if ptr == ptr::null_mut() { panic!("D-Bus error: dbus_message_new_method_return failed") } + Message {msg: ptr} + } + + /// The old way to create a new error reply + pub fn new_error(m: &Message, error_name: &str, error_message: &str) -> Option<Message> { + let (en, em) = (to_c_str(error_name), to_c_str(error_message)); + let ptr = unsafe { ffi::dbus_message_new_error(m.msg, en.as_ptr(), em.as_ptr()) }; + if ptr == ptr::null_mut() { None } else { Some(Message { msg: ptr} ) } + } + + /// Creates a new error reply + pub fn error(&self, error_name: &ErrorName, error_message: &CStr) -> Message { + let ptr = unsafe { ffi::dbus_message_new_error(self.msg, error_name.as_ref().as_ptr(), error_message.as_ptr()) }; + if ptr == ptr::null_mut() { panic!("D-Bus error: dbus_message_new_error failed") } + Message { msg: ptr} + } + + /// Get the MessageItems that make up the message. + /// + /// Note: use `iter_init` or `get1`/`get2`/etc instead for faster access to the arguments. + /// This method is provided for backwards compatibility. + pub fn get_items(&self) -> Vec<MessageItem> { + let mut i = new_dbus_message_iter(); + match unsafe { ffi::dbus_message_iter_init(self.msg, &mut i) } { + 0 => Vec::new(), + _ => MessageItem::from_iter(&mut i) + } + } + + /// Get the D-Bus serial of a message, if one was specified. + pub fn get_serial(&self) -> u32 { + unsafe { ffi::dbus_message_get_serial(self.msg) } + } + + /// Get the serial of the message this message is a reply to, if present. + pub fn get_reply_serial(&self) -> Option<u32> { + let s = unsafe { ffi::dbus_message_get_reply_serial(self.msg) }; + if s == 0 { None } else { Some(s) } + } + + /// Returns true if the message does not expect a reply. + pub fn get_no_reply(&self) -> bool { unsafe { ffi::dbus_message_get_no_reply(self.msg) != 0 } } + + /// Set whether or not the message expects a reply. + /// + /// Set to true if you send a method call and do not want a reply. + pub fn set_no_reply(&self, v: bool) { + unsafe { ffi::dbus_message_set_no_reply(self.msg, if v { 1 } else { 0 }) } + } + + /// Returns true if the message can cause a service to be auto-started. + pub fn get_auto_start(&self) -> bool { unsafe { ffi::dbus_message_get_auto_start(self.msg) != 0 } } + + /// Sets whether or not the message can cause a service to be auto-started. + /// + /// Defaults to true. + pub fn set_auto_start(&self, v: bool) { + unsafe { ffi::dbus_message_set_auto_start(self.msg, if v { 1 } else { 0 }) } + } + + /// Add one or more MessageItems to this Message. + /// + /// Note: using `append1`, `append2` or `append3` might be faster, especially for large arrays. + /// This method is provided for backwards compatibility. + pub fn append_items(&mut self, v: &[MessageItem]) { + let mut i = new_dbus_message_iter(); + unsafe { ffi::dbus_message_iter_init_append(self.msg, &mut i) }; + MessageItem::copy_to_iter(&mut i, v); + } + + /// Appends one MessageItem to a message. + /// Use in builder style: e g `m.method_return().append(7i32)` + /// + /// Note: using `append1`, `append2` or `append3` might be faster, especially for large arrays. + /// This method is provided for backwards compatibility. + pub fn append<I: Into<MessageItem>>(self, v: I) -> Self { + let mut i = new_dbus_message_iter(); + unsafe { ffi::dbus_message_iter_init_append(self.msg, &mut i) }; + MessageItem::copy_to_iter(&mut i, &[v.into()]); + self + } + + /// Appends one argument to this message. + /// Use in builder style: e g `m.method_return().append1(7i32)` + pub fn append1<A: Append>(mut self, a: A) -> Self { + { + let mut m = IterAppend::new(&mut self); + m.append(a); + } + self + } + + /// Appends two arguments to this message. + /// Use in builder style: e g `m.method_return().append2(7i32, 6u8)` + pub fn append2<A1: Append, A2: Append>(mut self, a1: A1, a2: A2) -> Self { + { + let mut m = IterAppend::new(&mut self); + m.append(a1); m.append(a2); + } + self + } + + /// Appends three arguments to this message. + /// Use in builder style: e g `m.method_return().append3(7i32, 6u8, true)` + pub fn append3<A1: Append, A2: Append, A3: Append>(mut self, a1: A1, a2: A2, a3: A3) -> Self { + { + let mut m = IterAppend::new(&mut self); + m.append(a1); m.append(a2); m.append(a3); + } + self + } + + /// Appends RefArgs to this message. + /// Use in builder style: e g `m.method_return().append_ref(&[7i32, 6u8, true])` + pub fn append_ref<A: RefArg>(mut self, r: &[A]) -> Self { + { + let mut m = IterAppend::new(&mut self); + for rr in r { + rr.append(&mut m); + } + } + self + } + + /// Gets the first argument from the message, if that argument is of type G1. + /// Returns None if there are not enough arguments, or if types don't match. + pub fn get1<'a, G1: Get<'a>>(&'a self) -> Option<G1> { + let mut i = Iter::new(&self); + i.get() + } + + /// Gets the first two arguments from the message, if those arguments are of type G1 and G2. + /// Returns None if there are not enough arguments, or if types don't match. + pub fn get2<'a, G1: Get<'a>, G2: Get<'a>>(&'a self) -> (Option<G1>, Option<G2>) { + let mut i = Iter::new(&self); + let g1 = i.get(); + if !i.next() { return (g1, None); } + (g1, i.get()) + } + + /// Gets the first three arguments from the message, if those arguments are of type G1, G2 and G3. + /// Returns None if there are not enough arguments, or if types don't match. + pub fn get3<'a, G1: Get<'a>, G2: Get<'a>, G3: Get<'a>>(&'a self) -> (Option<G1>, Option<G2>, Option<G3>) { + let mut i = Iter::new(&self); + let g1 = i.get(); + if !i.next() { return (g1, None, None) } + let g2 = i.get(); + if !i.next() { return (g1, g2, None) } + (g1, g2, i.get()) + } + + /// Gets the first four arguments from the message, if those arguments are of type G1, G2, G3 and G4. + /// Returns None if there are not enough arguments, or if types don't match. + pub fn get4<'a, G1: Get<'a>, G2: Get<'a>, G3: Get<'a>, G4: Get<'a>>(&'a self) -> (Option<G1>, Option<G2>, Option<G3>, Option<G4>) { + let mut i = Iter::new(&self); + let g1 = i.get(); + if !i.next() { return (g1, None, None, None) } + let g2 = i.get(); + if !i.next() { return (g1, g2, None, None) } + let g3 = i.get(); + if !i.next() { return (g1, g2, g3, None) } + (g1, g2, g3, i.get()) + } + + /// Gets the first five arguments from the message, if those arguments are of type G1, G2, G3 and G4. + /// Returns None if there are not enough arguments, or if types don't match. + /// Note: If you need more than five arguments, use `iter_init` instead. + pub fn get5<'a, G1: Get<'a>, G2: Get<'a>, G3: Get<'a>, G4: Get<'a>, G5: Get<'a>>(&'a self) -> (Option<G1>, Option<G2>, Option<G3>, Option<G4>, Option<G5>) { + let mut i = Iter::new(&self); + let g1 = i.get(); + if !i.next() { return (g1, None, None, None, None) } + let g2 = i.get(); + if !i.next() { return (g1, g2, None, None, None) } + let g3 = i.get(); + if !i.next() { return (g1, g2, g3, None, None) } + let g4 = i.get(); + if !i.next() { return (g1, g2, g3, g4, None) } + (g1, g2, g3, g4, i.get()) + } + + /// Gets the first argument from the message, if that argument is of type G1. + /// + /// Returns a TypeMismatchError if there are not enough arguments, or if types don't match. + pub fn read1<'a, G1: Arg + Get<'a>>(&'a self) -> Result<G1, TypeMismatchError> { + let mut i = Iter::new(&self); + i.read() + } + + /// Gets the first two arguments from the message, if those arguments are of type G1 and G2. + /// + /// Returns a TypeMismatchError if there are not enough arguments, or if types don't match. + pub fn read2<'a, G1: Arg + Get<'a>, G2: Arg + Get<'a>>(&'a self) -> Result<(G1, G2), TypeMismatchError> { + let mut i = Iter::new(&self); + Ok((try!(i.read()), try!(i.read()))) + } + + /// Gets the first three arguments from the message, if those arguments are of type G1, G2 and G3. + /// + /// Returns a TypeMismatchError if there are not enough arguments, or if types don't match. + pub fn read3<'a, G1: Arg + Get<'a>, G2: Arg + Get<'a>, G3: Arg + Get<'a>>(&'a self) -> + Result<(G1, G2, G3), TypeMismatchError> { + let mut i = Iter::new(&self); + Ok((try!(i.read()), try!(i.read()), try!(i.read()))) + } + + /// Gets the first four arguments from the message, if those arguments are of type G1, G2, G3 and G4. + /// + /// Returns a TypeMismatchError if there are not enough arguments, or if types don't match. + pub fn read4<'a, G1: Arg + Get<'a>, G2: Arg + Get<'a>, G3: Arg + Get<'a>, G4: Arg + Get<'a>>(&'a self) -> + Result<(G1, G2, G3, G4), TypeMismatchError> { + let mut i = Iter::new(&self); + Ok((try!(i.read()), try!(i.read()), try!(i.read()), try!(i.read()))) + } + + /// Gets the first five arguments from the message, if those arguments are of type G1, G2, G3, G4 and G5. + /// + /// Returns a TypeMismatchError if there are not enough arguments, or if types don't match. + /// Note: If you need more than five arguments, use `iter_init` instead. + pub fn read5<'a, G1: Arg + Get<'a>, G2: Arg + Get<'a>, G3: Arg + Get<'a>, G4: Arg + Get<'a>, G5: Arg + Get<'a>>(&'a self) -> + Result<(G1, G2, G3, G4, G5), TypeMismatchError> { + let mut i = Iter::new(&self); + Ok((try!(i.read()), try!(i.read()), try!(i.read()), try!(i.read()), try!(i.read()))) + } + + /// Returns a struct for retreiving the arguments from a message. Supersedes get_items(). + pub fn iter_init<'a>(&'a self) -> Iter<'a> { Iter::new(&self) } + + /// Gets the MessageType of the Message. + pub fn msg_type(&self) -> MessageType { + unsafe { mem::transmute(ffi::dbus_message_get_type(self.msg)) } + } + + fn msg_internal_str<'a>(&'a self, c: *const libc::c_char) -> Option<&'a [u8]> { + if c == ptr::null() { None } + else { Some( unsafe { CStr::from_ptr(c) }.to_bytes_with_nul()) } + } + + /// Gets the name of the connection that originated this message. + pub fn sender<'a>(&'a self) -> Option<BusName<'a>> { + self.msg_internal_str(unsafe { ffi::dbus_message_get_sender(self.msg) }) + .map(|s| unsafe { BusName::from_slice_unchecked(s) }) + } + + /// Returns a tuple of (Message type, Path, Interface, Member) of the current message. + pub fn headers(&self) -> (MessageType, Option<String>, Option<String>, Option<String>) { + let p = unsafe { ffi::dbus_message_get_path(self.msg) }; + let i = unsafe { ffi::dbus_message_get_interface(self.msg) }; + let m = unsafe { ffi::dbus_message_get_member(self.msg) }; + (self.msg_type(), + c_str_to_slice(&p).map(|s| s.to_string()), + c_str_to_slice(&i).map(|s| s.to_string()), + c_str_to_slice(&m).map(|s| s.to_string())) + } + + /// Gets the object path this Message is being sent to. + pub fn path<'a>(&'a self) -> Option<Path<'a>> { + self.msg_internal_str(unsafe { ffi::dbus_message_get_path(self.msg) }) + .map(|s| unsafe { Path::from_slice_unchecked(s) }) + } + + /// Gets the destination this Message is being sent to. + pub fn destination<'a>(&'a self) -> Option<BusName<'a>> { + self.msg_internal_str(unsafe { ffi::dbus_message_get_destination(self.msg) }) + .map(|s| unsafe { BusName::from_slice_unchecked(s) }) + } + + /// Sets the destination of this Message + /// + /// If dest is none, that means broadcast to all relevant destinations. + pub fn set_destination(&mut self, dest: Option<BusName>) { + let c_dest = dest.as_ref().map(|d| d.as_cstr().as_ptr()).unwrap_or(ptr::null()); + assert!(unsafe { ffi::dbus_message_set_destination(self.msg, c_dest) } != 0); + } + + /// Gets the interface this Message is being sent to. + pub fn interface<'a>(&'a self) -> Option<Interface<'a>> { + self.msg_internal_str(unsafe { ffi::dbus_message_get_interface(self.msg) }) + .map(|s| unsafe { Interface::from_slice_unchecked(s) }) + } + + /// Gets the interface member being called. + pub fn member<'a>(&'a self) -> Option<Member<'a>> { + self.msg_internal_str(unsafe { ffi::dbus_message_get_member(self.msg) }) + .map(|s| unsafe { Member::from_slice_unchecked(s) }) + } + + /// When the remote end returns an error, the message itself is + /// correct but its contents is an error. This method will + /// transform such an error to a D-Bus Error or otherwise return + /// the original message. + pub fn as_result(&mut self) -> Result<&mut Message, Error> { + self.set_error_from_msg().map(|_| self) + } + + pub (super) fn set_error_from_msg(&self) -> Result<(), Error> { + let mut e = Error::empty(); + if unsafe { ffi::dbus_set_error_from_message(e.get_mut(), self.msg) } != 0 { Err(e) } + else { Ok(()) } + } + + pub (crate) fn ptr(&self) -> *mut ffi::DBusMessage { self.msg } + + pub (crate) fn from_ptr(ptr: *mut ffi::DBusMessage, add_ref: bool) -> Message { + if add_ref { + unsafe { ffi::dbus_message_ref(ptr) }; + } + Message { msg: ptr } + } + +} + +impl Drop for Message { + fn drop(&mut self) { + unsafe { + ffi::dbus_message_unref(self.msg); + } + } +} + +impl fmt::Debug for Message { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{:?}", self.headers()) + } +} + +/// A convenience struct that wraps connection, destination and path. +/// +/// Useful if you want to make many method calls to the same destination path. +#[derive(Clone, Debug)] +pub struct ConnPath<'a, C> { + /// Some way to access the connection, e g a &Connection or Rc<Connection> + pub conn: C, + /// Destination, i e what D-Bus service you're communicating with + pub dest: BusName<'a>, + /// Object path on the destination + pub path: Path<'a>, + /// Timeout in milliseconds for blocking method calls + pub timeout: i32, +} + +impl<'a, C: ::std::ops::Deref<Target=Connection>> ConnPath<'a, C> { + /// Make a D-Bus method call, where you can append arguments inside the closure. + pub fn method_call_with_args<F: FnOnce(&mut Message)>(&self, i: &Interface, m: &Member, f: F) -> Result<Message, Error> { + let mut msg = Message::method_call(&self.dest, &self.path, i, m); + f(&mut msg); + self.conn.send_with_reply_and_block(msg, self.timeout) + } + + /// Emit a D-Bus signal, where you can append arguments inside the closure. + pub fn signal_with_args<F: FnOnce(&mut Message)>(&self, i: &Interface, m: &Member, f: F) -> Result<u32, Error> { + let mut msg = Message::signal(&self.path, i, m); + f(&mut msg); + self.conn.send(msg).map_err(|_| Error::new_custom("org.freedesktop.DBus.Error.Failed", "Sending signal failed")) + } + + /// Emit a D-Bus signal, where the arguments are in a struct. + pub fn emit<S: SignalArgs>(&self, signal: &S) -> Result<u32, Error> { + let msg = signal.to_emit_message(&self.path); + self.conn.send(msg).map_err(|_| Error::new_custom("org.freedesktop.DBus.Error.Failed", "Sending signal failed")) + } +} + +// For purpose of testing the library only. +#[cfg(test)] +pub (crate) fn message_set_serial(m: &mut Message, s: u32) { + unsafe { ffi::dbus_message_set_serial(m.msg, s) }; +} + +#[cfg(test)] +mod test { + extern crate tempdir; + + use super::super::{Connection, Message, MessageType, BusType, MessageItem, OwnedFd, libc, Path, BusName}; + + #[test] + fn unix_fd() { + use std::io::prelude::*; + use std::io::SeekFrom; + use std::fs::OpenOptions; + use std::os::unix::io::AsRawFd; + + let c = Connection::get_private(BusType::Session).unwrap(); + c.register_object_path("/hello").unwrap(); + let mut m = Message::new_method_call(&c.unique_name(), "/hello", "com.example.hello", "Hello").unwrap(); + let tempdir = tempdir::TempDir::new("dbus-rs-test").unwrap(); + let mut filename = tempdir.path().to_path_buf(); + filename.push("test"); + println!("Creating file {:?}", filename); + let mut file = OpenOptions::new().create(true).read(true).write(true).open(&filename).unwrap(); + file.write_all(b"z").unwrap(); + file.seek(SeekFrom::Start(0)).unwrap(); + let ofd = OwnedFd::new(file.as_raw_fd()); + m.append_items(&[MessageItem::UnixFd(ofd.clone())]); + println!("Sending {:?}", m.get_items()); + c.send(m).unwrap(); + + loop { for n in c.incoming(1000) { + if n.msg_type() == MessageType::MethodCall { + let z: OwnedFd = n.read1().unwrap(); + println!("Got {:?}", z); + let mut q: libc::c_char = 100; + assert_eq!(1, unsafe { libc::read(z.as_raw_fd(), &mut q as *mut _ as *mut libc::c_void, 1) }); + assert_eq!(q, 'z' as libc::c_char); + return; + } else { + println!("Got {:?}", n); + } + }} + } + + #[test] + fn message_types() { + let c = Connection::get_private(BusType::Session).unwrap(); + c.register_object_path("/hello").unwrap(); + let mut m = Message::new_method_call(&c.unique_name(), "/hello", "com.example.hello", "Hello").unwrap(); + m.append_items(&[ + 2000u16.into(), + MessageItem::new_array(vec!(129u8.into())).unwrap(), + ["Hello", "world"][..].into(), + 987654321u64.into(), + (-1i32).into(), + format!("Hello world").into(), + (-3.14f64).into(), + MessageItem::Struct(vec!(256i16.into())), + Path::new("/some/path").unwrap().into(), + MessageItem::new_array(vec!((123543u32.into(), true.into()).into())).unwrap() + ]); + let sending = format!("{:?}", m.get_items()); + println!("Sending {}", sending); + c.send(m).unwrap(); + + loop { for n in c.incoming(1000) { + if n.msg_type() == MessageType::MethodCall { + let receiving = format!("{:?}", n.get_items()); + println!("Receiving {}", receiving); + assert_eq!(sending, receiving); + return; + } else { + println!("Got {:?}", n); + } + }} + } + + #[test] + fn dict_of_dicts() { + use std::collections::BTreeMap; + + let officeactions: BTreeMap<&'static str, MessageItem> = BTreeMap::new(); + let mut officethings = BTreeMap::new(); + officethings.insert("pencil", 2u16.into()); + officethings.insert("paper", 5u16.into()); + let mut homethings = BTreeMap::new(); + homethings.insert("apple", 11u16.into()); + let mut homeifaces = BTreeMap::new(); + homeifaces.insert("getThings", homethings); + let mut officeifaces = BTreeMap::new(); + officeifaces.insert("getThings", officethings); + officeifaces.insert("getActions", officeactions); + let mut paths = BTreeMap::new(); + paths.insert("/hello/office", officeifaces); + paths.insert("/hello/home", homeifaces); + + println!("Original treemap: {:?}", paths); + let m = MessageItem::new_array(paths.iter().map( + |(path, ifaces)| (MessageItem::ObjectPath(Path::new(*path).unwrap()), + MessageItem::new_array(ifaces.iter().map( + |(iface, props)| (iface.to_string().into(), + MessageItem::from_dict::<(),_>(props.iter().map( + |(name, value)| Ok((name.to_string(), value.clone())) + )).unwrap() + ).into() + ).collect()).unwrap() + ).into() + ).collect()).unwrap(); + println!("As MessageItem: {:?}", m); + assert_eq!(&*m.signature(), "a{oa{sa{sv}}}"); + + let c = Connection::get_private(BusType::Session).unwrap(); + c.register_object_path("/hello").unwrap(); + let mut msg = Message::new_method_call(&c.unique_name(), "/hello", "org.freedesktop.DBusObjectManager", "GetManagedObjects").unwrap(); + msg.append_items(&[m]); + let sending = format!("{:?}", msg.get_items()); + println!("Sending {}", sending); + c.send(msg).unwrap(); + + loop { for n in c.incoming(1000) { + if n.msg_type() == MessageType::MethodCall { + let receiving = format!("{:?}", n.get_items()); + println!("Receiving {}", receiving); + assert_eq!(sending, receiving); + return; + } else { + println!("Got {:?}", n); + } + } } + } + + #[test] + fn issue24() { + let c = Connection::get_private(BusType::Session).unwrap(); + let mut m = Message::new_method_call("org.test.rust", "/", "org.test.rust", "Test").unwrap(); + + let a = MessageItem::from("test".to_string()); + let b = MessageItem::from("test".to_string()); + let foo = MessageItem::Struct(vec!(a, b)); + let bar = foo.clone(); + + let args = [MessageItem::new_array(vec!(foo, bar)).unwrap()]; + println!("{:?}", args); + + m.append_items(&args); + c.send(m).unwrap(); + } + + #[test] + fn set_valid_destination() { + let mut m = Message::new_method_call("org.test.rust", "/", "org.test.rust", "Test").unwrap(); + let d = Some(BusName::new(":1.14").unwrap()); + m.set_destination(d); + + assert!(!m.get_no_reply()); + m.set_no_reply(true); + assert!(m.get_no_reply()); + } +} diff --git a/third_party/rust/dbus/src/methoddisp.rs b/third_party/rust/dbus/src/methoddisp.rs new file mode 100644 index 0000000000..9f3ed3695b --- /dev/null +++ b/third_party/rust/dbus/src/methoddisp.rs @@ -0,0 +1,1058 @@ +/// NOTE: No longer used - replaced with files in the "tree" directory. + + +#![allow(dead_code)] + +use {MessageItem, Message, MessageType, Connection, ConnectionItem, Error, ErrorName}; +use {Signature, Member, Path}; +use Interface as IfaceName; +use std::cell::RefCell; +use std::sync::{Arc, Mutex}; +use std::collections::BTreeMap; +use std::marker::PhantomData; +use std::ffi::{CStr, CString}; +use std::fmt; +use super::arg; + +type ArcMap<K, V> = BTreeMap<Arc<K>, Arc<V>>; + +#[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) } + + 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) + } + fn introspect_all(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)] +struct Annotations(Option<BTreeMap<String, String>>); + +impl Annotations { + fn new() -> Annotations { Annotations(None) } + + 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()); + } + + 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()) } +} + +#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] +/// A D-Bus Method Error. +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 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() + } +} + +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()) } +} + +/// Result containing the Messages returned from the Method, or a MethodErr. +pub type MethodResult = Result<Vec<Message>, MethodErr>; + +/// A MethodType that wraps an Fn function +pub struct MethodFn<'a>(Box<Fn(&Message, &ObjectPath<MethodFn<'a>>, &Tree<MethodFn<'a>>) -> MethodResult + 'a>); +/// A MethodType that wraps an FnMut function. Calling this recursively will cause a refcell panic. +pub struct MethodFnMut<'a>(Box<RefCell<FnMut(&Message, &ObjectPath<MethodFnMut<'a>>, &Tree<MethodFnMut<'a>>) -> MethodResult + 'a>>); +/// A MethodType that wraps an Fn+Send+Sync function, so it can be called from several threads in parallel. +pub struct MethodSync(Box<Fn(&Message, &ObjectPath<MethodSync>, &Tree<MethodSync>) -> MethodResult + Send + Sync + 'static>); + +impl<'a> fmt::Debug for MethodFn<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "<Fn>") } +} + +impl<'a> fmt::Debug for MethodFnMut<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "<FnMut>") } +} + +impl fmt::Debug for MethodSync { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "<Fn + Send + Sync>") } +} + +/// A helper trait used internally to make the tree generic over MethodFn, MethodFnMut and MethodSync. +pub trait MethodType: Sized { + fn call_method(&self, m: &Message, o: &ObjectPath<Self>, i: &Tree<Self>) -> MethodResult; + fn box_method<H>(h: H) -> Self + where H: Fn(&Message, &ObjectPath<Self>, &Tree<Self>) -> MethodResult + Send + Sync + 'static; +} + +impl<'a> MethodType for MethodFn<'a> { + fn call_method(&self, m: &Message, o: &ObjectPath<MethodFn<'a>>, i: &Tree<MethodFn<'a>>) -> MethodResult { self.0(m, o, i) } + + fn box_method<H>(h: H) -> Self + where H: Fn(&Message, &ObjectPath<MethodFn<'a>>, &Tree<MethodFn<'a>>) -> MethodResult + Send + Sync + 'static { + MethodFn(Box::new(h)) + } +} + +impl MethodType for MethodSync { + fn call_method(&self, m: &Message, o: &ObjectPath<MethodSync>, i: &Tree<MethodSync>) -> MethodResult { self.0(m, o, i) } + + fn box_method<H>(h: H) -> Self + where H: Fn(&Message, &ObjectPath<MethodSync>, &Tree<MethodSync>) -> MethodResult + Send + Sync + 'static { + MethodSync(Box::new(h)) + } +} + +impl<'a> MethodType for MethodFnMut<'a> { + fn call_method(&self, m: &Message, o: &ObjectPath<MethodFnMut<'a>>, i: &Tree<MethodFnMut<'a>>) -> MethodResult { + let mut z = self.0.borrow_mut(); + (&mut *z)(m, o, i) + } + + fn box_method<H>(h: H) -> Self + where H: Fn(&Message, &ObjectPath<MethodFnMut<'a>>, &Tree<MethodFnMut<'a>>) -> MethodResult + Send + Sync + 'static { + MethodFnMut(Box::new(RefCell::new(h))) + } +} + +#[derive(Debug)] +/// A D-Bus Method. +pub struct Method<M> { + cb: M, + name: Arc<Member<'static>>, + i_args: Vec<Argument>, + o_args: Vec<Argument>, + anns: Annotations, +} + +impl<M> Method<M> { + /// 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 + } + + /// Add 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 + } + /// Add an annotation that this entity is deprecated. + pub fn deprecated(self) -> Self { self.annotate("org.freedesktop.DBus.Deprecated", "true") } +} + +impl<M: MethodType> Method<M> { + /// Call the Method. + pub fn call(&self, m: &Message, o: &ObjectPath<M>, i: &Tree<M>) -> MethodResult { self.cb.call_method(m, o, i) } + + fn new(n: Member<'static>, cb: M) -> Self { Method { name: Arc::new(n), i_args: vec!(), + o_args: vec!(), anns: Annotations::new(), cb: cb } } +} + + +#[derive(Debug)] +/// Represents a D-Bus interface. +pub struct Interface<M> { + name: Arc<IfaceName<'static>>, + methods: ArcMap<Member<'static>, Method<M>>, + signals: ArcMap<Member<'static>, Signal>, + properties: ArcMap<String, Property<M>>, + anns: Annotations, +} + +impl<M> Interface<M> { + /// Adds a method to the interface. + pub fn add_m(mut self, m: Method<M>) -> Self { self.methods.insert(m.name.clone(), Arc::new(m)); self } + /// Adds a signal to the interface. + pub fn add_s(mut self, s: Signal) -> Self { self.signals.insert(s.name.clone(), Arc::new(s)); self } + /// Adds a signal to the interface. Lets you keep another clone of the signal + /// (which you can use to emit the signal, once it belongs to an object path). + /// + /// Note: You are not allowed to add a signal to more than one interface. + pub fn add_s_arc(mut self, s: Arc<Signal>) -> Self { self.signals.insert(s.name.clone(), s); self } + /// Adds a signal to the interface. Returns a reference to the signal + /// (which you can use to emit the signal, once it belongs to an object path). + pub fn add_s_ref(&mut self, s: Signal) -> Arc<Signal> { + let s = Arc::new(s); + self.signals.insert(s.name.clone(), s.clone()); + s + } + + /// Adds a property to the interface. + pub fn add_p(mut self, p: Property<M>) -> Self { self.properties.insert(p.name.clone(), Arc::new(p)); self } + /// Adds a property to the interface. Lets you keep another clone of the property + /// (which you can use to get and set the current value of the property). + /// + /// Note: You are not allowed to add a property to more than one interface. Later function calls might panic if you do so. + pub fn add_p_arc(mut self, p: Arc<Property<M>>) -> Self { self.properties.insert(p.name.clone(), p); self } + /// Adds a property to the interface. Returns a reference to the property + /// (which you can use to get and set the current value of the property). + pub fn add_p_ref(&mut self, p: Property<M>) -> Arc<Property<M>> { + let p = Arc::new(p); + self.properties.insert(p.name.clone(), p.clone()); + p + } + + /// Add an annotation to this Inteface. + 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") } + + fn new(t: IfaceName<'static>) -> Interface<M> { + Interface { name: Arc::new(t), methods: BTreeMap::new(), signals: BTreeMap::new(), + properties: BTreeMap::new(), anns: Annotations::new() + } + } + +} + +#[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", + } + } +} + +#[derive(Debug)] +/// A D-Bus Property. +pub struct Property<M> { + name: Arc<String>, + value: Mutex<MessageItem>, + emits: EmitsChangedSignal, + rw: Access, + set_cb: Option<M>, + owner: Mutex<Option<(Arc<Path<'static>>, Arc<IfaceName<'static>>)>>, + anns: Annotations, +} + +impl<M: MethodType> Property<M> { + /// Gets the value of the Property. + pub fn get_value(&self) -> MessageItem { + self.value.lock().unwrap().clone() + } + + /// Gets the signal (if any) associated with the Property. + pub fn get_signal(&self) -> Option<Message> { + self.owner.lock().unwrap().as_ref().map(|&(ref p, ref i)| { + Message::signal(&p, &"org.freedesktop.DBus.Properties".into(), &"PropertiesChanged".into()) + .append(String::from(&***i)) + }) + } + + /// Returns error if "emits" is "Const", and the property is in a + /// tree. Returns messages to be sent over a connection, this + /// could be the PropertiesChanged signal. + pub fn set_value(&self, m: MessageItem) -> Result<Vec<Message>,()> { + let ss = match self.emits { + EmitsChangedSignal::False => None, + EmitsChangedSignal::Const => if self.get_signal().is_some() { return Err(()) } else { None }, + EmitsChangedSignal::True => self.get_signal().map(|s| + s.append2(arg::Dict::new(vec!((&**self.name, arg::Variant(m.clone())))), arg::Array::<&str, _>::new(vec!())) + ), + EmitsChangedSignal::Invalidates => self.get_signal().map(|s| { + s.append2(arg::Dict::<&str, arg::Variant<MessageItem>, _>::new(vec!()), arg::Array::new(vec!(&**self.name))) + }), + }; + *self.value.lock().unwrap() = m; + Ok(ss.map(|s| vec!(s)).unwrap_or(vec!())) + } + + /// Builder method that allows setting the Property's signal + /// behavior when changed. + pub fn emits_changed(mut self, e: EmitsChangedSignal) -> Self { + self.emits = e; + assert!(self.rw == Access::Read || self.emits != EmitsChangedSignal::Const); + self + } + + /// Builder method that allows setting the Property as readable, + /// writable, or both. + pub fn access(mut self, e: Access) -> Self { + self.rw = e; + assert!(self.rw == Access::Read || self.emits != EmitsChangedSignal::Const); + self + } + + /// Helper method to check accessibility before getting a value. + pub fn remote_get(&self, _: &Message) -> Result<MessageItem, MethodErr> { + // TODO: We should be able to call a user-defined callback here instead... + if self.rw == Access::Write { return Err(MethodErr::failed(&format!("Property {} is write only", &self.name))) } + Ok(self.get_value()) + } + + /// Helper method to verify and extract a MessageItem from a Set message + pub fn verify_remote_set(&self, m: &Message) -> Result<MessageItem, MethodErr> { + let items = m.get_items(); + let s: &MessageItem = try!(items.get(2).ok_or_else(|| MethodErr::no_arg()) + .and_then(|i| i.inner().map_err(|_| MethodErr::invalid_arg(&i)))); + + if self.rw == Access::Read { Err(MethodErr::ro_property(&self.name)) } + else if s.type_sig() != self.value.lock().unwrap().type_sig() { + Err(MethodErr::failed(&format!("Property {} cannot change type to {}", &self.name, s.type_sig()))) + } + else { Ok(s.clone()) } + } + + fn remote_set(&self, m: &Message, o: &ObjectPath<M>, t: &Tree<M>) -> Result<Vec<Message>, MethodErr> { + if let Some(ref cb) = self.set_cb { + cb.call_method(m, o, t) + } + else { + let s = try!(self.verify_remote_set(m)); + self.set_value(s).map_err(|_| MethodErr::ro_property(&self.name)) + } + } + + /// Add an annotation to this Property. + 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") } + + fn new(s: String, i: MessageItem) -> Property<M> { + Property { name: Arc::new(s), emits: EmitsChangedSignal::True, rw: Access::Read, + value: Mutex::new(i), owner: Mutex::new(None), anns: Annotations::new(), set_cb: None } + } +} + +impl Property<MethodSync> { + /// Sets a callback to be called when a "Set" call is coming in from the remote side. + /// Might change to something more ergonomic. + /// For multi-thread use. + pub fn on_set<H>(mut self, m: H) -> Self + where H: Fn(&Message, &ObjectPath<MethodSync>, &Tree<MethodSync>) -> MethodResult + Send + Sync + 'static { + self.set_cb = Some(MethodSync::box_method(m)); + self + } +} + +impl<'a> Property<MethodFn<'a>> { + /// Sets a callback to be called when a "Set" call is coming in from the remote side. + /// Might change to something more ergonomic. + /// For single-thread use. + pub fn on_set<H: 'a>(mut self, m: H) -> Self + where H: Fn(&Message, &ObjectPath<MethodFn<'a>>, &Tree<MethodFn<'a>>) -> MethodResult { + self.set_cb = Some(MethodFn(Box::new(m))); + self + } +} + +impl<'a> Property<MethodFnMut<'a>> { + /// Sets a callback to be called when a "Set" call is coming in from the remote side. + /// Might change to something more ergonomic. + /// For single-thread use. + pub fn on_set<H: 'a>(mut self, m: H) -> Self + where H: FnMut(&Message, &ObjectPath<MethodFnMut<'a>>, &Tree<MethodFnMut<'a>>) -> MethodResult { + self.set_cb = Some(MethodFnMut(Box::new(RefCell::new(m)))); + self + } +} + + +#[derive(Debug)] +/// A D-Bus Signal. +pub struct Signal { + name: Arc<Member<'static>>, + arguments: Vec<Argument>, + owner: Mutex<Option<(Arc<Path<'static>>, Arc<IfaceName<'static>>)>>, + anns: Annotations, +} + +impl Signal { + /// Returns a message which emits the signal when sent. + /// Panics if the signal is not inserted in an object path. + pub fn emit(&self, items: &[MessageItem]) -> Message { + let mut m = { + let lock = self.owner.lock().unwrap(); + let &(ref p, ref i) = lock.as_ref().unwrap(); + Message::signal(p, i, &self.name) + }; + m.append_items(items); + m + } + + /// Returns a message which emits the signal when sent. + /// Panics if the signal is not inserted in an object path. + /// + /// Same as "emit" but does not take a "MessageItem" argument. + pub fn msg(&self) -> Message { self.emit(&[]) } + + /// 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 "rguments to the Signel. + 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") } +} + +fn introspect_map<T, I: fmt::Display, C: Fn(&T) -> (String, String)> + (h: &ArcMap<I, T>, name: &str, indent: &str, func: C) -> String { + + h.iter().fold("".into(), |a, (k, v)| { + let (params, contents) = func(v); + format!("{}{}<{} name=\"{}\"{}{}>\n", + a, indent, name, &**k, params, if contents.len() > 0 { + format!(">\n{}{}</{}", contents, indent, name) + } + else { format!("/") } + ) + }) +} + +#[derive(Debug)] +/// A D-Bus Object Path. +pub struct ObjectPath<M> { + name: Arc<Path<'static>>, + ifaces: ArcMap<IfaceName<'static>, Interface<M>>, +} + +impl<M: MethodType> ObjectPath<M> { + fn new(p: Path<'static>) -> ObjectPath<M> { + ObjectPath { name: Arc::new(p), ifaces: BTreeMap::new() } + } + + fn get_iface<'a>(&'a self, i: Option<&'a CStr>) -> Result<&Arc<Interface<M>>, MethodErr> { + let iface_name = try!(i.ok_or_else(|| MethodErr::invalid_arg(&0))); + 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_set(&self, m: &Message, o: &ObjectPath<M>, t: &Tree<M>) -> MethodResult { + let (iname, p) = m.get2(); + let iface = try!(self.get_iface(iname)); + let prop_name: &str = try!(p.ok_or_else(|| MethodErr::invalid_arg(&1))); + let prop: &Property<M> = try!(iface.properties.get(&String::from(prop_name)) + .ok_or_else(|| MethodErr::no_property(&prop_name))); + let mut r = try!(prop.remote_set(m, o, t)); + r.push(m.method_return()); + Ok(r) + } + + fn prop_get(&self, m: &Message) -> MethodResult { + let (iname, p) = m.get2(); + let iface = try!(self.get_iface(iname)); + let prop_name: &str = try!(p.ok_or_else(|| MethodErr::invalid_arg(&1))); + let prop: &Property<M> = try!(iface.properties.get(&String::from(prop_name)) + .ok_or_else(|| MethodErr::no_property(&prop_name))); + let r = try!(prop.remote_get(m)); + Ok(vec!(m.method_return().append1(arg::Variant(r)))) + } + + fn prop_get_all(&self, m: &Message) -> MethodResult { + let iface = try!(self.get_iface(m.get1())); + let mut q = vec!(); + for v in iface.properties.values() { + q.push((&**v.name, arg::Variant(try!(v.remote_get(m))))); + } + Ok(vec!(m.method_return().append1(arg::Dict::new(q)))) + } + + fn add_property_handler(&mut self) { + let ifname = IfaceName::from("org.freedesktop.DBus.Properties"); + if self.ifaces.contains_key(&ifname) { return }; + let f: Factory<M> = Factory(PhantomData); + + let i = Interface::<M>::new(ifname) + .add_m(f.method_sync("Get", |m,o,_| o.prop_get(m) ) + .inarg::<&str,_>("interface_name") + .inarg::<&str,_>("property_name") + .outarg::<arg::Variant<()>,_>("value")) + .add_m(f.method_sync("GetAll", |m,o,_| o.prop_get_all(m)) + .inarg::<&str,_>("interface_name") + .outarg::<arg::Dict<&str, arg::Variant<()>, ()>,_>("props")) + .add_m(f.method_sync("Set", |m,o,t| o.prop_set(m, o, t)) + .inarg::<&str,_>("interface_name") + .inarg::<&str,_>("property_name") + .inarg::<arg::Variant<()>,_>("value")); + self.ifaces.insert(i.name.clone(), Arc::new(i)); + } + + /// Add an Interface to this Object Path. + pub fn add(mut self, p: Interface<M>) -> Self { + use std::mem; + for s in p.signals.values() { + let n = Some((self.name.clone(), p.name.clone())); + let o = mem::replace(&mut *s.owner.lock().unwrap(), n); + assert!(o.is_none(), "Signal {} already added to object path", s.name); + }; + for s in p.properties.values() { + let n = Some((self.name.clone(), p.name.clone())); + let o = mem::replace(&mut *s.owner.lock().unwrap(), n); + assert!(o.is_none(), "Property {} already added to object path", s.name); + }; + if !p.properties.is_empty() { self.add_property_handler(); } + self.ifaces.insert(p.name.clone(), Arc::new(p)); + self + } + + /// Adds introspection support for this object path. + pub fn introspectable(self) -> Self { + let ifname: IfaceName = "org.freedesktop.DBus.Introspectable".into(); + if self.ifaces.contains_key(&ifname) { return self }; + let f: Factory<M> = Factory(PhantomData); + self.add(Interface::<M>::new(ifname) + .add_m(f.method_sync("Introspect", + |m,o,t| Ok(vec!(m.method_return().append(o.introspect(t))))) + .out_arg(("xml_data", "s")))) + } + + fn handle(&self, m: &Message, t: &Tree<M>) -> MethodResult { + let i = try!(m.interface().and_then(|i| self.ifaces.get(&i)).ok_or( + ("org.freedesktop.DBus.Error.UnknownInterface", "Unknown interface"))); + let me = try!(m.member().and_then(|me| i.methods.get(&me)).ok_or( + ("org.freedesktop.DBus.Error.UnknownMethod", "Unknown method"))); + me.call(m, &self, t) + } + + fn introspect(&self, tree: &Tree<M>) -> String { + let ifacestr = introspect_map(&self.ifaces, "interface", " ", |iv| + (format!(""), format!("{}{}{}{}", + introspect_map(&iv.methods, "method", " ", |m| (format!(""), format!("{}{}{}", + Argument::introspect_all(&m.i_args, " ", " direction=\"in\""), + Argument::introspect_all(&m.o_args, " ", " direction=\"out\""), + m.anns.introspect(" ") + ))), + introspect_map(&iv.properties, "property", " ", |p| ( + format!(" type=\"{}\" access=\"{}\"", p.get_value().type_sig(), p.rw.introspect()), + p.anns.introspect(" ") + )), + introspect_map(&iv.signals, "signal", " ", |s| (format!(""), format!("{}{}", + Argument::introspect_all(&s.arguments, " ", ""), + s.anns.introspect(" ") + ))), + iv.anns.introspect(" ") + )) + ); + 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_managed_objects(&self, t: &Tree<M>) -> MessageItem { + let mut paths = t.children(&self, false); + paths.push(&self); + MessageItem::Array( + paths.iter().map(|p| ((&*p.name).clone().into(), MessageItem::Array( + p.ifaces.values().map(|i| ((&**i.name).into(), MessageItem::Array( + i.properties.values().map(|pp| ((&**pp.name).into(), Box::new(pp.get_value() + ).into()).into()).collect(), "{sv}".into() + )).into()).collect(), "{sa{sv}}".into() + )).into()).collect(), "{oa{sa{sv}}}".into() + ) + } + + /// 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(self) -> Self { + let ifname: IfaceName = "org.freedesktop.DBus.ObjectManager".into(); + if self.ifaces.contains_key(&ifname) { return self }; + let f: Factory<M> = Factory(PhantomData); + self.add(Interface::<M>::new(ifname) + .add_m(f.method_sync("GetManagedObjects", + |m,o,t| Ok(vec!(m.method_return().append(o.get_managed_objects(t))))) + .out_arg("a{oa{sa{sv}}}"))) + } +} + +/// 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: 'a> { + iter: I, + conn: &'a Connection, + tree: &'a Tree<M>, +} + +impl<'a, I: Iterator<Item=ConnectionItem>, M: 'a + MethodType> Iterator for TreeServer<'a, I, M> { + 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; + } + } +} + +/// A collection of object paths. +#[derive(Debug)] +pub struct Tree<M> { + paths: ArcMap<Path<'static>, ObjectPath<M>> +} + +impl<M: MethodType> Tree<M> { + + fn children(&self, o: &ObjectPath<M>, direct_only: bool) -> Vec<&ObjectPath<M>> { + 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() + } + + /// Add an Object Path to this Tree. + /// + /// Note: This does not unregister 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(mut self, p: ObjectPath<M>) -> Self { + self.paths.insert(p.name.clone(), Arc::new(p)); + self + } + + /// Adds an ObjectPath to this Tree. Returns a reference to the ObjectPath. + /// The note for add() also applies here. + pub fn add_o_ref(&mut self, p: ObjectPath<M>) -> Arc<ObjectPath<M>> { + let name = p.name.clone(); + let o = Arc::new(p); + self.paths.insert(name, o.clone()); + o + } + + /// 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>>> { + // 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(()) + } + + /// Handles a message. Will return None in case the object path was not + /// found, 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!(m.error(&e.0, &CString::new(e.1).unwrap()))))) } + } + + /// 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> { + TreeServer { iter: i, tree: &self, conn: c } + } +} + +/// The factory is used to create object paths, interfaces, methods etc. +/// +/// There are three factories: +/// +/// **Fn** - all methods are `Fn()`. +/// +/// **FnMut** - 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. +/// +/// **Sync** - all methods are `Fn() + Send + Sync + 'static`. This means that the methods +/// can be called from different threads in parallel. +#[derive(Debug, Copy, Clone)] +pub struct Factory<M>(PhantomData<M>); + +impl<'a> Factory<MethodFn<'a>> { + + /// Creates a new factory for single-thread use. + pub fn new_fn() -> Self { Factory(PhantomData) } + + /// Creates a new method for single-thread use. + pub fn method<'b, H: 'b, T>(&self, t: T, handler: H) -> Method<MethodFn<'b>> + where H: Fn(&Message, &ObjectPath<MethodFn<'b>>, &Tree<MethodFn<'b>>) -> MethodResult, T: Into<Member<'static>> { + Method::new(t.into(), MethodFn(Box::new(handler))) + } + + /// Creates a new property for single-thread use. + pub fn property<'b, T: Into<String>, I: Into<MessageItem>>(&self, t: T, i: I) -> Property<MethodFn<'b>> { + Property::new(t.into(), i.into()) + } + + /// Creates a new interface for single-thread use. + pub fn interface<'b, T: Into<IfaceName<'static>>>(&self, t: T) -> Interface<MethodFn<'b>> { Interface::new(t.into()) } + + /// Creates a new tree for single-thread use. + pub fn tree<'b>(&self) -> Tree<MethodFn<'b>> { Tree { paths: BTreeMap::new() }} + + /// Creates a new object path for single-thread use. + pub fn object_path<'b, T: Into<Path<'static>>>(&self, t: T) -> ObjectPath<MethodFn<'b>> { ObjectPath::new(t.into()) } +} + +impl<'a> Factory<MethodFnMut<'a>> { + + /// Creates a new factory for single-thread + mutable fns use. + pub fn new_fnmut() -> Self { Factory(PhantomData) } + + /// Creates a new method for single-thread use. + /// This method can mutate its environment, so it will panic in case + /// it is called recursively. + pub fn method<'b, H: 'b, T>(&self, t: T, handler: H) -> Method<MethodFnMut<'b>> + where H: FnMut(&Message, &ObjectPath<MethodFnMut<'b>>, &Tree<MethodFnMut<'b>>) -> MethodResult, T: Into<Member<'static>> { + Method::new(t.into(), MethodFnMut(Box::new(RefCell::new(handler)))) + } + + /// Creates a new mutable property for single-thread use. + pub fn property<'b, T: Into<String>, I: Into<MessageItem>>(&self, t: T, i: I) -> Property<MethodFnMut<'b>> { + Property::new(t.into(), i.into()) + } + + /// Creates a new mutable interface for single-thread use. + pub fn interface<'b, T: Into<IfaceName<'static>>>(&self, t: T) -> Interface<MethodFnMut<'b>> { Interface::new(t.into()) } + + /// Creates a new mutable tree for single-thread use. + pub fn tree<'b>(&self) -> Tree<MethodFnMut<'b>> { Tree { paths: BTreeMap::new() }} + + /// Creates a new mutable object path for single-thread use. + pub fn object_path<'b, T: Into<Path<'static>>>(&self, t: T) -> ObjectPath<MethodFnMut<'b>> { ObjectPath::new(t.into()) } +} + +impl Factory<MethodSync> { + + /// Creates a new factory for multi-thread use. + /// Trees created will be able to Send and Sync, i e, + /// it can handle several messages in parallel. + pub fn new_sync() -> Self { Factory(PhantomData) } + + /// Creates a new method for multi-thread use. + /// This puts bounds on the callback to enable it to be called from several threads + /// in parallel. + pub fn method<H, T>(&self, t: T, handler: H) -> Method<MethodSync> + where H: Fn(&Message, &ObjectPath<MethodSync>, &Tree<MethodSync>) -> MethodResult + Send + Sync + 'static, T: Into<Member<'static>> { + Method::new(t.into(), MethodSync(Box::new(handler))) + } + + /// Creates a new property for multi-threaded use. + pub fn property<T: Into<String>, I: Into<MessageItem>>(&self, t: T, i: I) -> Property<MethodSync> { + Property::new(t.into(), i.into()) + } + + /// Creates a new interface for multi-threaded use. + pub fn interface<T: Into<IfaceName<'static>>>(&self, t: T) -> Interface<MethodSync> { Interface::new(t.into()) } + + /// Creates a new tree for multi-threaded use. + pub fn tree(&self) -> Tree<MethodSync> { Tree { paths: BTreeMap::new() }} + + /// Creates a new object path for multi-threaded use. + pub fn object_path<T: Into<Path<'static>>>(&self, t: T) -> ObjectPath<MethodSync> { ObjectPath::new(t.into()) } +} + +impl<M> Factory<M> { + /// Create a Signal. + pub fn signal<T: Into<Member<'static>>>(&self, t: T) -> Signal { + Signal { name: Arc::new(t.into()), arguments: vec!(), owner: Mutex::new(None), anns: Annotations::new() } + } +} + +impl<M: MethodType> Factory<M> { + /// Creates a new method with bounds enough to be used in all trees. + pub fn method_sync<H, T>(&self, t: T, handler: H) -> Method<M> + where H: Fn(&Message, &ObjectPath<M>, &Tree<M>) -> MethodResult + Send + Sync + 'static, T: Into<Member<'static>> { + Method::new(t.into(), M::box_method(handler)) + } +} + +#[test] +fn factory_test() { + let f = Factory::new_fn(); + f.interface("com.example.hello").deprecated(); + let b = 5i32; + f.method("GetSomething", |m,_,_| Ok(vec!({ let mut z = m.method_return(); z.append_items(&[b.into()]); z}))); + let t = f.tree().add(f.object_path("/funghi").add(f.interface("a.b.c").deprecated())); + let t = t.add(f.object_path("/ab")).add(f.object_path("/a")).add(f.object_path("/a/b/c")).add(f.object_path("/a/b")); + assert_eq!(t.children(t.paths.get(&Path::from("/a")).unwrap(), true).len(), 1); +} + +#[test] +fn test_sync_prop() { + let f = Factory::new_sync(); + let mut i = f.interface("com.example.echo"); + let p = i.add_p_ref(f.property("EchoCount", 7i32)); + let tree1 = Arc::new(f.tree().add(f.object_path("/echo").introspectable().add(i))); + let tree2 = tree1.clone(); + println!("{:#?}", tree2); + ::std::thread::spawn(move || { + let r = p.set_value(9i32.into()).unwrap(); + let signal = r.get(0).unwrap(); + assert_eq!(signal.msg_type(), MessageType::Signal); + let mut msg = Message::new_method_call("com.example.echoserver", "/echo", "com.example", "dummy").unwrap(); + super::message::message_set_serial(&mut msg, 3); + tree2.handle(&msg); + }); + + let mut msg = Message::new_method_call("com.example.echoserver", "/echo", "org.freedesktop.DBus.Properties", "Get").unwrap() + .append("com.example.echo").append("EchoCount"); + super::message::message_set_serial(&mut msg, 4); + let r = tree1.handle(&msg).unwrap(); + let r1 = r.get(0).unwrap(); + println!("{:?}", r1.get_items()); + let vv: super::arg::Variant<i32> = r1.get1().unwrap(); + assert!(vv.0 == 7 || vv.0 == 9); +} + +/* This test case no longer works, for unknown reason, see + https://github.com/diwic/dbus-rs/issues/27 + +#[test] +fn prop_lifetime_simple() { + let count; + let f = Factory::new_fnmut(); + count = Arc::new(f.property("changes", 0i32)); + let mut i = f.interface("com.example.dbus.rs").add_p_arc(count.clone()); + + let _setme = i.add_p_ref(f.property("setme", 0u8).access(Access::ReadWrite).on_set(|_,_,_| { + let v: i32 = count.get_value().inner().unwrap(); + count.set_value((v + 1).into()).unwrap(); + Ok(vec!()) + })); +} +*/ + +#[test] +fn prop_server() { + let setme: Arc<RefCell<Option<Arc<Property<_>>>>>; // Yikes! + setme = Arc::new(RefCell::new(None)); + let f = Factory::new_fnmut(); + let mut i = f.interface("com.example.dbus.rs"); + let count = i.add_p_ref(f.property("changes", 0i32)); + let count2 = count.clone(); + let setme2 = setme.clone(); + let setme3 = Arc::new(f.property("setme", 0u8).access(Access::ReadWrite).on_set(move |m,_,_| { + let ss2 = setme2.borrow(); + let ss = ss2.as_ref().unwrap(); + let s = try!(ss.verify_remote_set(m)); + let r = try!(ss.set_value(s).map_err(|_| MethodErr::ro_property(&ss.name))); + let v: i32 = count2.get_value().inner().unwrap(); + count2.set_value((v + 1).into()).unwrap(); + Ok(r) + })); + *setme.borrow_mut() = Some(setme3.clone()); + let i = i.add_p_arc(setme3); + + let tree = f.tree().add(f.object_path("/example").add(i)); + + let mut msg = Message::new_method_call("com.example.dbus.rs", "/example", "org.freedesktop.DBus.Properties", "Get").unwrap() + .append("com.example.dbus.rs").append("changes"); + super::message::message_set_serial(&mut msg, 10); + let r = tree.handle(&msg).unwrap(); + let r1 = r.get(0).unwrap(); + let ii = r1.get_items(); + let vv: &MessageItem = ii.get(0).unwrap().inner().unwrap(); + let v: i32 = vv.inner().unwrap(); + assert_eq!(v, 0); + + // Read-only + let mut msg = Message::new_method_call("com.example.dbus.rs", "/example", "org.freedesktop.DBus.Properties", "Set").unwrap() + .append("com.example.dbus.rs").append("changes").append(5i32); + super::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() + .append("com.example.dbus.rs").append("setme").append(8i32); + super::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() + .append("com.example.dbus.rs").append("setme").append(Box::new(9u8.into())); + super::message::message_set_serial(&mut msg, 30); + let mut r = tree.handle(&msg).unwrap(); + + println!("{:?}", r[0].as_result()); + + let c: i32 = count.get_value().inner().unwrap(); + assert_eq!(c, 1); + +} + + +#[test] +fn test_introspection() { + let f = Factory::new_sync(); + 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", 7i32)) + .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/objpath.rs b/third_party/rust/dbus/src/objpath.rs new file mode 100644 index 0000000000..86d2c3f01a --- /dev/null +++ b/third_party/rust/dbus/src/objpath.rs @@ -0,0 +1,553 @@ +#![allow(deprecated)] + +use super::{Connection, Message, MessageItem, Error, TypeSig}; +use std::collections::BTreeMap; +use std::rc::Rc; +use std::cell::{Cell, RefCell}; +use std::borrow::Cow; + +/// a Method has a list of Arguments. +pub struct Argument<'a> { + name: &'a str, + sig: TypeSig<'a>, +} + +impl<'a> Argument<'a> { + /// Create a new Argument. + pub fn new<T: Into<Cow<'a, str>>>(name: &'a str, sig: T) -> Argument<'a> { + Argument { name: name, sig: sig.into() } + } +} + +struct Annotation { + name: String, + value: String, +} + +struct ISignal<'a> { + args: Vec<Argument<'a>>, + anns: Vec<Annotation>, +} + +/// Declares that an Interface can send this signal +pub struct Signal<'a> { + name: String, + i: ISignal<'a>, +} + +impl<'a> Signal<'a> { + /// Create a new Signal. + pub fn new<N: ToString>(name: N, args: Vec<Argument<'a>>) -> Signal<'a> { + Signal { name: name.to_string(), i: ISignal { args: args, anns: vec![] } } + } + + /// Add an Annotation to the Signal. + pub fn annotate<N: ToString, V: ToString>(&mut self, name: N, value: V) { + self.i.anns.push(Annotation { name: name.to_string(), value: value.to_string() }); + } +} + +/// A method returns either a list of MessageItems, or an error - the tuple +/// represents the name and message of the Error. +pub type MethodResult = Result<Vec<MessageItem>, (&'static str, String)>; +/// Contains the retrieved MessageItem or an error tuple containing the +/// name and message of the error. +pub type PropertyGetResult = Result<MessageItem, (&'static str, String)>; +/// Contains () or an error tuple containing the name and message of +/// the error. +pub type PropertySetResult = Result<(), (&'static str, String)>; + +/// A boxed closure for dynamic dispatch. It is called when the method is +/// called by a remote application. +pub type MethodHandler<'a> = Box<FnMut(&mut Message) -> MethodResult + 'a>; + +struct IMethod<'a> { + in_args: Vec<Argument<'a>>, + out_args: Vec<Argument<'a>>, + cb: Rc<RefCell<MethodHandler<'a>>>, + anns: Vec<Annotation>, +} + +/// a method that can be called from another application +pub struct Method<'a> { + name: String, + i: IMethod<'a> +} + +impl<'a> Method<'a> { + /// Create a new Method. + #[deprecated(note="please use `tree` module instead")] + pub fn new<N: ToString>(name: N, in_args: Vec<Argument<'a>>, + out_args: Vec<Argument<'a>>, cb: MethodHandler<'a>) -> Method<'a> { + Method { name: name.to_string(), i: IMethod { + in_args: in_args, out_args: out_args, cb: Rc::new(RefCell::new(cb)), anns: vec![] } + } + } + + /// Add an Annotation to the Method. + pub fn annotate<N: ToString, V: ToString>(&mut self, name: N, value: V) { + self.i.anns.push(Annotation { name: name.to_string(), value: value.to_string() }); + } +} + +/// A read/write property handler. +pub trait PropertyRWHandler { + /// Get a property's value. + fn get(&self) -> PropertyGetResult; + /// Set a property's value. + fn set(&self, &MessageItem) -> PropertySetResult; +} + +/// A read-only property handler. +pub trait PropertyROHandler { + /// Get a property's value. + fn get(&self) -> PropertyGetResult; +} + +/// A write-only property handler. +pub trait PropertyWOHandler { + /// Set a property's value. + fn set(&self, &MessageItem) -> PropertySetResult; +} + +/// Types of access to a Property. +pub enum PropertyAccess<'a> { + RO(Box<PropertyROHandler+'a>), + RW(Box<PropertyRWHandler+'a>), + WO(Box<PropertyWOHandler+'a>), +} + +struct IProperty<'a> { + sig: TypeSig<'a>, + access: PropertyAccess<'a>, + anns: Vec<Annotation>, +} + +/// Properties that a remote application can get/set. +pub struct Property<'a> { + name: String, + i: IProperty<'a> +} + +impl<'a> Property<'a> { + fn new<N: ToString>(name: N, sig: TypeSig<'a>, a: PropertyAccess<'a>) -> Property<'a> { + Property { name: name.to_string(), i: IProperty { sig: sig, access: a, anns: vec![] } } + } + /// Creates a new read-only Property + pub fn new_ro<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyROHandler+'a>) -> Property<'a> { + Property::new(name, sig, PropertyAccess::RO(h)) + } + /// Creates a new read-write Property + pub fn new_rw<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyRWHandler+'a>) -> Property<'a> { + Property::new(name, sig, PropertyAccess::RW(h)) + } + /// Creates a new write-only Property + pub fn new_wo<N: ToString>(name: N, sig: TypeSig<'a>, h: Box<PropertyWOHandler+'a>) -> Property<'a> { + Property::new(name, sig, PropertyAccess::WO(h)) + } + /// Add an annotation to the Property + pub fn annotate<N: ToString, V: ToString>(&mut self, name: N, value: V) { + self.i.anns.push(Annotation { name: name.to_string(), value: value.to_string() }) + } +} + +/// Interfaces can contain Methods, Properties, and Signals. +pub struct Interface<'a> { + methods: BTreeMap<String, IMethod<'a>>, + properties: BTreeMap<String, IProperty<'a>>, + signals: BTreeMap<String, ISignal<'a>>, +} + +impl<'a> Interface<'a> { + /// Create a new Interface. + #[deprecated(note="please use `tree` module instead")] + pub fn new(m: Vec<Method<'a>>, p: Vec<Property<'a>>, s: Vec<Signal<'a>>) -> Interface<'a> { + Interface { + methods: m.into_iter().map(|m| (m.name, m.i)).collect(), + properties: p.into_iter().map(|p| (p.name, p.i)).collect(), + signals: s.into_iter().map(|s| (s.name, s.i)).collect(), + } + } +} + +struct IObjectPath<'a> { + conn: &'a Connection, + path: String, + registered: Cell<bool>, + interfaces: RefCell<BTreeMap<String, Interface<'a>>>, +} + +/// Represents a D-Bus object path, which can in turn contain Interfaces. +pub struct ObjectPath<'a> { + // We need extra references for the introspector and property handlers, hence this extra boxing + i: Rc<IObjectPath<'a>>, +} + +impl<'a> Drop for ObjectPath<'a> { + fn drop(&mut self) { + let _ = self.i.set_registered(false); + self.i.interfaces.borrow_mut().clear(); // This should remove all the other references to i + } +} + +fn introspect_args(args: &Vec<Argument>, indent: &str, dir: &str) -> String { + args.iter().fold("".to_string(), |aa, az| { + format!("{}{}<arg name=\"{}\" type=\"{}\"{}/>\n", aa, indent, az.name, az.sig, dir) + }) +} + +fn introspect_anns(anns: &Vec<Annotation>, indent: &str) -> String { + anns.iter().fold("".to_string(), |aa, az| { + format!("{}{}<annotation name=\"{}\" value=\"{}\"/>\n", aa, indent, az.name, az.value) + }) +} + +fn introspect_map<T, C: Fn(&T) -> (String, String)> + (h: &BTreeMap<String, T>, name: &str, indent: &str, func: C) -> String { + + h.iter().fold("".to_string(), |a, (k, v)| { + let (params, contents) = func(v); + format!("{}{}<{} name=\"{}\"{}{}>\n", + a, indent, name, k, params, if contents.len() > 0 { + format!(">\n{}{}</{}", contents, indent, name) + } + else { format!("/") } + ) + }) +} + +impl<'a> IObjectPath<'a> { + + fn set_registered(&self, register: bool) -> Result<(), Error> { + if register == self.registered.get() { return Ok(()) }; + if register { + try!(self.conn.register_object_path(&self.path)); + } else { + self.conn.unregister_object_path(&self.path); + } + self.registered.set(register); + Ok(()) + } + + fn introspect(&self, _: &mut Message) -> MethodResult { + let ifacestr = introspect_map(&self.interfaces.borrow(), "interface", " ", |iv| + (format!(""), format!("{}{}{}", + introspect_map(&iv.methods, "method", " ", |m| (format!(""), format!("{}{}{}", + introspect_args(&m.in_args, " ", " direction=\"in\""), + introspect_args(&m.out_args, " ", " direction=\"out\""), + introspect_anns(&m.anns, " ") + ))), + introspect_map(&iv.properties, "property", " ", |p| ( + format!(" type=\"{}\" access=\"{}\"", p.sig, match p.access { + PropertyAccess::RO(_) => "read", + PropertyAccess::RW(_) => "readwrite", + PropertyAccess::WO(_) => "write", + }), + introspect_anns(&p.anns, " ") + )), + introspect_map(&iv.signals, "signal", " ", |s| (format!(""), format!("{}{}", + introspect_args(&s.args, " ", ""), + introspect_anns(&s.anns, " ") + ))) + )) + ); + let childstr = self.conn.list_registered_object_paths(&self.path).iter().fold("".to_string(), |na, n| + format!(r##"{} <node name="{}"/> +"##, na, n) + ); + 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.path, ifacestr, childstr); + + Ok(vec!(MessageItem::Str(nodestr))) + } + + fn property_get(&self, msg: &mut Message) -> MethodResult { + let items = msg.get_items(); + let iface_name = try!(parse_msg_str(items.get(0))); + let prop_name = try!(parse_msg_str(items.get(1))); + + let is = self.interfaces.borrow(); + let i = try!(is.get(iface_name).ok_or_else(|| + ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name)))); + let p = try!(i.properties.get(prop_name).ok_or_else(|| + ("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", prop_name)))); + let v = try!(match p.access { + PropertyAccess::RO(ref cb) => cb.get(), + PropertyAccess::RW(ref cb) => cb.get(), + PropertyAccess::WO(_) => { + return Err(("org.freedesktop.DBus.Error.Failed", format!("Property {} is write only", prop_name))) + } + }); + Ok(vec!(MessageItem::Variant(Box::new(v)))) + } + + fn property_getall(&self, msg: &mut Message) -> MethodResult { + let items = msg.get_items(); + let iface_name = try!(parse_msg_str(items.get(0))); + + let is = self.interfaces.borrow(); + let i = try!(is.get(iface_name).ok_or_else(|| + ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name)))); + let mut result = Vec::new(); + result.push(try!(MessageItem::from_dict(i.properties.iter().filter_map(|(pname, pv)| { + let v = match pv.access { + PropertyAccess::RO(ref cb) => cb.get(), + PropertyAccess::RW(ref cb) => cb.get(), + PropertyAccess::WO(_) => { return None } + }; + Some(v.map(|vv| (pname.clone(),vv))) + })))); + Ok(result) + } + + fn property_set(&self, msg: &mut Message) -> MethodResult { + let items = msg.get_items(); + let iface_name = try!(parse_msg_str(items.get(0))); + let prop_name = try!(parse_msg_str(items.get(1))); + let value = try!(parse_msg_variant(items.get(2))); + + let is = self.interfaces.borrow(); + let i = try!(is.get(iface_name).ok_or_else(|| + ("org.freedesktop.DBus.Error.UnknownInterface", format!("Unknown interface {}", iface_name)))); + let p = try!(i.properties.get(prop_name).ok_or_else(|| + ("org.freedesktop.DBus.Error.UnknownProperty", format!("Unknown property {}", prop_name)))); + try!(match p.access { + PropertyAccess::WO(ref cb) => cb.set(value), + PropertyAccess::RW(ref cb) => cb.set(value), + PropertyAccess::RO(_) => { + return Err(("org.freedesktop.DBus.Error.PropertyReadOnly", format!("Property {} is read only", prop_name))) + } + }); + Ok(vec!()) + } +} + +fn parse_msg_str(a: Option<&MessageItem>) -> Result<&str,(&'static str, String)> { + let name = try!(a.ok_or_else(|| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a)))); + name.inner().map_err(|_| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a))) +} + +fn parse_msg_variant(a: Option<&MessageItem>) -> Result<&MessageItem,(&'static str, String)> { + let name = try!(a.ok_or_else(|| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a)))); + name.inner().map_err(|_| ("org.freedesktop.DBus.Error.InvalidArgs", format!("Invalid argument {:?}", a))) +} + +impl PropertyROHandler for MessageItem { + fn get(&self) -> PropertyGetResult { + Ok(self.clone()) + } +} + +impl<'a> ObjectPath<'a> { + /// Create a new ObjectPath. + #[deprecated(note="please use `tree` module instead")] + pub fn new(conn: &'a Connection, path: &str, introspectable: bool) -> ObjectPath<'a> { + let i = IObjectPath { + conn: conn, + path: path.to_string(), + registered: Cell::new(false), + interfaces: RefCell::new(BTreeMap::new()), + }; + let mut o = ObjectPath { i: Rc::new(i) }; + + if introspectable { + let o_cl = o.i.clone(); + let i = Interface::new(vec!( + Method::new("Introspect", vec!(), vec!(Argument::new("xml_data", "s")), + Box::new(move |m| { o_cl.introspect(m) }))), vec!(), vec!()); + o.insert_interface("org.freedesktop.DBus.Introspectable", i); + } + o + } + + fn add_property_handler(&mut self) { + if self.i.interfaces.borrow().contains_key("org.freedesktop.DBus.Properties") { return }; + let (cl1, cl2, cl3) = (self.i.clone(), self.i.clone(), self.i.clone()); + let i = Interface::new(vec!( + Method::new("Get", + vec!(Argument::new("interface_name", "s"), Argument::new("property_name", "s")), + vec!(Argument::new("value", "v")), + Box::new(move |m| cl1.property_get(m))), + Method::new("GetAll", + vec!(Argument::new("interface_name", "s")), + vec!(Argument::new("props", "a{sv}")), + Box::new(move |m| cl2.property_getall(m))), + Method::new("Set", + vec!(Argument::new("interface_name", "s"), Argument::new("property_name", "s"), + Argument::new("value", "v")), + vec!(), + Box::new(move |m| cl3.property_set(m)))), + vec!(), vec!()); + self.insert_interface("org.freedesktop.DBus.Properties", i); + } + + /// Add an Interface to this ObjectPath. + pub fn insert_interface<N: ToString>(&mut self, name: N, i: Interface<'a>) { + if !i.properties.is_empty() { + self.add_property_handler(); + } + self.i.interfaces.borrow_mut().insert(name.to_string(), i); + } + + /// Returns if the ObjectPath is registered. + pub fn is_registered(&self) -> bool { + self.i.registered.get() + } + + /// Changes the registration status of the ObjectPath. + pub fn set_registered(&mut self, register: bool) -> Result<(), Error> { + self.i.set_registered(register) + } + + /// Handles a method call if the object path matches. + /// Return value: None => not handled (no match), + /// Some(Err(())) => message reply send failed, + /// Some(Ok()) => message reply send ok */ + pub fn handle_message(&mut self, msg: &mut Message) -> Option<Result<(), ()>> { + let (_, path, iface, method) = msg.headers(); + if path.is_none() || path.unwrap() != self.i.path { return None; } + if iface.is_none() { return None; } + + let method = { + // This is because we don't want to hold the refcell lock when we call the + // callback - maximum flexibility for clients. + if let Some(i) = self.i.interfaces.borrow().get(&iface.unwrap()) { + if let Some(Some(m)) = method.map(|m| i.methods.get(&m)) { + m.cb.clone() + } else { + return Some(self.i.conn.send(Message::new_error( + msg, "org.freedesktop.DBus.Error.UnknownMethod", "Unknown method").unwrap()).map(|_| ())); + } + } else { + return Some(self.i.conn.send(Message::new_error(msg, + "org.freedesktop.DBus.Error.UnknownInterface", "Unknown interface").unwrap()).map(|_| ())); + } + }; + + let r = { + // Now call it + let mut m = method.borrow_mut(); + (&mut **m)(msg) + }; + + let reply = match r { + Ok(r) => { + let mut z = Message::new_method_return(msg).unwrap(); + z.append_items(&r); + z + }, + Err((aa,bb)) => Message::new_error(msg, aa, &bb).unwrap(), + }; + + Some(self.i.conn.send(reply).map(|_| ())) + } +} + +#[cfg(test)] +fn make_objpath<'a>(c: &'a Connection) -> ObjectPath<'a> { + let mut o = ObjectPath::new(c, "/echo", true); + o.insert_interface("com.example.echo", Interface::new( + vec!(Method::new("Echo", + vec!(Argument::new("request", "s")), + vec!(Argument::new("reply", "s")), Box::new(|_| { Err(("dummy", "dummy".to_string())) } ))), + vec!(Property::new_ro("EchoCount", MessageItem::Int32(7).type_sig(), Box::new(MessageItem::Int32(7)))), + vec!(Signal::new("Echoed", vec!(Argument::new("data", "s")))))); + o +} + +#[test] +fn test_objpath() { + let c = Connection::get_private(super::BusType::Session).unwrap(); + let mut o = make_objpath(&c); + o.set_registered(true).unwrap(); + let busname = format!("com.example.objpath.test.test_objpath"); + assert_eq!(c.register_name(&busname, super::NameFlag::ReplaceExisting as u32).unwrap(), super::RequestNameReply::PrimaryOwner); + + let thread = ::std::thread::spawn(move || { + let c = Connection::get_private(super::BusType::Session).unwrap(); + let pr = super::Props::new(&c, &*busname, "/echo", "com.example.echo", 5000); + assert_eq!(pr.get("EchoCount").unwrap(), 7i32.into()); + let m = pr.get_all().unwrap(); + assert_eq!(m.get("EchoCount").unwrap(), &7i32.into()); + }); + + let mut i = 0; + for n in c.iter(1000) { + println!("objpath msg {:?}", n); + if let super::ConnectionItem::MethodCall(mut m) = n { + if let Some(msg) = o.handle_message(&mut m) { + msg.unwrap(); + i += 1; + if i >= 2 { break }; + } + } + } + + thread.join().unwrap(); +} + + +/// Currently commented out because it requires feature(alloc) +/* +#[test] +fn test_refcount() { + let c = Connection::get_private(super::BusType::Session).unwrap(); + let i = { + let o = make_objpath(&c); + o.i.clone() + }; + assert!(::std::rc::is_unique(&i)); +} +*/ + +#[test] +fn test_introspect() { + let c = Connection::get_private(super::BusType::Session).unwrap(); + let mut o = make_objpath(&c); + o.set_registered(true).unwrap(); + let mut o2 = ObjectPath::new(&c, "/echo/subpath", true); + o2.set_registered(true).unwrap(); + let mut msg = Message::new_method_call("com.example.echoserver", "/echo", "org.freedesktop.DBus.Introspectable", "Introspect").unwrap(); + println!("Introspect result: {}", parse_msg_str(o.i.introspect(&mut msg).unwrap().get(0)).unwrap()); + + let 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"/> + </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!(result, parse_msg_str(o.i.introspect(&mut msg).unwrap().get(0)).unwrap()); + +} + diff --git a/third_party/rust/dbus/src/prop.rs b/third_party/rust/dbus/src/prop.rs new file mode 100644 index 0000000000..8c52872e1d --- /dev/null +++ b/third_party/rust/dbus/src/prop.rs @@ -0,0 +1,136 @@ +use super::{Connection, Message, MessageItem, Error, Path, Interface, BusName}; +use std::collections::BTreeMap; + +/// Client side properties - get and set properties on a remote application. +pub struct Props<'a> { + name: BusName<'a>, + path: Path<'a>, + interface: Interface<'a>, + timeout_ms: i32, + conn: &'a Connection, +} + +impl<'a> Props<'a> { + /// Create a new Props. + pub fn new<N, P, I>(conn: &'a Connection, name: N, path: P, interface: I, timeout_ms: i32) -> Props<'a> + where N: Into<BusName<'a>>, P: Into<Path<'a>>, I: Into<Interface<'a>> { + Props { + name: name.into(), + path: path.into(), + interface: interface.into(), + timeout_ms: timeout_ms, + conn: conn, + } + } + + /// Get a single property's value. + pub fn get(&self, propname: &str) -> Result<MessageItem, Error> { + let mut m = Message::method_call(&self.name, &self.path, + &"org.freedesktop.DBus.Properties".into(), &"Get".into()); + m.append_items(&[self.interface.to_string().into(), propname.to_string().into()]); + let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms)); + let reply = try!(r.as_result()).get_items(); + if reply.len() == 1 { + if let &MessageItem::Variant(ref v) = &reply[0] { + return Ok((**v).clone()) + } + } + let f = format!("Invalid reply for property get {}: '{:?}'", propname, reply); + return Err(Error::new_custom("InvalidReply", &f)); + } + + /// Set a single property's value. + pub fn set(&self, propname: &str, value: MessageItem) -> Result<(), Error> { + let mut m = Message::method_call(&self.name, &self.path, + &"org.freedesktop.DBus.Properties".into(), &"Set".into()); + m.append_items(&[self.interface.to_string().into(), propname.to_string().into(), Box::new(value).into()]); + let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms)); + try!(r.as_result()); + Ok(()) + } + + /// Get a map of all the properties' names and their values. + pub fn get_all(&self) -> Result<BTreeMap<String, MessageItem>, Error> { + let mut m = Message::method_call(&self.name, &self.path, + &"org.freedesktop.DBus.Properties".into(), &"GetAll".into()); + m.append_items(&[self.interface.to_string().into()]); + let mut r = try!(self.conn.send_with_reply_and_block(m, self.timeout_ms)); + let reply = try!(r.as_result()).get_items(); + + (|| { + if reply.len() != 1 { return Err(()) }; + let mut t = BTreeMap::new(); + let a: &[MessageItem] = try!(reply[0].inner()); + for p in a.iter() { + let (k, v) = try!(p.inner()); + let (k, v): (&String, &MessageItem) = (try!(k.inner()), try!(v.inner())); + t.insert(k.clone(), v.clone()); + } + Ok(t) + })().map_err(|_| { + let f = format!("Invalid reply for property GetAll: '{:?}'", reply); + Error::new_custom("InvalidReply", &f) + }) + } +} + +/// Wrapper around Props that keeps a map of fetched properties. +pub struct PropHandler<'a> { + p: Props<'a>, + map: BTreeMap<String, MessageItem>, +} + +impl<'a> PropHandler<'a> { + /// Create a new PropHandler from a Props. + pub fn new(p: Props) -> PropHandler { + PropHandler { p: p, map: BTreeMap::new() } + } + + /// Get a map of all the properties' names and their values. + pub fn get_all(&mut self) -> Result<(), Error> { + self.map = try!(self.p.get_all()); + Ok(()) + } + + /// Get a mutable reference to the PropHandler's fetched properties. + pub fn map_mut(&mut self) -> &mut BTreeMap<String, MessageItem> { &mut self.map } + + /// Get a reference to the PropHandler's fetched properties. + pub fn map(&self) -> &BTreeMap<String, MessageItem> { &self.map } + + /// Get a single property's value. + pub fn get(&mut self, propname: &str) -> Result<&MessageItem, Error> { + let v = try!(self.p.get(propname)); + self.map.insert(propname.to_string(), v); + Ok(self.map.get(propname).unwrap()) + } + + /// Set a single property's value. + pub fn set(&mut self, propname: &str, value: MessageItem) -> Result<(), Error> { + try!(self.p.set(propname, value.clone())); + self.map.insert(propname.to_string(), value); + Ok(()) + } +} + + +/* Unfortunately org.freedesktop.DBus has no properties we can use for testing, but PolicyKit should be around on most distros. */ +#[test] +fn test_get_policykit_version() { + use super::BusType; + let c = Connection::get_private(BusType::System).unwrap(); + let p = Props::new(&c, "org.freedesktop.PolicyKit1", "/org/freedesktop/PolicyKit1/Authority", + "org.freedesktop.PolicyKit1.Authority", 10000); + + /* Let's use both the get and getall methods and see if we get the same result */ + let v = p.get("BackendVersion").unwrap(); + let vall = p.get_all().unwrap(); + let v2 = vall.get("BackendVersion").unwrap(); + + assert_eq!(&v, &*v2); + match v { + MessageItem::Str(ref s) => { println!("Policykit Backend version is {}", s); } + _ => { panic!("Invalid Get: {:?}", v); } + }; +} + diff --git a/third_party/rust/dbus/src/signalargs.rs b/third_party/rust/dbus/src/signalargs.rs new file mode 100644 index 0000000000..6eb92950cb --- /dev/null +++ b/third_party/rust/dbus/src/signalargs.rs @@ -0,0 +1,107 @@ +use arg; +use {Message, MessageType, BusName, Path, Interface, Member, MatchRule}; + +/// Helper methods for structs representing a Signal +/// +/// # Example +/// +/// Listen to InterfacesRemoved signal from org.bluez.obex. +/// +/// ```rust,no_run +/// use dbus::{Connection, BusType, SignalArgs}; +/// use dbus::stdintf::org_freedesktop_dbus::ObjectManagerInterfacesRemoved as IR; +/// +/// let c = Connection::get_private(BusType::Session).unwrap(); +/// // Add a match for this signal +/// let mstr = IR::match_str(Some(&"org.bluez.obex".into()), None); +/// c.add_match(&mstr).unwrap(); +/// +/// // Wait for the signal to arrive. +/// for msg in c.incoming(1000) { +/// if let Some(ir) = IR::from_message(&msg) { +/// println!("Interfaces {:?} have been removed from bluez on path {}.", ir.interfaces, ir.object); +/// } +/// } +/// ``` + +pub trait SignalArgs: Default { + /// D-Bus name of signal + const NAME: &'static str; + + /// D-Bus name of interface this signal belongs to + const INTERFACE: &'static str; + + /// Low-level method for appending this struct to a message. + /// + /// You're more likely to use one of the more high level functions. + fn append(&self, i: &mut arg::IterAppend); + + /// Low-level method for getting arguments from a message. + /// + /// You're more likely to use one of the more high level functions. + fn get(&mut self, i: &mut arg::Iter) -> Result<(), arg::TypeMismatchError>; + + /// Returns a message that emits the signal. + fn to_emit_message(&self, path: &Path) -> Message { + let mut m = Message::signal(path, &Interface::from(Self::INTERFACE), &Member::from(Self::NAME)); + self.append(&mut arg::IterAppend::new(&mut m)); + m + } + + /// If the message is a signal of the correct type, return its arguments, otherwise return None. + /// + /// This does not check sender and path of the message, which is likely relevant to you as well. + fn from_message(m: &Message) -> Option<Self> { + if m.msg_type() != MessageType::Signal { None } + else if m.interface().as_ref().map(|x| &**x) != Some(Self::INTERFACE) { None } + else if m.member().as_ref().map(|x| &**x) != Some(Self::NAME) { None } + else { + let mut z: Self = Default::default(); + z.get(&mut m.iter_init()).ok().map(|_| z) + } + } + + /// Returns a match rule matching this signal. + /// + /// If sender and/or path is None, matches all senders and/or paths. + fn match_rule<'a>(sender: Option<&'a BusName>, path: Option<&'a Path>) -> MatchRule<'a> { + let mut m: MatchRule = Default::default(); + m.sender = sender.map(|x| x.clone()); + m.path = path.map(|x| x.clone()); + m.msg_type = Some(MessageType::Signal); + m.interface = Some(Self::INTERFACE.into()); + m.member = Some(Self::NAME.into()); + m + } + + + /// Returns a string that can be sent to `Connection::add_match`. + /// + /// If sender and/or path is None, matches all senders and/or paths. + fn match_str(sender: Option<&BusName>, path: Option<&Path>) -> String { + Self::match_rule(sender, path).match_str() + } +} + +#[test] +fn intf_removed() { + use {Connection, BusType}; + use stdintf::org_freedesktop_dbus::ObjectManagerInterfacesRemoved as IR; + let c = Connection::get_private(BusType::Session).unwrap(); + let mstr = IR::match_str(Some(&c.unique_name().into()), Some(&"/hello".into())); + println!("Match str: {}", mstr); + c.add_match(&mstr).unwrap(); + let ir = IR { object: "/hello".into(), interfaces: vec!("ABC.DEF".into(), "GHI.JKL".into()) }; + + let cp = c.with_path("dbus.dummy", "/hello", 2000); + cp.emit(&ir).unwrap(); + + for msg in c.incoming(1000) { + if &*msg.sender().unwrap() != &*c.unique_name() { continue; } + if let Some(ir2) = IR::from_message(&msg) { + assert_eq!(ir2.object, ir.object); + assert_eq!(ir2.interfaces, ir.interfaces); + break; + } + } +} diff --git a/third_party/rust/dbus/src/stdintf.rs b/third_party/rust/dbus/src/stdintf.rs new file mode 100644 index 0000000000..7a35c7fc8e --- /dev/null +++ b/third_party/rust/dbus/src/stdintf.rs @@ -0,0 +1,212 @@ +//! This module contains some standard interfaces and an easy way to call them. +//! +//! See the [D-Bus specification](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces) for more information about these standard interfaces. +//! +//! The code here was originally created by dbus-codegen. +//! +//! # Example +//! ``` +//! use dbus::{Connection, BusType}; +//! use dbus::stdintf::org_freedesktop_dbus::Introspectable; +//! let c = Connection::get_private(BusType::Session).unwrap(); +//! let p = c.with_path("org.freedesktop.DBus", "/", 10000); +//! println!("Introspection XML: {}", p.introspect().unwrap()); +//! ``` +//! + +#![allow(missing_docs)] + +pub use self::org_freedesktop_dbus::Peer as OrgFreedesktopDBusPeer; + +pub use self::org_freedesktop_dbus::Introspectable as OrgFreedesktopDBusIntrospectable; + +pub use self::org_freedesktop_dbus::Properties as OrgFreedesktopDBusProperties; + +pub use self::org_freedesktop_dbus::ObjectManager as OrgFreedesktopDBusObjectManager; + +pub mod org_freedesktop_dbus { + +use arg; + +/// Method of the [org.freedesktop.DBus.Introspectable](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-introspectable) interface. +pub trait Introspectable { + type Err; + fn introspect(&self) -> Result<String, Self::Err>; +} + +impl<'a, C: ::std::ops::Deref<Target=::Connection>> Introspectable for ::ConnPath<'a, C> { + type Err = ::Error; + + fn introspect(&self) -> Result<String, Self::Err> { + let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.Introspectable".into(), &"Introspect".into(), |_| { + })); + try!(m.as_result()); + let mut i = m.iter_init(); + let xml: String = try!(i.read()); + Ok(xml) + } +} + +/// Methods of the [org.freedesktop.DBus.Properties](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties) interface. +pub trait Properties { + type Err; + fn get<R0: for<'b> arg::Get<'b>>(&self, interface_name: &str, property_name: &str) -> Result<R0, Self::Err>; + fn get_all(&self, interface_name: &str) -> Result<::std::collections::HashMap<String, arg::Variant<Box<arg::RefArg>>>, Self::Err>; + fn set<I2: arg::Arg + arg::Append>(&self, interface_name: &str, property_name: &str, value: I2) -> Result<(), Self::Err>; +} + +impl<'a, C: ::std::ops::Deref<Target=::Connection>> Properties for ::ConnPath<'a, C> { + type Err = ::Error; + + fn get<R0: for<'b> arg::Get<'b>>(&self, interface_name: &str, property_name: &str) -> Result<R0, Self::Err> { + let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.Properties".into(), &"Get".into(), |msg| { + let mut i = arg::IterAppend::new(msg); + i.append(interface_name); + i.append(property_name); + })); + try!(m.as_result()); + let mut i = m.iter_init(); + let value: arg::Variant<R0> = try!(i.read()); + Ok(value.0) + } + + fn get_all(&self, interface_name: &str) -> Result<::std::collections::HashMap<String, arg::Variant<Box<arg::RefArg>>>, Self::Err> { + let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.Properties".into(), &"GetAll".into(), |msg| { + let mut i = arg::IterAppend::new(msg); + i.append(interface_name); + })); + try!(m.as_result()); + let mut i = m.iter_init(); + let properties: ::std::collections::HashMap<String, arg::Variant<Box<arg::RefArg>>> = try!(i.read()); + Ok(properties) + } + + fn set<I2: arg::Arg + arg::Append>(&self, interface_name: &str, property_name: &str, value: I2) -> Result<(), Self::Err> { + let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.Properties".into(), &"Set".into(), |msg| { + let mut i = arg::IterAppend::new(msg); + i.append(interface_name); + i.append(property_name); + i.append(arg::Variant(value)); + })); + try!(m.as_result()); + Ok(()) + } +} + +#[derive(Debug, Default)] +/// Struct to send/receive the PropertiesChanged signal of the +/// [org.freedesktop.DBus.Properties](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties) interface. +pub struct PropertiesPropertiesChanged { + pub interface_name: String, + pub changed_properties: ::std::collections::HashMap<String, arg::Variant<Box<arg::RefArg>>>, + pub invalidated_properties: Vec<String>, +} + +impl ::SignalArgs for PropertiesPropertiesChanged { + const NAME: &'static str = "PropertiesChanged"; + const INTERFACE: &'static str = "org.freedesktop.DBus.Properties"; + fn append(&self, i: &mut arg::IterAppend) { + (&self.interface_name as &arg::RefArg).append(i); + (&self.changed_properties as &arg::RefArg).append(i); + (&self.invalidated_properties as &arg::RefArg).append(i); + } + fn get(&mut self, i: &mut arg::Iter) -> Result<(), arg::TypeMismatchError> { + self.interface_name = try!(i.read()); + self.changed_properties = try!(i.read()); + self.invalidated_properties = try!(i.read()); + Ok(()) + } +} + +/// Method of the [org.freedesktop.DBus.ObjectManager](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager) interface. +pub trait ObjectManager { + type Err; + fn get_managed_objects(&self) -> Result<::std::collections::HashMap<::Path<'static>, ::std::collections::HashMap<String, ::std::collections::HashMap<String, arg::Variant<Box<arg::RefArg>>>>>, Self::Err>; +} + +impl<'a, C: ::std::ops::Deref<Target=::Connection>> ObjectManager for ::ConnPath<'a, C> { + type Err = ::Error; + + fn get_managed_objects(&self) -> Result<::std::collections::HashMap<::Path<'static>, ::std::collections::HashMap<String, ::std::collections::HashMap<String, arg::Variant<Box<arg::RefArg>>>>>, Self::Err> { + let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.ObjectManager".into(), &"GetManagedObjects".into(), |_| { + })); + try!(m.as_result()); + let mut i = m.iter_init(); + let objects: ::std::collections::HashMap<::Path<'static>, ::std::collections::HashMap<String, ::std::collections::HashMap<String, arg::Variant<Box<arg::RefArg>>>>> = try!(i.read()); + Ok(objects) + } +} + +#[derive(Debug, Default)] +/// Struct to send/receive the InterfacesAdded signal of the +/// [org.freedesktop.DBus.ObjectManager](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager) interface. +pub struct ObjectManagerInterfacesAdded { + pub object: ::Path<'static>, + pub interfaces: ::std::collections::HashMap<String, ::std::collections::HashMap<String, arg::Variant<Box<arg::RefArg>>>>, +} + +impl ::SignalArgs for ObjectManagerInterfacesAdded { + const NAME: &'static str = "InterfacesAdded"; + const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; + fn append(&self, i: &mut arg::IterAppend) { + (&self.object as &arg::RefArg).append(i); + (&self.interfaces as &arg::RefArg).append(i); + } + fn get(&mut self, i: &mut arg::Iter) -> Result<(), arg::TypeMismatchError> { + self.object = try!(i.read()); + self.interfaces = try!(i.read()); + Ok(()) + } +} + +#[derive(Debug, Default)] +/// Struct to send/receive the InterfacesRemoved signal of the +/// [org.freedesktop.DBus.ObjectManager](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager) interface. +pub struct ObjectManagerInterfacesRemoved { + pub object: ::Path<'static>, + pub interfaces: Vec<String>, +} + +impl ::SignalArgs for ObjectManagerInterfacesRemoved { + const NAME: &'static str = "InterfacesRemoved"; + const INTERFACE: &'static str = "org.freedesktop.DBus.ObjectManager"; + fn append(&self, i: &mut arg::IterAppend) { + (&self.object as &arg::RefArg).append(i); + (&self.interfaces as &arg::RefArg).append(i); + } + fn get(&mut self, i: &mut arg::Iter) -> Result<(), arg::TypeMismatchError> { + self.object = try!(i.read()); + self.interfaces = try!(i.read()); + Ok(()) + } +} + +/// Methods of the [org.freedesktop.DBus.Peer](https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-peer) interface. +pub trait Peer { + type Err; + fn ping(&self) -> Result<(), Self::Err>; + fn get_machine_id(&self) -> Result<String, Self::Err>; +} + +impl<'a, C: ::std::ops::Deref<Target=::Connection>> Peer for ::ConnPath<'a, C> { + type Err = ::Error; + + fn ping(&self) -> Result<(), Self::Err> { + let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.Peer".into(), &"Ping".into(), |_| { + })); + try!(m.as_result()); + Ok(()) + } + + fn get_machine_id(&self) -> Result<String, Self::Err> { + let mut m = try!(self.method_call_with_args(&"org.freedesktop.DBus.Peer".into(), &"GetMachineId".into(), |_| { + })); + try!(m.as_result()); + let mut i = m.iter_init(); + let machine_uuid: String = try!(i.read()); + Ok(machine_uuid) + } +} + + +} diff --git a/third_party/rust/dbus/src/strings.rs b/third_party/rust/dbus/src/strings.rs new file mode 100644 index 0000000000..8bd077d08e --- /dev/null +++ b/third_party/rust/dbus/src/strings.rs @@ -0,0 +1,216 @@ +// CString wrappers. + +use std::{str, fmt, ops, default, hash}; +use std::ffi::{CStr, CString}; +use std::borrow::{Borrow, Cow}; +use std::os::raw::c_char; + +#[cfg(not(feature = "no-string-validation"))] +use Error; +#[cfg(not(feature = "no-string-validation"))] +use ffi; + +macro_rules! cstring_wrapper { + ($t: ident, $s: ident) => { + +impl<'m> $t<'m> { + #[cfg(feature = "no-string-validation")] + fn check_valid(_: *const c_char) -> Result<(), String> { Ok(()) } + + #[cfg(not(feature = "no-string-validation"))] + fn check_valid(c: *const c_char) -> Result<(), String> { + let mut e = Error::empty(); + let b = unsafe { ffi::$s(c, e.get_mut()) }; + if b != 0 { Ok(()) } else { Err(e.message().unwrap().into()) } + } + + /// Creates a new instance of this struct. + /// + /// Note: If the no-string-validation feature is activated, this string + /// will not be checked for conformance with the D-Bus specification. + pub fn new<S: Into<Vec<u8>>>(s: S) -> Result<$t<'m>, String> { + let c = try!(CString::new(s).map_err(|e| e.to_string())); + $t::check_valid(c.as_ptr()).map(|_| $t(Cow::Owned(c))) + } + + /// Creates a new instance of this struct. If you end it with \0, + /// it can borrow the slice without extra allocation. + /// + /// Note: If the no-string-validation feature is activated, this string + /// will not be checked for conformance with the D-Bus specification. + pub fn from_slice(s: &'m [u8]) -> Result<$t<'m>, String> { + if s.len() == 0 || s[s.len()-1] != 0 { return $t::new(s) }; + $t::check_valid(s.as_ptr() as *const c_char).map(|_| { + let c = unsafe { CStr::from_ptr(s.as_ptr() as *const c_char) }; + $t(Cow::Borrowed(c)) + }) + } + + /// This function creates a new instance of this struct, without checking. + /// It's up to you to guarantee that s ends with a \0 and is valid. + pub unsafe fn from_slice_unchecked(s: &'m [u8]) -> $t<'m> { + debug_assert!(s[s.len()-1] == 0); + $t(Cow::Borrowed(CStr::from_ptr(s.as_ptr() as *const c_char))) + } + + /// View this struct as a CStr. + pub fn as_cstr(&self) -> &CStr { &self.0 } + + /// Makes sure this string does not contain borrows. + pub fn into_static(self) -> $t<'static> { + $t(Cow::Owned(self.0.into_owned())) + } + + /// Converts this struct to a CString. + pub fn into_cstring(self) -> CString { self.0.into_owned() } +} + +/* +/// #Panics +/// +/// If given string is not valid. +/// impl<S: Into<Vec<u8>>> From<S> for $t { fn from(s: S) -> $t { $t::new(s).unwrap() } } +*/ + +/// #Panics +/// +/// If given string is not valid. +impl<'m> From<String> for $t<'m> { fn from(s: String) -> $t<'m> { $t::new(s).unwrap() } } + +/// #Panics +/// +/// If given string is not valid. +impl<'m> From<&'m String> for $t<'m> { fn from(s: &'m String) -> $t<'m> { $t::from_slice(s.as_bytes()).unwrap() } } + +/// #Panics +/// +/// If given string is not valid. +impl<'m> From<&'m str> for $t<'m> { fn from(s: &'m str) -> $t<'m> { $t::from_slice(s.as_bytes()).unwrap() } } + +impl<'m> From<$t<'m>> for CString { fn from(s: $t<'m>) -> CString { s.0.into_owned() } } + + +/// #Panics +/// +/// If given string is not valid. +impl<'m> From<Cow<'m, str>> for $t<'m> { + fn from(s: Cow<'m, str>) -> $t<'m> { + match s { + Cow::Borrowed(z) => z.into(), + Cow::Owned(z) => z.into(), + } + } +} + +impl<'inner, 'm: 'inner> From<&'m $t<'inner>> for $t<'m> { + fn from(borrow: &'m $t<'inner>) -> $t<'m> { + $t(Cow::Borrowed(borrow.0.borrow())) + } +} + +impl<'m> ops::Deref for $t<'m> { + type Target = str; + fn deref(&self) -> &str { str::from_utf8(self.0.to_bytes()).unwrap() } +} + +impl<'m> fmt::Display for $t<'m> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let s: &str = &self; + (&s as &fmt::Display).fmt(f) + } +} + +impl<'m> AsRef<CStr> for $t<'m> { + fn as_ref(&self) -> &CStr { &self.0 } +} + +impl<'m> hash::Hash for $t<'m> { + fn hash<H: hash::Hasher>(&self, state: &mut H) { + self.0.hash(state); + } +} + +}} + +/// A wrapper around a string that is guaranteed to be +/// a valid (single) D-Bus type signature. Supersedes TypeSig. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct Signature<'a>(Cow<'a, CStr>); + +cstring_wrapper!(Signature, dbus_signature_validate_single); + +impl Signature<'static> { + /// Makes a D-Bus signature that corresponds to A. + pub fn make<A: super::arg::Arg>() -> Signature<'static> { A::signature() } +} + +/// A wrapper around a string that is guaranteed to be +/// a valid D-Bus object path. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct Path<'a>(Cow<'a, CStr>); + +cstring_wrapper!(Path, dbus_validate_path); + +// This is needed so one can make arrays of paths easily +impl<'a> default::Default for Path<'a> { + fn default() -> Path<'a> { Path(Cow::Borrowed(unsafe { CStr::from_ptr(b"/\0".as_ptr() as *const c_char)})) } +} + +/// A wrapper around a string that is guaranteed to be +/// a valid D-Bus member, i e, a signal or method name. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct Member<'a>(Cow<'a, CStr>); + +cstring_wrapper!(Member, dbus_validate_member); + +/// A wrapper around a string that is guaranteed to be +/// a valid D-Bus interface name. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct Interface<'a>(Cow<'a, CStr>); + +cstring_wrapper!(Interface, dbus_validate_interface); + +/// A wrapper around a string that is guaranteed to be +/// a valid D-Bus bus name. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct BusName<'a>(Cow<'a, CStr>); + +cstring_wrapper!(BusName, dbus_validate_bus_name); + +/// A wrapper around a string that is guaranteed to be +/// a valid D-Bus bus name. +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +pub struct ErrorName<'a>(Cow<'a, CStr>); + +cstring_wrapper!(ErrorName, dbus_validate_error_name); + +#[test] +fn some_path() { + use std::os::raw::c_char; + let p1: Path = "/valid".into(); + let p2 = Path::new("##invalid##"); + assert_eq!(p1, Path(Cow::Borrowed(unsafe { CStr::from_ptr(b"/valid\0".as_ptr() as *const c_char) }))); + #[cfg(not(feature = "no-string-validation"))] + assert_eq!(p2, Err("Object path was not valid: '##invalid##'".into())); + #[cfg(feature = "no-string-validation")] + assert_eq!(p2, Ok(Path(Cow::Borrowed(unsafe { CStr::from_ptr(b"##invalid##\0".as_ptr() as *const c_char) })))); +} + +#[test] +fn reborrow_path() { + let p1 = Path::from("/valid"); + let p2 = p1.clone(); + { + let p2_borrow: &Path = &p2; + let p3 = Path::from(p2_borrow); + // Check path created from borrow + assert_eq!(p2, p3); + } + // Check path that was previously borrowed + assert_eq!(p1, p2); +} + +#[test] +fn make_sig() { + assert_eq!(&*Signature::make::<(&str, u8)>(), "(sy)"); +} 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; +} + diff --git a/third_party/rust/dbus/src/watch.rs b/third_party/rust/dbus/src/watch.rs new file mode 100644 index 0000000000..e1959f1085 --- /dev/null +++ b/third_party/rust/dbus/src/watch.rs @@ -0,0 +1,255 @@ +use ffi; +use libc; +use super::Connection; + +use std::mem; +use std::sync::{Mutex, RwLock}; +use std::os::unix::io::{RawFd, AsRawFd}; +use std::os::raw::{c_void, c_uint}; + +/// A file descriptor to watch for incoming events (for async I/O). +/// +/// # Example +/// ``` +/// extern crate libc; +/// extern crate dbus; +/// fn main() { +/// use dbus::{Connection, BusType, WatchEvent}; +/// let c = Connection::get_private(BusType::Session).unwrap(); +/// +/// // Get a list of fds to poll for +/// let mut fds: Vec<_> = c.watch_fds().iter().map(|w| w.to_pollfd()).collect(); +/// +/// // Poll them with a 1 s timeout +/// let r = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::c_ulong, 1000) }; +/// assert!(r >= 0); +/// +/// // And handle incoming events +/// for pfd in fds.iter().filter(|pfd| pfd.revents != 0) { +/// for item in c.watch_handle(pfd.fd, WatchEvent::from_revents(pfd.revents)) { +/// // Handle item +/// println!("Received ConnectionItem: {:?}", item); +/// } +/// } +/// } +/// ``` + +#[repr(C)] +#[derive(Debug, PartialEq, Copy, Clone)] +/// The enum is here for backwards compatibility mostly. +/// +/// It should really be bitflags instead. +pub enum WatchEvent { + /// The fd is readable + Readable = ffi::DBUS_WATCH_READABLE as isize, + /// The fd is writable + Writable = ffi::DBUS_WATCH_WRITABLE as isize, + /// An error occured on the fd + Error = ffi::DBUS_WATCH_ERROR as isize, + /// The fd received a hangup. + Hangup = ffi::DBUS_WATCH_HANGUP as isize, +} + +impl WatchEvent { + /// After running poll, this transforms the revents into a parameter you can send into `Connection::watch_handle` + pub fn from_revents(revents: libc::c_short) -> c_uint { + 0 + + if (revents & libc::POLLIN) != 0 { WatchEvent::Readable as c_uint } else { 0 } + + if (revents & libc::POLLOUT) != 0 { WatchEvent::Writable as c_uint } else { 0 } + + if (revents & libc::POLLERR) != 0 { WatchEvent::Error as c_uint } else { 0 } + + if (revents & libc::POLLHUP) != 0 { WatchEvent::Hangup as c_uint } else { 0 } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +/// A file descriptor, and an indication whether it should be read from, written to, or both. +pub struct Watch { + fd: RawFd, + read: bool, + write: bool, +} + +impl Watch { + /// Get the RawFd this Watch is for + pub fn fd(&self) -> RawFd { self.fd } + /// Add POLLIN to events to listen for + pub fn readable(&self) -> bool { self.read } + /// Add POLLOUT to events to listen for + pub fn writable(&self) -> bool { self.write } + /// Returns the current watch as a libc::pollfd, to use with libc::poll + pub fn to_pollfd(&self) -> libc::pollfd { + libc::pollfd { fd: self.fd, revents: 0, events: libc::POLLERR + libc::POLLHUP + + if self.readable() { libc::POLLIN } else { 0 } + + if self.writable() { libc::POLLOUT } else { 0 }, + } + } +/* + pub (crate) unsafe fn from_raw(watch: *mut ffi::DBusWatch) -> Self { + let mut w = Watch { fd: ffi::dbus_watch_get_unix_fd(watch), read: false, write: false}; + let enabled = ffi::dbus_watch_get_enabled(watch) != 0; + if enabled { + let flags = ffi::dbus_watch_get_flags(watch); + w.read = (flags & WatchEvent::Readable as c_uint) != 0; + w.write = (flags & WatchEvent::Writable as c_uint) != 0; + } + w + } +*/ +} + +impl AsRawFd for Watch { + fn as_raw_fd(&self) -> RawFd { self.fd } +} + +/// Note - internal struct, not to be used outside API. Moving it outside its box will break things. +pub struct WatchList { + watches: RwLock<Vec<*mut ffi::DBusWatch>>, + enabled_fds: Mutex<Vec<Watch>>, + on_update: Mutex<Box<Fn(Watch) + Send>>, +} + +impl WatchList { + pub fn new(c: &Connection, on_update: Box<Fn(Watch) + Send>) -> Box<WatchList> { + let w = Box::new(WatchList { on_update: Mutex::new(on_update), watches: RwLock::new(vec!()), enabled_fds: Mutex::new(vec!()) }); + if unsafe { ffi::dbus_connection_set_watch_functions(super::connection::conn_handle(c), + Some(add_watch_cb), Some(remove_watch_cb), Some(toggled_watch_cb), &*w as *const _ as *mut _, None) } == 0 { + panic!("dbus_connection_set_watch_functions failed"); + } + w + } + + pub fn set_on_update(&self, on_update: Box<Fn(Watch) + Send>) { *self.on_update.lock().unwrap() = on_update; } + + pub fn watch_handle(&self, fd: RawFd, flags: c_uint) { + // println!("watch_handle {} flags {}", fd, flags); + for &q in self.watches.read().unwrap().iter() { + let w = self.get_watch(q); + if w.fd != fd { continue }; + if unsafe { ffi::dbus_watch_handle(q, flags) } == 0 { + panic!("dbus_watch_handle failed"); + } + self.update(q); + }; + } + + pub fn get_enabled_fds(&self) -> Vec<Watch> { + self.enabled_fds.lock().unwrap().clone() + } + + fn get_watch(&self, watch: *mut ffi::DBusWatch) -> Watch { + let mut w = Watch { fd: unsafe { ffi::dbus_watch_get_unix_fd(watch) }, read: false, write: false}; + let enabled = self.watches.read().unwrap().contains(&watch) && unsafe { ffi::dbus_watch_get_enabled(watch) != 0 }; + let flags = unsafe { ffi::dbus_watch_get_flags(watch) }; + if enabled { + w.read = (flags & WatchEvent::Readable as c_uint) != 0; + w.write = (flags & WatchEvent::Writable as c_uint) != 0; + } + // println!("Get watch fd {:?} ptr {:?} enabled {:?} flags {:?}", w, watch, enabled, flags); + w + } + + fn update(&self, watch: *mut ffi::DBusWatch) { + let mut w = self.get_watch(watch); + + for &q in self.watches.read().unwrap().iter() { + if q == watch { continue }; + let ww = self.get_watch(q); + if ww.fd != w.fd { continue }; + w.read |= ww.read; + w.write |= ww.write; + } + // println!("Updated sum: {:?}", w); + + { + let mut fdarr = self.enabled_fds.lock().unwrap(); + + if w.write || w.read { + if fdarr.contains(&w) { return; } // Nothing changed + } + else if !fdarr.iter().any(|q| w.fd == q.fd) { return; } // Nothing changed + + fdarr.retain(|f| f.fd != w.fd); + if w.write || w.read { fdarr.push(w) }; + } + let func = self.on_update.lock().unwrap(); + (*func)(w); + } +} + +extern "C" fn add_watch_cb(watch: *mut ffi::DBusWatch, data: *mut c_void) -> u32 { + let wlist: &WatchList = unsafe { mem::transmute(data) }; + // println!("Add watch {:?}", watch); + wlist.watches.write().unwrap().push(watch); + wlist.update(watch); + 1 +} + +extern "C" fn remove_watch_cb(watch: *mut ffi::DBusWatch, data: *mut c_void) { + let wlist: &WatchList = unsafe { mem::transmute(data) }; + // println!("Removed watch {:?}", watch); + wlist.watches.write().unwrap().retain(|w| *w != watch); + wlist.update(watch); +} + +extern "C" fn toggled_watch_cb(watch: *mut ffi::DBusWatch, data: *mut c_void) { + let wlist: &WatchList = unsafe { mem::transmute(data) }; + // println!("Toggled watch {:?}", watch); + wlist.update(watch); +} + +#[cfg(test)] +mod test { + use libc; + use super::super::{Connection, Message, BusType, WatchEvent, ConnectionItem, MessageType}; + + #[test] + fn async() { + let c = Connection::get_private(BusType::Session).unwrap(); + c.register_object_path("/test").unwrap(); + let m = Message::new_method_call(&c.unique_name(), "/test", "com.example.asynctest", "AsyncTest").unwrap(); + let serial = c.send(m).unwrap(); + println!("Async: sent serial {}", serial); + + let mut fds: Vec<_> = c.watch_fds().iter().map(|w| w.to_pollfd()).collect(); + let mut new_fds = None; + let mut i = 0; + let mut success = false; + while !success { + i += 1; + if let Some(q) = new_fds { fds = q; new_fds = None }; + + for f in fds.iter_mut() { f.revents = 0 }; + + assert!(unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::nfds_t, 1000) } > 0); + + for f in fds.iter().filter(|pfd| pfd.revents != 0) { + let m = WatchEvent::from_revents(f.revents); + println!("Async: fd {}, revents {} -> {}", f.fd, f.revents, m); + assert!(f.revents & libc::POLLIN != 0 || f.revents & libc::POLLOUT != 0); + + for e in c.watch_handle(f.fd, m) { + println!("Async: got {:?}", e); + match e { + ConnectionItem::MethodCall(m) => { + assert_eq!(m.headers(), (MessageType::MethodCall, Some("/test".to_string()), + Some("com.example.asynctest".into()), Some("AsyncTest".to_string()))); + let mut mr = Message::new_method_return(&m).unwrap(); + mr.append_items(&["Goodies".into()]); + c.send(mr).unwrap(); + } + ConnectionItem::MethodReturn(m) => { + assert_eq!(m.headers().0, MessageType::MethodReturn); + assert_eq!(m.get_reply_serial().unwrap(), serial); + let i = m.get_items(); + let s: &str = i[0].inner().unwrap(); + assert_eq!(s, "Goodies"); + success = true; + } + _ => (), + } + } + if i > 100 { panic!() }; + } + } + } +} |