#![doc(html_root_url = "https://docs.rs/zbus-lockstep/0.5.2")]
#![allow(clippy::missing_errors_doc)]
mod error;
mod macros;
use std::{io::Read, str::FromStr};
pub use error::LockstepError;
pub use macros::resolve_xml_path;
pub use zbus_xml::{
self,
ArgDirection::{In, Out},
Node,
};
use zvariant::Signature;
use LockstepError::{ArgumentNotFound, InterfaceNotFound, MemberNotFound, PropertyNotFound};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum MsgType {
Method,
Signal,
Property,
}
pub fn get_signal_body_type(
mut xml: impl Read,
interface_name: &str,
member_name: &str,
arg: Option<&str>,
) -> Result<Signature> {
let node = Node::from_reader(&mut xml)?;
let interfaces = node.interfaces();
let interface = interfaces
.iter()
.find(|iface| iface.name() == interface_name)
.ok_or(InterfaceNotFound(interface_name.to_owned()))?;
let signals = interface.signals();
let signal = signals
.iter()
.find(|signal| signal.name() == member_name)
.ok_or(MemberNotFound(member_name.to_owned()))?;
let signature = {
if let Some(arg_name) = arg {
let args = signal.args();
let arg = args
.iter()
.find(|arg| arg.name() == Some(arg_name))
.ok_or(ArgumentNotFound(arg_name.to_owned()))?;
arg.ty().to_string()
} else {
signal
.args()
.iter()
.map(|arg| arg.ty().to_string())
.collect::<String>()
}
};
Ok(Signature::from_str(&signature).map_err(|_| "Invalid signature")?)
}
pub fn get_property_type(
mut xml: impl Read,
interface_name: &str,
property_name: &str,
) -> Result<Signature> {
let node = Node::from_reader(&mut xml)?;
let interfaces = node.interfaces();
let interface = interfaces
.iter()
.find(|iface| iface.name() == interface_name)
.ok_or(InterfaceNotFound(interface_name.to_string()))?;
let properties = interface.properties();
let property = properties
.iter()
.find(|property| property.name() == property_name)
.ok_or(PropertyNotFound(property_name.to_owned()))?;
let signature = property.ty().to_string();
Ok(Signature::from_str(&signature).map_err(|_| "Invalid signature")?)
}
pub fn get_method_return_type(
mut xml: impl Read,
interface_name: &str,
member_name: &str,
arg_name: Option<&str>,
) -> Result<Signature> {
let node = Node::from_reader(&mut xml)?;
let interfaces = node.interfaces();
let interface = interfaces
.iter()
.find(|iface| iface.name() == interface_name)
.ok_or(InterfaceNotFound(interface_name.to_string()))?;
let methods = interface.methods();
let method = methods
.iter()
.find(|method| method.name() == member_name)
.ok_or(MemberNotFound(member_name.to_string()))?;
let args = method.args();
let signature = {
if let Some(known_arg_name) = arg_name {
args.iter()
.find(|arg| arg.name() == Some(known_arg_name))
.ok_or(ArgumentNotFound(known_arg_name.to_string()))?
.ty()
.to_string()
} else {
args.iter()
.filter(|arg| arg.direction() == Some(Out))
.map(|arg| arg.ty().to_string())
.collect::<String>()
}
};
Ok(Signature::from_str(&signature).map_err(|_| "Invalid signature")?)
}
pub fn get_method_args_type(
mut xml: impl Read,
interface_name: &str,
member_name: &str,
arg_name: Option<&str>,
) -> Result<Signature> {
let node = Node::from_reader(&mut xml)?;
let interfaces = node.interfaces();
let interface = interfaces
.iter()
.find(|iface| iface.name() == interface_name)
.ok_or(InterfaceNotFound(interface_name.to_owned()))?;
let methods = interface.methods();
let method = methods
.iter()
.find(|method| method.name() == member_name)
.ok_or(member_name.to_owned())?;
let args = method.args();
let signature = if let Some(known_arg_name) = arg_name {
args.iter()
.find(|arg| arg.name() == Some(known_arg_name))
.ok_or(ArgumentNotFound(known_arg_name.to_string()))?
.ty()
.to_string()
} else {
args.iter()
.filter(|arg| arg.direction() == Some(In))
.map(|arg| arg.ty().to_string())
.collect::<String>()
};
Ok(Signature::from_str(&signature).map_err(|_| "Invalid signature")?)
}
#[cfg(test)]
mod test {
use std::io::{Seek, SeekFrom, Write};
use tempfile::tempfile;
use zvariant::{OwnedObjectPath, Type};
use crate::get_signal_body_type;
#[test]
fn test_get_signature_of_cache_add_accessible() {
#[derive(Debug, PartialEq, Type)]
struct Accessible {
name: String,
path: OwnedObjectPath,
}
#[derive(Debug, PartialEq, Type)]
struct CacheItem {
obj: Accessible,
application: Accessible,
parent: Accessible,
index_in_parent: i32,
child_count: i32,
interfaces: Vec<String>,
name: String,
role: u32,
description: String,
state_set: Vec<u32>,
}
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
<interface name="org.a11y.atspi.Cache">
<signal name="AddAccessible">
<arg name="nodeAdded" type="((so)(so)(so)iiassusau)"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QSpiAccessibleCacheItem"/>
</signal>
</interface>
</node>
"#;
let mut xml_file = tempfile().unwrap();
xml_file.write_all(xml.as_bytes()).unwrap();
xml_file.seek(SeekFrom::Start(0)).unwrap();
let interface_name = "org.a11y.atspi.Cache";
let member_name = "AddAccessible";
let signature = get_signal_body_type(xml_file, interface_name, member_name, None).unwrap();
assert_eq!(signature, *CacheItem::SIGNATURE);
}
}