├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── examples ├── client.rs └── server.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dbus-macros" 3 | version = "0.2.4" 4 | authors = ["Antoni Boucher ", 5 | "Zeeshan Ali "] 6 | license = "MIT" 7 | description = "Convenient macros to use the dbus crate" 8 | repository = "https://github.com/antoyo/dbus-macros-rs" 9 | 10 | [dependencies] 11 | dbus = "^0.5" 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Boucher, Antoni 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ---- 2 | 3 | No longer maintained or developed. Dropped in favor of [zbus](https://crates.io/crates/zbus) 4 | ---- 5 | 6 | D-Bus macros for Rust 7 | ===================== 8 | 9 | Dealing with D-Bus in your code can be a bit tedious. These macros makes the 10 | task simpler. They are inspired by [Vala's awesome D-Bus support](https://chebizarro.gitbooks.io/the-vala-tutorial/content/d-bus-integration.html). 11 | 12 | Examples 13 | ======== 14 | 15 | Server 16 | ------ 17 | 18 | This example serves a bunch of methods on an object 19 | 20 | ```rust 21 | extern crate dbus; 22 | #[macro_use] 23 | extern crate dbus_macros; 24 | 25 | use dbus::{Connection, BusType}; 26 | use std::rc::Rc; 27 | 28 | dbus_class!("com.dbus.test", class Hello (variable: i32) { 29 | fn hello(&this) -> String { 30 | "Hello!" 31 | } 32 | 33 | fn hello_with_name(&this, name: &str) -> String { 34 | format!("Hello, {}!", name) 35 | } 36 | 37 | fn get_variable(&this) -> i32 { 38 | this.variable 39 | } 40 | }); 41 | 42 | fn main() { 43 | let variable = 24; 44 | let session_connection = Connection::get_private(BusType::Session).unwrap(); 45 | let hello = Hello::new(variable); 46 | hello.run("com.dbus.test", &session_connection, "/Hello"); 47 | } 48 | ``` 49 | 50 | You can try a similar example (which has more methods) by running: 51 | 52 | cargo run --example server 53 | 54 | Client 55 | ------ 56 | 57 | This example opens a connection to the server example above and calls its methods. 58 | 59 | ```rust 60 | extern crate dbus; 61 | #[macro_use] 62 | extern crate dbus_macros; 63 | 64 | use dbus::{Connection, BusType}; 65 | use std::rc::Rc; 66 | 67 | dbus_interface!("com.dbus.test", interface Hello { 68 | fn hello() -> String; 69 | fn hello_with_name(name: &str) -> String; 70 | fn get_variable() -> i32; 71 | }); 72 | 73 | fn main() { 74 | let session_connection = std::rc::Rc::new(dbus::Connection::get_private(dbus::BusType::Session).unwrap()); 75 | let hello = Hello::new("com.dbus.test", "/Hello", session_connection); 76 | 77 | match hello.hello() { 78 | Ok(string) => println!("{}", string), 79 | Err(error) => println!("Error calling DBus service: {}", error), 80 | } 81 | println!("{}", hello.hello_with_name("World").unwrap()); 82 | println!("{}", hello.get_variable().unwrap()); 83 | } 84 | ``` 85 | 86 | You can try a similar example (that tries more method calls on the server example) by running: 87 | 88 | cargo run --example client 89 | 90 | Requirements 91 | ============ 92 | 93 | [dbus](https://github.com/diwic/dbus-rs) 0.5 or higher, but it's handled for you 94 | by the cargo system. 95 | -------------------------------------------------------------------------------- /examples/client.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Boucher, Antoni 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | extern crate dbus; 23 | #[macro_use] 24 | extern crate dbus_macros; 25 | 26 | use std::rc::Rc; 27 | 28 | dbus_interface!("com.dbus.test", interface Hello { 29 | fn hello() -> String; 30 | fn hello_with_name(name: &str) -> String; 31 | fn greeting(greeting: &str, name: &str) -> String; 32 | fn greeting_with_separator(greeting: &str, separator: &str, name: &str) -> String; 33 | fn greeting_with_separator_and_end(greeting: &str, separator: &str, name: &str, end: &str) -> String; 34 | fn to_string5(arg1: &str, arg2: &str, arg3: &str, arg4: &str, arg5: &str) -> String; 35 | fn int_to_string(int: i32) -> String; 36 | fn get_variable() -> i32; 37 | fn debug(); 38 | fn debug1(arg1: &str); 39 | fn debug2(arg1: &str, arg2: &str); 40 | fn debug3(arg1: &str, arg2: &str, arg3: &str); 41 | fn debug4(arg1: &str, arg2: &str, arg3: &str, arg4: &str); 42 | fn debug5(arg1: &str, arg2: &str, arg3: &str, arg4: &str, arg5: &str); 43 | }); 44 | 45 | 46 | fn main() { 47 | let connection = std::rc::Rc::new(dbus::Connection::get_private(dbus::BusType::Session).unwrap()); 48 | let hello = Hello::new("com.dbus.test", "/Hello", connection.clone()); 49 | 50 | match hello.hello() { 51 | Ok(string) => println!("{}", string), 52 | Err(error) => println!("Error calling DBus service: {}", error), 53 | } 54 | println!("{}", hello.hello_with_name("World").unwrap()); 55 | println!("{}", hello.greeting("Hi", "Me").unwrap()); 56 | println!("{}", hello.greeting_with_separator("Salut", " - ", "Toi").unwrap()); 57 | println!("{}", hello.greeting_with_separator_and_end("Salut", " - ", "Toi", "?").unwrap()); 58 | println!("{}", hello.to_string5("arg1 ", "arg2 ", "arg3 ", "arg4 ", "arg5").unwrap()); 59 | let string: String = hello.int_to_string(42).unwrap(); 60 | println!("{}", string); 61 | println!("{}", hello.get_variable().unwrap()); 62 | hello.debug().unwrap(); 63 | hello.debug1("arg1").unwrap(); 64 | hello.debug2("arg1", "arg2").unwrap(); 65 | hello.debug3("arg1", "arg2", "arg3").unwrap(); 66 | hello.debug4("arg1", "arg2", "arg3", "arg4").unwrap(); 67 | hello.debug5("arg1", "arg2", "arg3", "arg4", "arg5").unwrap(); 68 | } 69 | -------------------------------------------------------------------------------- /examples/server.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Boucher, Antoni 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | extern crate dbus; 23 | #[macro_use] 24 | extern crate dbus_macros; 25 | 26 | dbus_class!("com.dbus.test", class Hello (variable: i32) { 27 | fn hello(&this) -> String { 28 | "Hello!" 29 | } 30 | 31 | fn hello_with_name(&this, name: &str) -> String { 32 | format!("Hello, {}!", name) 33 | } 34 | 35 | fn hello_with_error(&this) -> Result { 36 | Err(dbus::Error::new_custom("com.dbus.test.Error", "This error is expected, don't panic!")) 37 | } 38 | 39 | fn greeting(&this, greeting: &str, name: &str) -> String { 40 | format!("{}, {}!", greeting, name) 41 | } 42 | 43 | fn greeting_with_separator(&this, greeting: &str, separator: &str, name: &str) -> String { 44 | format!("{}{}{}!", greeting, separator, name) 45 | } 46 | 47 | fn greeting_with_separator_and_end(&this, greeting: &str, separator: &str, name: &str, end: &str) -> String { 48 | format!("{}{}{}{}", greeting, separator, name, end) 49 | } 50 | 51 | fn to_string5(&this, arg1: &str, arg2: &str, arg3: &str, arg4: &str, arg5: &str) -> String { 52 | format!("{}{}{}{}{}", arg1, arg2, arg3, arg4, arg5) 53 | } 54 | 55 | fn int_to_string(&this, int: i32) -> String { 56 | int.to_string() 57 | } 58 | 59 | fn get_variable(&this) -> i32 { 60 | this.variable 61 | } 62 | 63 | fn debug(&this) { 64 | println!("Debug"); 65 | } 66 | 67 | fn debug1(&this, arg1: &str) { 68 | println!("Debug: {}", arg1); 69 | } 70 | 71 | fn debug2(&this, arg1: &str, arg2: &str) { 72 | println!("Debug: {} {}", arg1, arg2); 73 | } 74 | 75 | fn debug3(&this, arg1: &str, arg2: &str, arg3: &str) { 76 | println!("Debug: {} {} {}", arg1, arg2, arg3); 77 | } 78 | 79 | fn debug4(&this, arg1: &str, arg2: &str, arg3: &str, arg4: &str) { 80 | println!("Debug: {} {} {} {}", arg1, arg2, arg3, arg4); 81 | } 82 | 83 | fn debug5(&this, arg1: &str, arg2: &str, arg3: &str, arg4: &str, arg5: &str) { 84 | println!("Debug: {} {} {} {} {}", arg1, arg2, arg3, arg4, arg5); 85 | } 86 | }); 87 | 88 | fn main() { 89 | let variable = 24; 90 | 91 | let hello = Hello::new(variable); 92 | let connection = dbus::Connection::get_private(dbus::BusType::Session).unwrap(); 93 | hello.run("com.dbus.test", &connection, "/Hello"); 94 | } 95 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Boucher, Antoni 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | /* 23 | * TODO: add Cargo categories. 24 | * TODO: Switch to macro 1.1. 25 | */ 26 | 27 | pub fn to_camel(term: &str) -> String { 28 | let underscore_count = term.chars().filter(|c| *c == '_').count(); 29 | let mut result = String::with_capacity(term.len() - underscore_count); 30 | let mut at_new_word = true; 31 | 32 | for c in term.chars() { 33 | if c == '_' { 34 | at_new_word = true; 35 | } else if at_new_word { 36 | result.push(c.to_ascii_uppercase()); 37 | at_new_word = false; 38 | } else { 39 | result.push(c); 40 | } 41 | } 42 | 43 | result 44 | } 45 | 46 | // 47 | // Server-side 48 | // 49 | 50 | #[macro_export] 51 | macro_rules! dbus_functions { 52 | ($self_:expr, $factory:expr, $interface:ident,) => { 53 | }; 54 | ($self_:expr, $factory:expr, $interface:ident, fn $func_name:ident (&$this:ident $(, $arg:ident : $arg_type:ty )* ) -> Result<$return_type:ty,$error:ty> $block:block $($rest:tt)*) => { 55 | let $this = $self_.clone(); 56 | let $interface = $interface.add_m( 57 | $factory.method(::dbus_macros::to_camel(stringify!($func_name)), (), move |method| { 58 | let mut i = method.msg.iter_init(); 59 | $( 60 | let $arg: $arg_type = i.get().ok_or(dbus::tree::MethodErr::no_arg())?; 61 | i.next(); 62 | )* 63 | let result: $return_type = $block?; 64 | Ok(vec!(method.msg.method_return().append1(result))) 65 | }) 66 | $( 67 | .inarg::<$arg_type, _>(stringify!($arg)) 68 | )* 69 | .outarg::<$return_type, _>("result") 70 | ); 71 | dbus_functions!($self_, $factory, $interface, $($rest)*); 72 | }; 73 | ($self_:expr, $factory:expr, $interface:ident, fn $func_name:ident (&$this:ident $(, $arg:ident : $arg_type:ty )* ) -> $return_type:ty $block:block $($rest:tt)*) => { 74 | let $this = $self_.clone(); 75 | let $interface = $interface.add_m( 76 | $factory.method(::dbus_macros::to_camel(stringify!($func_name)), (), move |method| { 77 | let mut i = method.msg.iter_init(); 78 | $( 79 | let $arg: $arg_type = i.get().ok_or(dbus::tree::MethodErr::no_arg())?; 80 | i.next(); 81 | )* 82 | let result = $block; 83 | Ok(vec!(method.msg.method_return().append1(result))) 84 | }) 85 | $( 86 | .inarg::<$arg_type, _>(stringify!($arg)) 87 | )* 88 | .outarg::<$return_type, _>("result") 89 | ); 90 | dbus_functions!($self_, $factory, $interface, $($rest)*); 91 | }; 92 | ($self_:expr, $factory:expr, $interface:ident, fn $func_name:ident (&$this:ident $(, $arg:ident : $arg_type:ty )* ) $block:block $($rest:tt)*) => { 93 | let $this = $self_.clone(); 94 | let $interface = $interface.add_m( 95 | $factory.method(::dbus_macros::to_camel(stringify!($func_name)), (), move |method| { 96 | let mut i = method.msg.iter_init(); 97 | $( 98 | let $arg: $arg_type = i.get().ok_or(dbus::tree::MethodErr::no_arg())?; 99 | i.next(); 100 | )* 101 | $block; 102 | let result = 0; 103 | Ok(vec!(method.msg.method_return().append1(result))) 104 | }) 105 | $( 106 | .inarg::<$arg_type, _>(stringify!($arg)) 107 | )* 108 | .outarg::("result") 109 | ); 110 | dbus_functions!($self_, $factory, $interface, $($rest)*); 111 | }; 112 | } 113 | 114 | #[macro_export] 115 | macro_rules! dbus_class { 116 | ($interface_name:expr, class $class_name:ident { $($functions:tt)* }) => { 117 | #[derive(Clone)] 118 | pub struct $class_name { 119 | } 120 | 121 | impl $class_name { 122 | pub fn new() -> Self { 123 | $class_name { 124 | } 125 | } 126 | 127 | pub fn run

