use crate::{
match_rule::PathSpec,
message::Type,
names::{BusName, InterfaceName, MemberName, UniqueName},
zvariant::{ObjectPath, Str},
Error, MatchRule, Result,
};
const MAX_ARGS: u8 = 64;
#[derive(Debug)]
pub struct Builder<'m>(MatchRule<'m>);
impl<'m> Builder<'m> {
pub fn build(self) -> MatchRule<'m> {
self.0
}
pub fn sender<B>(mut self, sender: B) -> Result<Self>
where
B: TryInto<BusName<'m>>,
B::Error: Into<Error>,
{
self.0.sender = Some(sender.try_into().map_err(Into::into)?);
Ok(self)
}
pub fn msg_type(mut self, msg_type: Type) -> Self {
self.0.msg_type = Some(msg_type);
self
}
pub fn interface<I>(mut self, interface: I) -> Result<Self>
where
I: TryInto<InterfaceName<'m>>,
I::Error: Into<Error>,
{
self.0.interface = Some(interface.try_into().map_err(Into::into)?);
Ok(self)
}
pub fn member<M>(mut self, member: M) -> Result<Self>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
{
self.0.member = Some(member.try_into().map_err(Into::into)?);
Ok(self)
}
pub fn path<P>(mut self, path: P) -> Result<Self>
where
P: TryInto<ObjectPath<'m>>,
P::Error: Into<Error>,
{
self.0.path_spec = path
.try_into()
.map(PathSpec::Path)
.map(Some)
.map_err(Into::into)?;
Ok(self)
}
pub fn path_namespace<P>(mut self, path_namespace: P) -> Result<Self>
where
P: TryInto<ObjectPath<'m>>,
P::Error: Into<Error>,
{
self.0.path_spec = path_namespace
.try_into()
.map(PathSpec::PathNamespace)
.map(Some)
.map_err(Into::into)?;
Ok(self)
}
pub fn destination<B>(mut self, destination: B) -> Result<Self>
where
B: TryInto<UniqueName<'m>>,
B::Error: Into<Error>,
{
self.0.destination = Some(destination.try_into().map_err(Into::into)?);
Ok(self)
}
pub fn add_arg<S>(self, arg: S) -> Result<Self>
where
S: Into<Str<'m>>,
{
let idx = self.0.args.len() as u8;
self.arg(idx, arg)
}
pub fn arg<S>(mut self, idx: u8, arg: S) -> Result<Self>
where
S: Into<Str<'m>>,
{
if idx >= MAX_ARGS {
return Err(Error::InvalidMatchRule);
}
let value = (idx, arg.into());
let vec_idx = match self.0.args().binary_search_by(|(i, _)| i.cmp(&idx)) {
Ok(i) => {
self.0.args.remove(i);
i
}
Err(i) => i,
};
self.0.args.insert(vec_idx, value);
Ok(self)
}
pub fn add_arg_path<P>(self, arg_path: P) -> Result<Self>
where
P: TryInto<ObjectPath<'m>>,
P::Error: Into<Error>,
{
let idx = self.0.arg_paths.len() as u8;
self.arg_path(idx, arg_path)
}
pub fn arg_path<P>(mut self, idx: u8, arg_path: P) -> Result<Self>
where
P: TryInto<ObjectPath<'m>>,
P::Error: Into<Error>,
{
if idx >= MAX_ARGS {
return Err(Error::InvalidMatchRule);
}
let value = (idx, arg_path.try_into().map_err(Into::into)?);
let vec_idx = match self.0.arg_paths().binary_search_by(|(i, _)| i.cmp(&idx)) {
Ok(i) => {
self.0.arg_paths.remove(i);
i
}
Err(i) => i,
};
self.0.arg_paths.insert(vec_idx, value);
Ok(self)
}
pub fn arg0ns<S>(mut self, namespace: S) -> Result<Self>
where
S: Into<Str<'m>>,
{
let namespace: Str<'m> = namespace.into();
if namespace.is_empty() || namespace.len() > 255 {
return Err(Error::InvalidMatchRule);
}
let (is_unique, namespace_str) = match namespace.strip_prefix(':') {
Some(s) => (true, s),
None => (false, namespace.as_str()),
};
let valid_first_char = |s: &str| match s.chars().next() {
None | Some('.') => false,
Some('0'..='9') if !is_unique => false,
_ => true,
};
if !valid_first_char(namespace_str)
|| !namespace_str.split('.').all(valid_first_char)
|| !namespace_str
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_' || c == '.')
{
return Err(Error::InvalidMatchRule);
}
self.0.arg0ns = Some(namespace);
Ok(self)
}
pub(crate) fn new() -> Self {
Self(MatchRule {
msg_type: None,
sender: None,
interface: None,
member: None,
path_spec: None,
destination: None,
args: Vec::with_capacity(MAX_ARGS as usize),
arg_paths: Vec::with_capacity(MAX_ARGS as usize),
arg0ns: None,
})
}
}