#![cfg(feature = "xml")]
use serde::{Deserialize, Serialize};
use serde_xml_rs::{from_reader, from_str, to_writer};
use static_assertions::assert_impl_all;
use std::{
io::{Read, Write},
result::Result,
};
use crate::Error;
macro_rules! get_vec {
($vec:expr, $kind:path) => {
$vec.iter()
.filter_map(|e| if let $kind(m) = e { Some(m) } else { None })
.collect()
};
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Annotation {
name: String,
value: String,
}
assert_impl_all!(Annotation: Send, Sync, Unpin);
impl Annotation {
pub fn name(&self) -> &str {
&self.name
}
pub fn value(&self) -> &str {
&self.value
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Arg {
name: Option<String>,
r#type: String,
direction: Option<String>,
#[serde(rename = "annotation", default)]
annotations: Vec<Annotation>,
}
assert_impl_all!(Arg: Send, Sync, Unpin);
impl Arg {
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn ty(&self) -> &str {
&self.r#type
}
pub fn direction(&self) -> Option<&str> {
self.direction.as_deref()
}
pub fn annotations(&self) -> Vec<&Annotation> {
self.annotations.iter().collect()
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "lowercase")]
enum MethodElement {
Arg(Arg),
Annotation(Annotation),
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Method {
name: String,
#[serde(rename = "$value", default)]
elems: Vec<MethodElement>,
}
assert_impl_all!(Method: Send, Sync, Unpin);
impl Method {
pub fn name(&self) -> &str {
&self.name
}
pub fn args(&self) -> Vec<&Arg> {
get_vec!(self.elems, MethodElement::Arg)
}
pub fn annotations(&self) -> Vec<&Annotation> {
get_vec!(self.elems, MethodElement::Annotation)
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "lowercase")]
enum SignalElement {
Arg(Arg),
Annotation(Annotation),
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Signal {
name: String,
#[serde(rename = "$value", default)]
elems: Vec<SignalElement>,
}
assert_impl_all!(Signal: Send, Sync, Unpin);
impl Signal {
pub fn name(&self) -> &str {
&self.name
}
pub fn args(&self) -> Vec<&Arg> {
get_vec!(self.elems, SignalElement::Arg)
}
pub fn annotations(&self) -> Vec<&Annotation> {
get_vec!(self.elems, SignalElement::Annotation)
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Property {
name: String,
r#type: String,
access: String,
#[serde(rename = "annotation", default)]
annotations: Vec<Annotation>,
}
assert_impl_all!(Property: Send, Sync, Unpin);
impl Property {
pub fn name(&self) -> &str {
&self.name
}
pub fn ty(&self) -> &str {
&self.r#type
}
pub fn access(&self) -> &str {
&self.access
}
pub fn annotations(&self) -> Vec<&Annotation> {
self.annotations.iter().collect()
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "lowercase")]
enum InterfaceElement {
Method(Method),
Signal(Signal),
Property(Property),
Annotation(Annotation),
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Interface {
name: String,
#[serde(rename = "$value", default)]
elems: Vec<InterfaceElement>,
}
assert_impl_all!(Interface: Send, Sync, Unpin);
impl Interface {
pub fn name(&self) -> &str {
&self.name
}
pub fn methods(&self) -> Vec<&Method> {
get_vec!(self.elems, InterfaceElement::Method)
}
pub fn signals(&self) -> Vec<&Signal> {
get_vec!(self.elems, InterfaceElement::Signal)
}
pub fn properties(&self) -> Vec<&Property> {
get_vec!(self.elems, InterfaceElement::Property)
}
pub fn annotations(&self) -> Vec<&Annotation> {
get_vec!(self.elems, InterfaceElement::Annotation)
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[serde(rename_all = "lowercase")]
enum NodeElement {
Node(Node),
Interface(Interface),
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Node {
name: Option<String>,
#[serde(rename = "$value", default)]
elems: Vec<NodeElement>,
}
assert_impl_all!(Node: Send, Sync, Unpin);
impl Node {
pub fn from_reader<R: Read>(reader: R) -> Result<Node, Error> {
Ok(from_reader(reader)?)
}
pub fn to_writer<W: Write>(&self, writer: W) -> Result<(), Error> {
Ok(to_writer(writer, &self)?)
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn nodes(&self) -> Vec<&Node> {
get_vec!(self.elems, NodeElement::Node)
}
pub fn interfaces(&self) -> Vec<&Interface> {
get_vec!(self.elems, NodeElement::Interface)
}
}
impl std::str::FromStr for Node {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(from_str(s)?)
}
}
#[cfg(test)]
mod tests {
use std::{error::Error, str::FromStr};
use test_log::test;
use super::Node;
static EXAMPLE: &str = r##"
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/com/example/sample_object0">
<node name="first"/>
<interface name="com.example.SampleInterface0">
<method name="Frobate">
<arg name="foo" type="i" direction="in"/>
<arg name="bar" type="s" direction="out"/>
<arg name="baz" type="a{us}" direction="out"/>
<annotation name="org.freedesktop.DBus.Deprecated" value="true"/>
</method>
<method name="Bazify">
<arg name="bar" type="(iiu)" direction="in"/>
<arg name="bar" type="v" direction="out"/>
</method>
<method name="Mogrify">
<arg name="bar" type="(iiav)" direction="in"/>
</method>
<signal name="Changed">
<arg name="new_value" type="b"/>
</signal>
<property name="Bar" type="y" access="readwrite"/>
</interface>
<node name="child_of_sample_object"/>
<node name="another_child_of_sample_object"/>
</node>
"##;
#[test]
fn serde() -> Result<(), Box<dyn Error>> {
let node = Node::from_reader(EXAMPLE.as_bytes())?;
assert_eq!(node.interfaces().len(), 1);
assert_eq!(node.nodes().len(), 3);
let node_str = Node::from_str(EXAMPLE)?;
assert_eq!(node_str.interfaces().len(), 1);
assert_eq!(node_str.nodes().len(), 3);
Ok(())
}
}