(&self, bus_name: &str, connection: &dbus::Connection, path: P) where P: Into> { 128 | connection.register_name(bus_name, dbus::NameFlag::ReplaceExisting as u32).unwrap(); 129 | 130 | let factory = dbus::tree::Factory::new_fn::<()>(); 131 | let class = factory.tree(()).add(factory.object_path(path, ()).introspectable().add({ 132 | let interface = factory.interface($interface_name, ()); 133 | dbus_functions!(self, factory, interface, $($functions)*); 134 | interface 135 | })); 136 | class.set_registered(&connection, true).unwrap(); 137 | 138 | for _ in class.run(&connection, connection.iter(1000)) { 139 | } 140 | } 141 | } 142 | }; 143 | ($interface_name:expr, class $class_name:ident ($($variables:ident : $variable_types:ty),*) { $($functions:tt)* }) => { 144 | #[derive(Clone)] 145 | pub struct $class_name { 146 | $($variables : $variable_types,)* 147 | } 148 | 149 | impl $class_name { 150 | pub fn new($($variables: $variable_types),*) -> Self { 151 | $class_name { 152 | $($variables : $variables,)* 153 | } 154 | } 155 | 156 | pub fn run

(&self, bus_name: &str, connection: &dbus::Connection, path: P) where P: Into> { 157 | connection.register_name(bus_name, dbus::NameFlag::ReplaceExisting as u32).unwrap(); 158 | 159 | let factory = dbus::tree::Factory::new_fn::<()>(); 160 | let class = factory.tree(()).add(factory.object_path(path, ()).introspectable().add({ 161 | let interface = factory.interface($interface_name, ()); 162 | dbus_functions!(self, factory, interface, $($functions)*); 163 | interface 164 | })); 165 | class.set_registered(&connection, true).unwrap(); 166 | 167 | for _ in class.run(&connection, connection.iter(1000)) { 168 | } 169 | } 170 | } 171 | }; 172 | } 173 | 174 | // 175 | // Client-side 176 | // 177 | 178 | #[macro_export] 179 | macro_rules! dbus_prototypes { 180 | ($interface_name:expr, $class_name:ident, ) => { 181 | }; 182 | ($interface_name:expr, $class_name:ident, fn $func_name:ident ( $( $arg:ident : $arg_type:ty ),* ) -> $return_type:ty; $($rest:tt)*) => { 183 | pub fn $func_name(&self, $( $arg: $arg_type ),* ) -> Result<$return_type, dbus::Error> { 184 | let message = dbus::Message::new_method_call(&self.bus_name, self.path.clone(), $interface_name, ::dbus_macros::to_camel(stringify!($func_name))).unwrap(); 185 | $( 186 | let message = message.append1($arg); 187 | )* 188 | let response = try!(self.connection.send_with_reply_and_block(message, 2000)); 189 | response.get1().ok_or(dbus::Error::from(dbus::tree::MethodErr::no_arg())) 190 | } 191 | dbus_prototypes!($interface_name, $class_name, $($rest)*); 192 | }; 193 | ($interface_name:expr, $class_name:ident, fn $func_name:ident ( $( $arg:ident : $arg_type:ty ),* ) ; $($rest:tt)*) => { 194 | pub fn $func_name(&self, $( $arg: $arg_type ),* ) -> Result<(), dbus::Error> { 195 | let message = dbus::Message::new_method_call(&self.bus_name, self.path.clone(), $interface_name, ::dbus_macros::to_camel(stringify!($func_name))).unwrap(); 196 | $( 197 | let message = message.append1($arg); 198 | )* 199 | self.connection.send(message).ok(); 200 | Ok(()) 201 | } 202 | dbus_prototypes!($interface_name, $class_name, $($rest)*); 203 | }; 204 | } 205 | 206 | #[macro_export] 207 | macro_rules! dbus_interface { 208 | ($interface_name:expr, interface $class_name:ident { $($prototypes:tt)* }) => { 209 | pub struct $class_name<'a> { 210 | bus_name: String, 211 | path: dbus::Path<'a>, 212 | connection: Rc, 213 | } 214 | 215 | impl<'a> $class_name<'a> { 216 | pub fn new

(dbus_name: &str, path: P, connection: Rc) -> Self where P: Into> { 217 | $class_name { 218 | bus_name: dbus_name.to_string(), 219 | path: path.into(), 220 | connection: connection, 221 | } 222 | } 223 | 224 | dbus_prototypes!($interface_name, $class_name, $($prototypes)*); 225 | } 226 | }; 227 | } 228 | --------------------------------------------------------------------------------