use std::{borrow::Borrow, collections::HashMap, marker::PhantomData};
use serde::{Deserialize, Serialize};
use zvariant::{Signature, Type};
use crate::{Interface, InterfaceSet, Role, State, StateSet};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, Type)]
#[repr(u32)]
pub enum TreeTraversalType {
RestrictChildren,
RestrictSibling,
#[default]
Inorder,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct ObjectMatchRule {
pub states: StateSet,
pub states_mt: MatchType,
pub attr: HashMap<String, String>,
pub attr_mt: MatchType,
pub roles: Vec<Role>,
pub roles_mt: MatchType,
pub ifaces: InterfaceSet,
pub ifaces_mt: MatchType,
pub invert: bool,
#[serde(skip)]
_marker: std::marker::PhantomData<()>,
}
impl Type for ObjectMatchRule {
fn signature() -> Signature<'static> {
Signature::from_str_unchecked("(aiia{ss}iaiiasib)")
}
}
impl ObjectMatchRule {
#[must_use]
pub fn builder() -> ObjectMatchRuleBuilder {
ObjectMatchRuleBuilder::default()
}
}
#[derive(Debug, Clone, Default)]
pub struct ObjectMatchRuleBuilder {
states: StateSet,
states_mt: MatchType,
attr: HashMap<String, String>,
attr_mt: MatchType,
roles: Vec<Role>,
roles_mt: MatchType,
ifaces: InterfaceSet,
ifaces_mt: MatchType,
invert: bool,
}
impl ObjectMatchRuleBuilder {
#[must_use]
pub fn states<I>(mut self, states: I, mt: MatchType) -> Self
where
I: IntoIterator,
I::Item: Borrow<State>,
{
self.states = states.into_iter().map(|state| *state.borrow()).collect();
self.states_mt = mt;
self
}
#[must_use]
pub fn attributes(mut self, attributes: HashMap<String, String>, mt: MatchType) -> Self {
self.attr = attributes;
self.attr_mt = mt;
self
}
#[must_use]
pub fn roles(mut self, roles: &[Role], mt: MatchType) -> Self {
self.roles = roles.into();
self.roles_mt = mt;
self
}
#[must_use]
pub fn interfaces<I>(mut self, interfaces: I, mt: MatchType) -> Self
where
I: IntoIterator,
I::Item: Borrow<Interface>,
{
self.ifaces = interfaces.into_iter().map(|iface| *iface.borrow()).collect();
self.ifaces_mt = mt;
self
}
#[must_use]
pub fn invert(mut self, invert: bool) -> Self {
self.invert = invert;
self
}
#[must_use]
pub fn build(self) -> ObjectMatchRule {
ObjectMatchRule {
states: self.states,
states_mt: self.states_mt,
attr: self.attr,
attr_mt: self.attr_mt,
roles: self.roles,
roles_mt: self.roles_mt,
ifaces: self.ifaces,
ifaces_mt: self.ifaces_mt,
invert: self.invert,
_marker: PhantomData,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type, Default)]
#[repr(i32)]
pub enum MatchType {
Invalid,
#[default]
All,
Any,
NA,
Empty,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type)]
#[repr(u32)]
pub enum SortOrder {
Invalid,
Canonical,
Flow,
Tab,
ReverseCanonical,
ReverseFlow,
ReverseTab,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{SortOrder, State};
use zbus_lockstep::method_args_signature;
#[test]
fn validate_match_rule_signature() {
let signature = method_args_signature!(member: "GetMatchesTo", interface: "org.a11y.atspi.Collection", argument: "rule");
assert_eq!(ObjectMatchRule::signature(), signature);
}
#[test]
fn validate_match_type_signature() {
let rule_signature = method_args_signature!(member: "GetMatchesTo", interface: "org.a11y.atspi.Collection", argument: "rule");
let match_type_signature = rule_signature.slice(3..4);
assert_eq!(MatchType::signature(), match_type_signature);
}
#[test]
fn validate_tree_traversal_type_signature() {
let signature = method_args_signature!(member: "GetMatchesTo", interface: "org.a11y.atspi.Collection", argument: "tree");
assert_eq!(TreeTraversalType::signature(), signature);
}
#[test]
fn validate_sort_order_signature() {
let signature = method_args_signature!(member: "GetMatches", interface: "org.a11y.atspi.Collection", argument: "sortby");
assert_eq!(SortOrder::signature(), signature);
}
#[test]
fn create_empty_object_match_rule() {
let rule = ObjectMatchRule::builder().build();
assert_eq!(rule.states, StateSet::default());
assert_eq!(rule.attr, HashMap::new());
assert_eq!(rule.roles, Vec::new());
assert_eq!(rule.ifaces, InterfaceSet::default());
assert!(!rule.invert);
}
#[test]
fn create_object_match_rule() {
let rule = ObjectMatchRule::builder()
.states(StateSet::new(State::Active), MatchType::All)
.attributes(
[("name".to_string(), "value".to_string())].iter().cloned().collect(),
MatchType::Any,
)
.roles(&[Role::Alert], MatchType::All)
.interfaces([Interface::Action], MatchType::Any)
.invert(true)
.build();
assert_eq!(rule.states, StateSet::new(State::Active));
assert_eq!(
rule.attr,
[("name".to_string(), "value".to_string())].iter().cloned().collect()
);
assert_eq!(rule.roles, vec![Role::Alert]);
assert_eq!(rule.ifaces, InterfaceSet::new(Interface::Action));
assert!(rule.invert);
}
}