use std::fmt;
use crate::pkg::Restrict as PkgRestrict;
pub mod atom;
pub(crate) mod boolean;
pub mod ordered;
pub mod parse;
pub mod set;
pub mod str;
boolean::restrict_with_boolean! {Restrict,
True,
False,
Atom(atom::Restrict),
Pkg(PkgRestrict),
Str(str::Restrict),
}
macro_rules! restrict_match {
($r:expr, $obj:expr, $($matcher:pat $(if $pred:expr)* => $result:expr,)+) => {
match $r {
$($matcher $(if $pred)* => $result,)+
Self::True => true,
Self::False => false,
Self::And(vals) => vals.iter().all(|r| r.matches($obj)),
Self::Or(vals) => vals.iter().any(|r| r.matches($obj)),
Self::Xor(vals) => {
let mut curr: Option<bool>;
let mut prev: Option<bool> = None;
for r in vals.iter() {
curr = Some(r.matches($obj));
if prev.is_some() && curr != prev {
return true;
}
prev = curr
}
false
},
Self::Not(r) => !r.matches($obj),
_ => {
tracing::warn!("invalid restriction {:?} for matching {:?}", $r, $obj);
false
}
}
}
}
pub(crate) use restrict_match;
impl Restrict {
boolean::restrict_impl_boolean! {Self}
}
boolean::restrict_ops_boolean!(Restrict);
pub trait Restriction<T>: fmt::Debug {
fn matches(&self, object: T) -> bool;
}
impl Restriction<&str> for Restrict {
fn matches(&self, s: &str) -> bool {
restrict_match! {self, s,
Self::Str(r) => r.matches(s),
}
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use crate::atom::Atom;
use super::*;
#[test]
fn test_filtering() {
let atom_strs = vec!["cat/pkg", ">=cat/pkg-1", "=cat/pkg-1:2/3::repo"];
let atoms: Vec<Atom> = atom_strs
.iter()
.map(|s| Atom::from_str(s).unwrap())
.collect();
let filter = |r: Restrict, atoms: Vec<Atom>| -> Vec<String> {
atoms
.into_iter()
.filter(|a| r.matches(a))
.map(|a| a.to_string())
.collect()
};
let r = Restrict::Atom(atom::Restrict::category("cat"));
assert_eq!(filter(r, atoms.clone()), atom_strs);
let r = Restrict::Atom(atom::Restrict::Version(None));
assert_eq!(filter(r, atoms.clone()), ["cat/pkg"]);
let cpv = Atom::from_str("=cat/pkg-1").unwrap();
let r = Restrict::from(&cpv);
assert_eq!(filter(r, atoms.clone()), [">=cat/pkg-1", "=cat/pkg-1:2/3::repo"]);
let r = Restrict::True;
assert_eq!(filter(r, atoms.clone()), atom_strs);
let r = Restrict::False;
assert!(filter(r, atoms).is_empty());
}
}