zvariant

Derive Macro Type

Source
#[derive(Type)]
{
    // Attributes available to this derive:
    #[zbus]
    #[zvariant]
}
Expand description

Derive macro to add Type implementation to structs and enums.

§Examples

For structs it works just like serde’s Serialize and Deserialize macros:

use zvariant::{serialized::Context, to_bytes, Type, LE};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
struct Struct<'s> {
    field1: u16,
    field2: i64,
    field3: &'s str,
}

assert_eq!(Struct::SIGNATURE, "(qxs)");
let s = Struct {
    field1: 42,
    field2: i64::max_value(),
    field3: "hello",
};
let ctxt = Context::new_dbus(LE, 0);
let encoded = to_bytes(ctxt, &s).unwrap();
let decoded: Struct = encoded.deserialize().unwrap().0;
assert_eq!(decoded, s);

Same with enum, except that all variants of the enum must have the same number and types of fields (if any). If you want the encoding size of the (unit-type) enum to be dictated by repr attribute (like in the example below), you’ll also need serde_repr crate.

use zvariant::{serialized::Context, to_bytes, Type, LE};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};

#[repr(u8)]
#[derive(Deserialize_repr, Serialize_repr, Type, Debug, PartialEq)]
enum Enum {
    Variant1,
    Variant2,
}
assert_eq!(Enum::SIGNATURE, u8::SIGNATURE);
let ctxt = Context::new_dbus(LE, 0);
let encoded = to_bytes(ctxt, &Enum::Variant2).unwrap();
let decoded: Enum = encoded.deserialize().unwrap().0;
assert_eq!(decoded, Enum::Variant2);

#[repr(i64)]
#[derive(Deserialize_repr, Serialize_repr, Type)]
enum Enum2 {
    Variant1,
    Variant2,
}
assert_eq!(Enum2::SIGNATURE, i64::SIGNATURE);

// w/o repr attribute, u32 representation is chosen
#[derive(Deserialize, Serialize, Type)]
enum NoReprEnum {
    Variant1,
    Variant2,
}
assert_eq!(NoReprEnum::SIGNATURE, u32::SIGNATURE);

// Not-unit enums are represented as a structure, with the first field being a u32 denoting the
// variant and the second as the actual value.
#[derive(Deserialize, Serialize, Type)]
enum NewType {
    Variant1(f64),
    Variant2(f64),
}
assert_eq!(NewType::SIGNATURE, "(ud)");

#[derive(Deserialize, Serialize, Type)]
enum StructFields {
    Variant1(u16, i64, &'static str),
    Variant2 { field1: u16, field2: i64, field3: &'static str },
}
assert_eq!(StructFields::SIGNATURE, "(u(qxs))");

§Custom signatures

There are times when you’d find yourself wanting to specify a hardcoded signature yourself for the type. The signature attribute exists for this purpose. A typical use case is when you’d need to encode your type as a dictionary (signature a{sv}) type. For convenience, dict is an alias for a{sv}. Here is an example:

use zvariant::{SerializeDict, DeserializeDict, serialized::Context, to_bytes, Type, LE};

#[derive(DeserializeDict, SerializeDict, Type, PartialEq, Debug)]
// `#[zvariant(signature = "a{sv}")]` would be the same.
#[zvariant(signature = "dict")]
struct Struct {
    field1: u16,
    field2: i64,
    field3: String,
}

assert_eq!(Struct::SIGNATURE, "a{sv}");
let s = Struct {
    field1: 42,
    field2: i64::max_value(),
    field3: "hello".to_string(),
};
let ctxt = Context::new_dbus(LE, 0);
let encoded = to_bytes(ctxt, &s).unwrap();
let decoded: Struct = encoded.deserialize().unwrap().0;
assert_eq!(decoded, s);

Another common use for custom signatures is (de)serialization of unit enums as strings:

use zvariant::{serialized::Context, to_bytes, Type, LE};
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Type, PartialEq, Debug)]
#[zvariant(signature = "s")]
enum StrEnum {
    Variant1,
    Variant2,
    Variant3,
}

assert_eq!(StrEnum::SIGNATURE, "s");
let ctxt = Context::new_dbus(LE, 0);
let encoded = to_bytes(ctxt, &StrEnum::Variant2).unwrap();
assert_eq!(encoded.len(), 13);
let decoded: StrEnum = encoded.deserialize().unwrap().0;
assert_eq!(decoded, StrEnum::Variant2);