I saw this example on the freedesktop.org pages:
use std::collections::HashMap;
use std::error::Error;
use zbus::Connection;
use zvariant::Value;
fn main() -> Result<(), Box<dyn Error>> {
let connection = Connection::new_session()?;
let m = connection.call_method(
Some("org.freedesktop.Notifications"),
"/org/freedesktop/Notifications",
Some("org.freedesktop.Notifications"),
"Notify",
&("my-app", 0u32, "dialog-information", "A summary", "Some body",
vec![""; 0], HashMap::<&str, &Value>::new(), 5000),
)?;
let reply: u32 = m.body().unwrap();
dbg!(reply);
Ok(())
}
And I wanted to refactor it so I could just call another dbus service, one that I have implemented in C.
The idea is that given a boolean value, I want to call one or another with different arguments.
But I have found that the refactor of the original code, trying to change the arguments tuple to a struct, and making the args into a zvariant::Value
is more difficult than I expected.
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::error::Error;
//use zbus::{Connection, zvariant::Value};
use zbus::Connection;
use zvariant::{DynamicType, OwnedValue, Structure, Type, Value, serialized::Context};
#[derive(Debug, PartialEq, Deserialize, Serialize, Type)]
#[zvariant(signature = "s")]
struct NetConfigMethodArgs {
interface: String,
ip: String,
}
const NETCONFIG_SERVICE: &str = "com.example.NetConfig";
const NETCONFIG_PATH: &str = "/com/example/NetConfig";
const NETCONFIG_OBJECT: &str = "com.example.Netconfig";
const NETCONFIG_METHOD: &str = "SetInterfaceIP";
#[derive(Debug, PartialEq, Deserialize, Serialize, Type)]
#[zvariant(signature = "susssasa{sv}i")]
struct NotificationsMethodArgs<> {
app_name: String,
replaces_id: u32,
app_icon: String,
summary: String,
body: String,
actions: Vec<String>,
hints: HashMap<&str, &Value>,
timeout: i32,
}
const NOTIFICATIONS_SERVICE: &str = "org.freedesktop.Notifications";
const NOTIFICATIONS_PATH: &str = "/org/freedesktop/Notifications";
const NOTIFICATIONS_OBJECT: &str = "org.freedesktop.Notifications";
const NOTIFICATIONS_METHOD: &str = "Notify";
// Although we use `tokio` here, you can use any async runtime of choice.
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let connection = Connection::session().await?;
let mut service = NETCONFIG_SERVICE;
let mut path = NETCONFIG_PATH;
let mut object = NETCONFIG_OBJECT;
let mut method = NETCONFIG_METHOD;
let netconfigargs = NetConfigMethodArgs {
interface: String::from("enxc0470e9a117"),
ip: String::from("192.168.1.141"),
};
let args = Value::new(netconfigargs);
if true {
service = NOTIFICATIONS_SERVICE;
path = NOTIFICATIONS_PATH;
object = NOTIFICATIONS_OBJECT;
method = NOTIFICATIONS_METHOD;
let notificationsargs = NotificationsMethodArgs {
app_name: String::from("my-app"),
replaces_id: 0u32,
app_icon: String::from("dialog-information"),
summary: String::from("A summary"),
body: String::from("Some body"),
actions: vec!["".to_string(); 0],
hints: HashMap::<&str, &Value>::new(),
timeout: 5000,
};
}
let m = connection
.call_method(Some(service), path, Some(object), method, &args)
.await?;
let reply: u32 = m.body().deserialize().unwrap();
dbg!(reply);
Ok(())
}
Depending on what things I do, the compiler complaints in different ways. But the most interesting to me for resolve are these two:
How I can maintaint the hints: HashMap<&str, &Value>
field in the struct as in the tuple of the example without the compiler complaining. I've tried to change &Value
to Value
and this works. Also, instead of having a HashMap
I have used another structure following another example and can also make it work. Although there are alternatives, I want to know if I can make it work the way it is right now.
How can I use a zvariant::Value
to either have one struct or another inside. Now it is complaining that:
Diagnostics:
the trait bound `zvariant::Structure<'_>: From<NetConfigMethodArgs>` is not satisfied the following other types implement trait `From<T>`:
`zvariant::Structure<'_>` implements `From<(T0, T1)>`
`zvariant::Structure<'_>` implements `From<(T0, T1, T2)>`
`zvariant::Structure<'_>` implements `From<(T0, T1, T2, T3)>`
`zvariant::Structure<'_>` implements `From<(T0, T1, T2, T3, T4)>`
`zvariant::Structure<'_>` implements `From<(T0, T1, T2, T3, T4, T5)>`
`zvariant::Structure<'_>` implements `From<(T0, T1, T2, T3, T4, T5, T6)>`
`zvariant::Structure<'_>` implements `From<(T0, T1, T2, T3, T4, T5, T6, T7)>`
`zvariant::Structure<'_>` implements `From<(T0, T1, T2, T3, T4, T5, T6, T7, T8)>` and 8 others required for `NetConfigMethodArgs` to implement `Into<zvariant::Structure<'_>>` required for `zvariant::Value<'_>` to implement `From<NetConfigMethodArgs>` 1 redundant requirement hidden required for `NetConfigMethodArgs` to implement `Into<zvariant::Value<'_>>` [E0277]
required by a bound introduced by this call [E0277]
I probably need to implement some traits to make a conversion form my structs to Value
and viceversa.
I found a solution. It seems that the call_method was not expecting a zvariant::Value, but whatever type that could match some specific traits. zvariant::Structure<'_>
was one of those types, so I only had to convert my structure to a zvariant::Structure<'_>
by implementing the From trait:
impl From<NotificationsMethodArgs> for zvariant::Structure<'_> {
fn from(value: NotificationsMethodArgs) -> Self {
let ret_value = zvariant::StructureBuilder::new()
.add_field(value.app_name)
.add_field(value.replaces_id)
.add_field(value.app_icon)
.add_field(value.summary)
.add_field(value.body)
.add_field(value.actions)
.add_field(value.hints)
.add_field(value.timeout).build().unwrap();
ret_value
}
}
Also, instead of having to deal with lifetimes, I changed the NotificationsMethodArgs
to have a HashMap<String, OwnedValue>
instead of HashMap<&str, &Value>
#[derive(Debug, PartialEq, Deserialize, Serialize, Type)]
struct NotificationsMethodArgs {
app_name: String,
replaces_id: u32,
app_icon: String,
summary: String,
body: String,
actions: Vec<String>,
hints: HashMap<String, OwnedValue>,
timeout: i32,
}