tokio-dbus-codegen 0.1.1

Build time code generation of D-Bus clients and servers for tokio-dbus.
Documentation
use crate::{Builder, Result};

const EXAMPLE: &str = r#"
<node>
  <interface name="com.example.Example">
    <method name="Add">
      <arg name="a" type="i" direction="in"/>
      <arg name="b" type="i" direction="in"/>
      <arg name="sum" type="i" direction="out"/>
    </method>
    <method name="Reset"/>
    <method name="Split">
      <arg name="input" type="s" direction="in"/>
      <arg name="head" type="s" direction="out"/>
      <arg name="tail" type="as" direction="out"/>
    </method>
    <signal name="Changed">
      <arg name="value" type="i"/>
    </signal>
    <signal name="Reset"/>
    <property name="Total" type="i" access="read"/>
    <property name="Label" type="s" access="readwrite"/>
    <property name="Hints" type="a{sv}" access="read"/>
  </interface>
</node>
"#;

/// Write the interface file under a name of its own, since the tests run in
/// parallel and would otherwise read a file another one is still writing.
fn builder(name: &str) -> Builder {
    let dir = std::env::temp_dir().join("tokio-dbus-codegen-tests");
    std::fs::create_dir_all(&dir).unwrap();
    let path = dir.join(format!("{name}.xml"));
    std::fs::write(&path, EXAMPLE).unwrap();
    Builder::new().file(&path)
}

const EXAMPLE_NAME: &str = "com.example.Example";

#[test]
fn generates_a_client() -> Result<()> {
    let out = builder("client").client(EXAMPLE_NAME).to_string()?;
    assert!(out.contains("pub mod example"));
    assert!(out.contains("pub struct Example"));
    assert!(out.contains("pub async fn add("));
    assert!(out.contains("a: i32,"));
    assert!(out.contains("-> Result<i32>"));
    assert!(out.contains("-> Result<(String, Vec<String>)>"));
    assert!(out.contains("pub async fn label(&self, conn: &mut Connection) -> Result<String>"));
    assert!(out.contains("pub async fn set_label("));
    assert!(!out.contains("pub async fn set_total("));
    assert!(out.contains("pub enum Signal"));
    assert!(out.contains("Changed { "));
    assert!(!out.contains("ExampleServer"));
    Ok(())
}

#[test]
fn generates_a_server() -> Result<()> {
    let out = builder("server").server(EXAMPLE_NAME).to_string()?;
    assert!(out.contains("pub trait ExampleServer"));
    assert!(out.contains("async fn add(&mut self, a: i32, b: i32) -> Result<i32>;"));
    assert!(out.contains("async fn reset(&mut self) -> Result<()>;"));
    assert!(out.contains("async fn total(&mut self) -> Result<i32>;"));
    assert!(out.contains("async fn set_label(&mut self, value: String) -> Result<()>;"));
    assert!(out.contains("pub async fn dispatch<T>"));
    assert!(out.contains("async fn dispatch_properties<T>"));
    assert!(!out.contains("pub struct Example {"));
    Ok(())
}

#[test]
fn generates_both() -> Result<()> {
    let out = builder("both").both(EXAMPLE_NAME).to_string()?;
    assert!(out.contains("pub struct Example {"));
    assert!(out.contains("pub trait ExampleServer"));
    Ok(())
}