zbus

Attribute Macro interface

Source
#[interface]
Expand description

Attribute macro for implementing a D-Bus interface.

The macro must be applied on an impl T. All methods will be exported, either as methods, properties or signal depending on the item attributes. It will implement the Interface trait for T on your behalf, to handle the message dispatching and introspection support.

The trait accepts the interface attributes:

  • name - the D-Bus interface name

  • spawn - Controls the spawning of tasks for method calls. By default, true, allowing zbus to spawn a separate task for each method call. This default behavior can lead to methods being handled out of their received order, which might not always align with expected or desired behavior.

    • When True (Default): Suitable for interfaces where method calls are independent of each other or can be processed asynchronously without strict ordering. In scenarios where a client must wait for a reply before making further dependent calls, this default behavior is appropriate.

    • When False: Use this setting to ensure methods are handled in the order they are received, which is crucial for interfaces requiring sequential processing of method calls. However, care must be taken to avoid making D-Bus method calls from within your interface methods when this setting is false, as it may lead to deadlocks under certain conditions.

  • proxy - If specified, a proxy type will also be generated for the interface. This attribute supports all the proxy-specific sub-attributes (e.g gen_async). The common sub-attributes (e.g name) are automatically forworded to the proxy macro.

The methods accepts the interface attributes:

  • name - override the D-Bus name (pascal case form of the method by default)

  • property - expose the method as a property. If the method takes an argument, it must be a setter, with a set_ prefix. Otherwise, it’s a getter. If it may fail, a property method must return zbus::fdo::Result. An additional sub-attribute exists to control the emission of signals on changes to the property:

    • emits_changed_signal - specifies how property changes are signaled. Valid values are those documented in DBus specifications:
      • "true" - (default) the change signal is always emitted when the property’s setter is called. The value of the property is included in the signal.
      • "invalidates" - the change signal is emitted, but the value is not included in the signal.
      • "const" - the property never changes, thus no signal is ever emitted for it.
      • "false" - the change signal is not emitted if the property changes.
  • signal - the method is a “signal”. It must be a method declaration (without body). Its code block will be expanded to emit the signal from the object path associated with the interface instance. Moreover, interface will also generate a trait named <Interface>Signals that provides all the signal methods but without the SignalEmitter argument. The macro implements this trait for two types, zbus::object_server::InterfaceRef<Interface> and SignalEmitter<'_>. The former is useful for emitting signals from outside the context of an interface method and the latter is useful for emitting signals from inside interface methods.

    You can call a signal method from a an interface method, or from an ObjectServer::with function.

  • out_args - When returning multiple values from a method, naming the out arguments become important. You can use out_args to specify their names.

  • proxy - Use this to specify the proxy-specific method sub-attributes (e.g object). The common sub-attributes (e.g name) are automatically forworded to the proxy macro. Moreover, you can use visibility sub-attribute to specify the visibility of the generated proxy type(s).

    In such case, your method must return a tuple containing your out arguments, in the same order as passed to out_args.

The struct_return attribute (from zbus 1.x) is no longer supported. If you want to return a single structure from a method, declare it to return a tuple containing either a named structure or a nested tuple.

Note: a <property_name_in_snake_case>_changed method is generated for each property: this method emits the “PropertiesChanged” signal for the associated property. The setter (if it exists) will automatically call this method. For instance, a property setter named set_foo will be called to set the property “Foo”, and will emit the “PropertiesChanged” signal with the new value for “Foo”. Other changes to the “Foo” property can be signaled manually with the generated foo_changed method. In addition, a <property_name_in_snake_case>_invalidated method is also generated that much like _changed method, emits a “PropertyChanged” signal but does not send over the new value of the property along with it. It is usually best to avoid using this since it will force all interested peers to fetch the new value and hence result in excess traffic on the bus.

The method arguments support the following zbus attributes:

  • object_server - This marks the method argument to receive a reference to the ObjectServer this method was called by.
  • connection - This marks the method argument to receive a reference to the Connection on which the method call was received.
  • header - This marks the method argument to receive the message header associated with the D-Bus method call being handled.
  • signal_emitter - This marks the method argument to receive a SignalEmitter instance, which is needed for emitting signals the easy way.

§Example

use zbus_macros::interface;
use zbus::{ObjectServer, object_server::SignalEmitter, message::Header};

struct Example {
    _some_data: String,
}

#[interface(name = "org.myservice.Example")]
impl Example {
    // "Quit" method. A method may throw errors.
    async fn quit(
        &self,
        #[zbus(header)]
        hdr: Header<'_>,
        #[zbus(signal_emitter)]
        emitter: SignalEmitter<'_>,
        #[zbus(object_server)]
        _server: &ObjectServer,
    ) -> zbus::fdo::Result<()> {
        let path = hdr.path().unwrap();
        let msg = format!("You are leaving me on the {} path?", path);
        emitter.bye(&msg).await?;

        // Do some asynchronous tasks before quitting..

        Ok(())
    }

    // "TheAnswer" property (note: the "name" attribute), with its associated getter.
    // A `the_answer_changed` method has also been generated to emit the
    // "PropertiesChanged" signal for this property.
    #[zbus(property, name = "TheAnswer")]
    fn answer(&self) -> u32 {
        2 * 3 * 7
    }

    // "IFail" property with its associated getter.
    // An `i_fail_changed` method has also been generated to emit the
    // "PropertiesChanged" signal for this property.
    #[zbus(property)]
    fn i_fail(&self) -> zbus::fdo::Result<i32> {
        Err(zbus::fdo::Error::UnknownProperty("IFail".into()))
    }

    // "Bye" signal (note: no implementation body).
    #[zbus(signal)]
    async fn bye(signal_emitter: &SignalEmitter<'_>, message: &str) -> zbus::Result<()>;

    #[zbus(out_args("answer", "question"))]
    fn meaning_of_life(&self) -> zbus::fdo::Result<(i32, String)> {
        Ok((42, String::from("Meaning of life")))
    }
}

See also ObjectServer documentation to learn how to export an interface over a Connection.