1Preamble 2-------- 3 4The 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. 5 6This guide is to help you on your way. In addition, many of the examples in the examples directory append and read arguments. 7 8Code generation 9--------------- 10 11First - 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: 12 13```rust 14cargo install dbus-codegen 15dbus-codegen-rust -s -g -m None -d org.freedesktop.NetworkManager -p /org/freedesktop/NetworkManager > networkmanager.rs 16``` 17 18You would then use this code like: 19 20```rust 21// main.rs 22mod networkmanager; 23 24/* ... */ 25 26// Start a connection to the system bus. 27let c = Connection::get_private(BusType::System)?; 28 29// Make a "ConnPath" struct that just contains a Connection, a destination and a path. 30let p = c.with_path("org.freedesktop.NetworkManager", "/org/freedesktop/NetworkManager", 5000); 31 32// Bring our generated code into scope. 33use networkmanager::OrgFreedesktopNetworkManager; 34 35// Now we can call methods on our connpath from the "org.freedesktop.NetworkManager" interface. 36let devices = c.get_all_devices()?; 37``` 38 39There is also pre-generated code for standard D-Bus interfaces in the `stdintf` module. A similar example: 40 41```rust 42let c = Connection::get_private(BusType::Session)?; 43 44// Make a "ConnPath" struct that just contains a Connection, a destination and a path. 45let p = c.with_path("org.mpris.MediaPlayer2.rhythmbox", "/org/mpris/MediaPlayer2", 5000); 46 47// The ConnPath struct implements many traits, e g `org.freedesktop.DBus.Properties`. Bring the trait into scope. 48use stdintf::org_freedesktop_dbus::Properties; 49 50// Now we can call org.freedesktop.DBus.Properties.Get just like an ordinary method and get the result back. 51let metadata = p.get("org.mpris.MediaPlayer2.Player", "Metadata")?; 52``` 53 54For more details, see `dbus-codegen-rust --help` and the `README.md` in the dbus-codegen directory. 55 56Now, 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: 57 58Append / get basic types 59------------------------ 60 61If you just want to get/append simple types, just use `append1` / `append2` / `append3`, and 62`read1` / `read2` / `read3`. The imaginary method below takes one byte parameter and one string parameter, and returns one string parameter and one int parameter. 63 64```rust 65let m = Message::new_method_call(dest, path, intf, member)?.append2(5u8, "Foo"); 66let r = c.send_with_reply_and_block(m, 2000)?; 67let (data1, data2): (&str, i32) = c.read2()?; 68``` 69 70Arrays and dictionaries 71----------------------- 72 73D-Bus arrays and dictionaries usually correspond to `Vec` and `HashMap`. You can just append and get them like basic types: 74 75```rust 76let v = vec![3i32, 4i32, 5i32]; 77let mut map = HashMap::new(); 78map.insert("Funghi", 5u16); 79map.insert("Mold", 8u16); 80 81let m = Message::new_method_call(dest, path, intf, member)?.append2(v, map); 82let r = c.send_with_reply_and_block(m, 2000)?; 83let (data1, data2): (Vec<i32>, HashMap<&str, u16>) = r.read2()?; 84``` 85 86Or 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. 87 88Slices 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]`. 89 90This 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. 91 92Variants 93-------- 94 95Things 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. 96 97If you know the type beforehand, it's still easy: 98 99```rust 100let v = Variant("This is a variant containing a &str"); 101let m = Message::new_method_call(dest, path, intf, member)?.append1(v); 102let r = c.send_with_reply_and_block(m, 2000)?; 103let z: Variant<i32> = r.read1()?; 104println!("Method returned {}", z.0); 105``` 106 107The `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. 108 109Sometimes 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). 110 111Through trait objects: 112 113```rust 114let x = Box::new(5000i32) as Box<RefArg>; 115let m = Message::new_method_call(dest, path, intf, member)?.append1(Variant(x)); 116let r = c.send_with_reply_and_block(m, 2000)?; 117let z: Variant<Box<RefArg>> = r.read1()?; 118``` 119 120Ok, 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. 121For floating point values, use `arg::cast` (this requires that the RefArg is `static` though, due to Rust type system limitations). 122Match over `arg_type` if you need to know the exact type. 123 124 125```rust 126let z: Variant<Box<RefArg + 'static>> = r.read1()?; 127let value = &z.0; 128 129if let Some(s) = value.as_str() { println!("It's a string: {}", s); } 130else if let Some(i) = value.as_i64() { println!("It's an integer: {}", i); } 131else if let Some(f) = arg::cast::<f64>(value) { println!("It's a float: {}", f); } 132else { println!("Don't know how to handle a {:?}", value.arg_type()) } 133``` 134 135Dicts 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>>>`. 136 137Structs 138------- 139 140D-Bus structs are implemented as Rust tuples. You can append and get tuples like you do with other types of arguments. 141 142TODO: Example 143 144Declare method arguments 145------------------------ 146 147When 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: 148 149```rust 150factory.method( /* ... */ ) 151.inarg::<HashMap<i32, Vec<(i32, bool, String)>>,_>("request") 152.outarg::<&str,_>("reply") 153``` 154 155The 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. 156 157 158Iter / IterAppend 159----------------- 160 161Iter 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. 162 163E 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. 164To read a variant you can use `let i = msg.read1::<Variant<Iter>>::()?` and then examine the methods on `i.0` to probe the variant. 165 166Array and Dict types 167-------------------- 168 169These 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: 170 171```rust 172let x = &[("Hello", true), ("World", false)]; 173let m = Message::new_method_call(dest, path, intf, member)?.append1(Dict::new(x)); 174let r = c.send_with_reply_and_block(m, 2000)?; 175let z: Dict<i32, &str, _> = r.read1()?; 176for (key, value) in z { /* do something */ } 177``` 178 179An 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. 180 181Unusual types 182------------- 183 184The 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. 185 186For `OwnedFd`, which a wrapper around a file descriptor, remember that the file descriptor will be closed when it goes out of scope. 187 188MessageItem 189----------- 190 191MessageItem 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. 192 193 194