criteria: add infrastructure
This commit is contained in:
parent
597636fba6
commit
17e715cde4
18 changed files with 1214 additions and 3 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -602,6 +602,7 @@ dependencies = [
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"png",
|
"png",
|
||||||
"rand",
|
"rand",
|
||||||
|
"regex",
|
||||||
"repc",
|
"repc",
|
||||||
"rustc-demangle",
|
"rustc-demangle",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ rustc-demangle = { version = "0.1.24", optional = true }
|
||||||
tracy-client-sys = { version = "0.24.1", features = ["ondemand", "manual-lifetime", "debuginfod", "demangle"], optional = true }
|
tracy-client-sys = { version = "0.24.1", features = ["ondemand", "manual-lifetime", "debuginfod", "demangle"], optional = true }
|
||||||
kbvm = "0.1.4"
|
kbvm = "0.1.4"
|
||||||
tiny-skia = { version = "0.11.4", default-features = false, features = ["std"] }
|
tiny-skia = { version = "0.11.4", default-features = false, features = ["std"] }
|
||||||
|
regex = "1.11.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
repc = "0.1.1"
|
repc = "0.1.1"
|
||||||
|
|
|
||||||
96
src/criteria.rs
Normal file
96
src/criteria.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
mod crit_graph;
|
||||||
|
pub mod crit_leaf;
|
||||||
|
mod crit_matchers;
|
||||||
|
mod crit_per_target_data;
|
||||||
|
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
criteria::{
|
||||||
|
crit_graph::{CritMgr, CritMiddle, CritRoot, CritRootCriterion, CritRootFixed},
|
||||||
|
crit_leaf::CritLeafMatcher,
|
||||||
|
crit_matchers::{critm_any_or_all::CritMatchAnyOrAll, critm_exactly::CritMatchExactly},
|
||||||
|
},
|
||||||
|
utils::copyhashmap::CopyHashMap,
|
||||||
|
},
|
||||||
|
linearize::StaticMap,
|
||||||
|
regex::Regex,
|
||||||
|
std::rc::{Rc, Weak},
|
||||||
|
};
|
||||||
|
pub use {
|
||||||
|
crit_graph::{CritTarget, CritUpstreamNode},
|
||||||
|
crit_per_target_data::CritDestroyListener,
|
||||||
|
};
|
||||||
|
|
||||||
|
linear_ids!(CritMatcherIds, CritMatcherId, u64);
|
||||||
|
|
||||||
|
type RootMatcherMap<Target, T> = CopyHashMap<CritMatcherId, Weak<CritRoot<Target, T>>>;
|
||||||
|
type FixedRootMatcher<Target, T> = StaticMap<bool, Rc<CritRoot<Target, CritRootFixed<Target, T>>>>;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub enum CritLiteralOrRegex {
|
||||||
|
Literal(String),
|
||||||
|
Regex(Regex),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CritLiteralOrRegex {
|
||||||
|
fn matches(&self, string: &str) -> bool {
|
||||||
|
match self {
|
||||||
|
CritLiteralOrRegex::Literal(p) => string == p,
|
||||||
|
CritLiteralOrRegex::Regex(r) => r.is_match(string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub trait CritMgrExt: CritMgr {
|
||||||
|
fn list(
|
||||||
|
&self,
|
||||||
|
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
|
||||||
|
all: bool,
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||||
|
if upstream.is_empty() {
|
||||||
|
return self.match_constant()[all].clone();
|
||||||
|
}
|
||||||
|
CritMiddle::new(self, upstream, CritMatchAnyOrAll::new(upstream, all))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exactly(
|
||||||
|
&self,
|
||||||
|
upstream: &[Rc<dyn CritUpstreamNode<Self::Target>>],
|
||||||
|
num: usize,
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||||
|
if num > upstream.len() {
|
||||||
|
return self.match_constant()[false].clone();
|
||||||
|
}
|
||||||
|
if num == 0 {
|
||||||
|
let upstream: Vec<_> = upstream.iter().map(|u| u.not(self)).collect();
|
||||||
|
return self.list(&upstream, true);
|
||||||
|
}
|
||||||
|
CritMiddle::new(self, upstream, CritMatchExactly::new(upstream, num))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn leaf(
|
||||||
|
&self,
|
||||||
|
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
|
||||||
|
on_match: impl Fn(<Self::Target as CritTarget>::LeafData) -> Box<dyn FnOnce()> + 'static,
|
||||||
|
) -> Rc<CritLeafMatcher<Self::Target>> {
|
||||||
|
CritLeafMatcher::new(self, upstream, on_match)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(
|
||||||
|
&self,
|
||||||
|
upstream: &Rc<dyn CritUpstreamNode<Self::Target>>,
|
||||||
|
) -> Rc<dyn CritUpstreamNode<Self::Target>> {
|
||||||
|
upstream.not(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn root<T>(&self, criterion: T) -> Rc<dyn CritUpstreamNode<Self::Target>>
|
||||||
|
where
|
||||||
|
T: CritRootCriterion<Self::Target>,
|
||||||
|
{
|
||||||
|
CritRoot::new(self.roots(), self.id(), criterion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> CritMgrExt for T where T: CritMgr {}
|
||||||
16
src/criteria/crit_graph.rs
Normal file
16
src/criteria/crit_graph.rs
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
mod crit_downstream;
|
||||||
|
mod crit_middle;
|
||||||
|
mod crit_root;
|
||||||
|
mod crit_target;
|
||||||
|
mod crit_upstream;
|
||||||
|
|
||||||
|
pub use {
|
||||||
|
crit_downstream::{CritDownstream, CritDownstreamData},
|
||||||
|
crit_middle::{CritMiddle, CritMiddleCriterion},
|
||||||
|
crit_root::{
|
||||||
|
CritFixedRootCriterion, CritFixedRootCriterionBase, CritRoot, CritRootCriterion,
|
||||||
|
CritRootFixed,
|
||||||
|
},
|
||||||
|
crit_target::{CritMgr, CritTarget, CritTargetOwner, WeakCritTargetOwner},
|
||||||
|
crit_upstream::{CritUpstreamData, CritUpstreamNode},
|
||||||
|
};
|
||||||
52
src/criteria/crit_graph/crit_downstream.rs
Normal file
52
src/criteria/crit_graph/crit_downstream.rs
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
use {
|
||||||
|
crate::criteria::{
|
||||||
|
CritMatcherId,
|
||||||
|
crit_graph::{CritTarget, crit_upstream::CritUpstreamNode},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CritDownstreamData<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
id: CritMatcherId,
|
||||||
|
pub(super) upstream: Vec<Rc<dyn CritUpstreamNode<Target>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CritDownstream<Target>: 'static {
|
||||||
|
fn update_matched(self: Rc<Self>, target: &Target, matched: bool);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> CritDownstreamData<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
pub fn new(id: CritMatcherId, upstream: &[Rc<dyn CritUpstreamNode<Target>>]) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
upstream: upstream.to_vec(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attach(&self, slf: &Rc<impl CritDownstream<Target>>) {
|
||||||
|
for upstream in &self.upstream {
|
||||||
|
upstream.attach(self.id, slf.clone() as _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn not(&self, mgr: &Target::Mgr) -> Vec<Rc<dyn CritUpstreamNode<Target>>> {
|
||||||
|
self.upstream.iter().map(|n| n.not(mgr)).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> Drop for CritDownstreamData<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
for el in &self.upstream {
|
||||||
|
el.detach(self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
111
src/criteria/crit_graph/crit_middle.rs
Normal file
111
src/criteria/crit_graph/crit_middle.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
use {
|
||||||
|
crate::criteria::{
|
||||||
|
CritUpstreamNode,
|
||||||
|
crit_graph::{
|
||||||
|
CritDownstream, CritDownstreamData, CritTarget, CritUpstreamData,
|
||||||
|
crit_target::CritMgr,
|
||||||
|
crit_upstream::{CritUpstreamNodeBase, CritUpstreamNodeData},
|
||||||
|
},
|
||||||
|
crit_per_target_data::{CritDestroyListenerBase, CritPerTargetData},
|
||||||
|
},
|
||||||
|
std::rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CritMiddle<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritMiddleCriterion<Target>,
|
||||||
|
{
|
||||||
|
upstream: CritDownstreamData<Target>,
|
||||||
|
downstream: CritUpstreamData<Target, CritMiddleData<T::Data>>,
|
||||||
|
criterion: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct CritMiddleData<T> {
|
||||||
|
matches: usize,
|
||||||
|
data: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CritMiddleCriterion<Target>: Sized + 'static {
|
||||||
|
type Data: Default;
|
||||||
|
type Not: CritMiddleCriterion<Target>;
|
||||||
|
|
||||||
|
fn update_matched(&self, target: &Target, node: &mut Self::Data, matched: bool) -> bool;
|
||||||
|
fn pull(&self, upstream: &[Rc<dyn CritUpstreamNode<Target>>], target: &Target) -> bool;
|
||||||
|
fn not(&self) -> Self::Not;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> CritMiddle<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritMiddleCriterion<Target>,
|
||||||
|
{
|
||||||
|
pub fn new(
|
||||||
|
mgr: &Target::Mgr,
|
||||||
|
upstream: &[Rc<dyn CritUpstreamNode<Target>>],
|
||||||
|
criterion: T,
|
||||||
|
) -> Rc<Self> {
|
||||||
|
let id = mgr.id();
|
||||||
|
let slf = Rc::new_cyclic(|slf| Self {
|
||||||
|
upstream: CritDownstreamData::new(id, upstream),
|
||||||
|
downstream: CritUpstreamData::new(slf, id),
|
||||||
|
criterion,
|
||||||
|
});
|
||||||
|
slf.upstream.attach(&slf);
|
||||||
|
slf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> CritDownstream<Target> for CritMiddle<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritMiddleCriterion<Target>,
|
||||||
|
{
|
||||||
|
fn update_matched(self: Rc<Self>, target: &Target, matched: bool) {
|
||||||
|
let mut node = self.downstream.get_or_create(target);
|
||||||
|
let change = self
|
||||||
|
.criterion
|
||||||
|
.update_matched(target, &mut node.data, matched);
|
||||||
|
let matches = match matched {
|
||||||
|
true => node.matches + 1,
|
||||||
|
false => node.matches - 1,
|
||||||
|
};
|
||||||
|
node.matches = matches;
|
||||||
|
self.downstream
|
||||||
|
.update_matched(target, node, change, matches == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> CritUpstreamNodeBase<Target> for CritMiddle<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritMiddleCriterion<Target>,
|
||||||
|
{
|
||||||
|
type Data = CritMiddleData<T::Data>;
|
||||||
|
|
||||||
|
fn data(&self) -> &CritUpstreamData<Target, Self::Data> {
|
||||||
|
&self.downstream
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(&self, mgr: &Target::Mgr) -> Rc<dyn CritUpstreamNode<Target>> {
|
||||||
|
let upstream = self.upstream.not(mgr);
|
||||||
|
CritMiddle::new(mgr, &upstream, self.criterion.not())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pull(&self, target: &Target) -> bool {
|
||||||
|
self.criterion.pull(&self.upstream.upstream, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> CritDestroyListenerBase<Target> for CritMiddle<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritMiddleCriterion<Target>,
|
||||||
|
{
|
||||||
|
type Data = CritUpstreamNodeData<Target, CritMiddleData<T::Data>>;
|
||||||
|
|
||||||
|
fn data(&self) -> &CritPerTargetData<Target, Self::Data> {
|
||||||
|
&self.downstream.nodes
|
||||||
|
}
|
||||||
|
}
|
||||||
173
src/criteria/crit_graph/crit_root.rs
Normal file
173
src/criteria/crit_graph/crit_root.rs
Normal file
|
|
@ -0,0 +1,173 @@
|
||||||
|
use {
|
||||||
|
crate::criteria::{
|
||||||
|
CritMatcherId, CritUpstreamNode, FixedRootMatcher, RootMatcherMap,
|
||||||
|
crit_graph::{
|
||||||
|
CritTarget, CritUpstreamData,
|
||||||
|
crit_target::CritMgr,
|
||||||
|
crit_upstream::{CritUpstreamNodeBase, CritUpstreamNodeData},
|
||||||
|
},
|
||||||
|
crit_per_target_data::{CritDestroyListenerBase, CritPerTargetData},
|
||||||
|
},
|
||||||
|
std::{marker::PhantomData, rc::Rc},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CritRoot<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritRootCriterion<Target>,
|
||||||
|
{
|
||||||
|
id: CritMatcherId,
|
||||||
|
downstream: CritUpstreamData<Target, ()>,
|
||||||
|
not: bool,
|
||||||
|
criterion: Rc<T>,
|
||||||
|
roots: Rc<Target::RootMatchers>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CritRootFixed<Target, Crit>(pub Crit, pub PhantomData<fn(&Target)>);
|
||||||
|
|
||||||
|
pub trait CritRootCriterion<Target>: Sized + 'static
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
fn matches(&self, data: &Target) -> bool;
|
||||||
|
fn nodes(roots: &Target::RootMatchers) -> Option<&RootMatcherMap<Target, Self>> {
|
||||||
|
let _ = roots;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
fn not(&self, mgr: &Target::Mgr) -> Option<Rc<dyn CritUpstreamNode<Target>>> {
|
||||||
|
let _ = mgr;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CritFixedRootCriterionBase<Target>: Sized + 'static
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
fn constant(&self) -> bool;
|
||||||
|
fn not<'a>(&self, mgr: &'a Target::Mgr) -> &'a FixedRootMatcher<Target, Self>
|
||||||
|
where
|
||||||
|
Self: CritFixedRootCriterion<Target>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CritFixedRootCriterion<Target>: CritFixedRootCriterionBase<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
const COMPARE: bool = true;
|
||||||
|
|
||||||
|
fn matches(&self, data: &Target) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> CritRootCriterion<Target> for CritRootFixed<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritFixedRootCriterion<Target>,
|
||||||
|
{
|
||||||
|
fn matches(&self, data: &Target) -> bool {
|
||||||
|
let mut res = self.0.matches(data);
|
||||||
|
if T::COMPARE {
|
||||||
|
res = res == self.0.constant();
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(&self, mgr: &Target::Mgr) -> Option<Rc<dyn CritUpstreamNode<Target>>> {
|
||||||
|
Some(self.0.not(mgr)[!self.0.constant()].clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> CritUpstreamNodeBase<Target> for CritRoot<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritRootCriterion<Target>,
|
||||||
|
{
|
||||||
|
type Data = ();
|
||||||
|
|
||||||
|
fn data(&self) -> &CritUpstreamData<Target, Self::Data> {
|
||||||
|
&self.downstream
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(&self, mgr: &Target::Mgr) -> Rc<dyn CritUpstreamNode<Target>> {
|
||||||
|
if let Some(node) = self.criterion.not(mgr) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
let id = mgr.id();
|
||||||
|
Self::new_(&self.roots, id, self.criterion.clone(), !self.not)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pull(&self, target: &Target) -> bool {
|
||||||
|
self.criterion.matches(target) ^ self.not
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> CritRoot<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritRootCriterion<Target>,
|
||||||
|
{
|
||||||
|
pub fn new(roots: &Rc<Target::RootMatchers>, id: CritMatcherId, criterion: T) -> Rc<Self> {
|
||||||
|
Self::new_(roots, id, Rc::new(criterion), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_(
|
||||||
|
roots: &Rc<Target::RootMatchers>,
|
||||||
|
id: CritMatcherId,
|
||||||
|
criterion: Rc<T>,
|
||||||
|
not: bool,
|
||||||
|
) -> Rc<Self> {
|
||||||
|
let slf = Rc::new_cyclic(|slf| Self {
|
||||||
|
id,
|
||||||
|
downstream: CritUpstreamData::new(slf, id),
|
||||||
|
not,
|
||||||
|
criterion,
|
||||||
|
roots: roots.clone(),
|
||||||
|
});
|
||||||
|
if let Some(nodes) = T::nodes(roots) {
|
||||||
|
nodes.set(id, Rc::downgrade(&slf));
|
||||||
|
}
|
||||||
|
slf
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub fn handle(&self, target: &Target) {
|
||||||
|
let new = self.criterion.matches(target) ^ self.not;
|
||||||
|
let node = match new {
|
||||||
|
true => self.downstream.get_or_create(target),
|
||||||
|
false => match self.downstream.get(target) {
|
||||||
|
Some(n) => n,
|
||||||
|
None => return,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.downstream.update_matched(target, node, new, !new);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub fn has_downstream(&self) -> bool {
|
||||||
|
self.downstream.has_downstream()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> CritDestroyListenerBase<Target> for CritRoot<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritRootCriterion<Target>,
|
||||||
|
{
|
||||||
|
type Data = CritUpstreamNodeData<Target, ()>;
|
||||||
|
|
||||||
|
fn data(&self) -> &CritPerTargetData<Target, Self::Data> {
|
||||||
|
&self.downstream.nodes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> Drop for CritRoot<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritRootCriterion<Target>,
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(nodes) = T::nodes(&self.roots) {
|
||||||
|
nodes.remove(&self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
48
src/criteria/crit_graph/crit_target.rs
Normal file
48
src/criteria/crit_graph/crit_target.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
criteria::{
|
||||||
|
CritDestroyListener, CritMatcherId, FixedRootMatcher, crit_leaf::CritLeafEvent,
|
||||||
|
crit_matchers::critm_constant::CritMatchConstant,
|
||||||
|
},
|
||||||
|
utils::{copyhashmap::CopyHashMap, queue::AsyncQueue},
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
hash::Hash,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait CritMgr: 'static {
|
||||||
|
type Target: CritTarget<Mgr = Self>;
|
||||||
|
|
||||||
|
fn id(&self) -> CritMatcherId;
|
||||||
|
fn leaf_events(&self) -> &Rc<AsyncQueue<CritLeafEvent<Self::Target>>>;
|
||||||
|
fn match_constant(&self) -> &FixedRootMatcher<Self::Target, CritMatchConstant<Self::Target>>;
|
||||||
|
fn roots(&self) -> &Rc<<Self::Target as CritTarget>::RootMatchers>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CritTarget: 'static {
|
||||||
|
type Id: Copy + Hash + Eq;
|
||||||
|
type Mgr: CritMgr<Target = Self>;
|
||||||
|
type RootMatchers;
|
||||||
|
type LeafData: Copy + Eq;
|
||||||
|
type Owner: WeakCritTargetOwner<Target = Self>;
|
||||||
|
|
||||||
|
fn owner(&self) -> Self::Owner;
|
||||||
|
fn id(&self) -> Self::Id;
|
||||||
|
fn destroyed(&self) -> &CopyHashMap<CritMatcherId, Weak<dyn CritDestroyListener<Self>>>;
|
||||||
|
fn leaf_data(&self) -> Self::LeafData;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CritTargetOwner: 'static {
|
||||||
|
type Target: CritTarget;
|
||||||
|
|
||||||
|
fn data(&self) -> &Self::Target;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WeakCritTargetOwner: 'static {
|
||||||
|
type Target: CritTarget;
|
||||||
|
type Owner: CritTargetOwner<Target = Self::Target>;
|
||||||
|
|
||||||
|
fn upgrade(&self) -> Option<Self::Owner>;
|
||||||
|
}
|
||||||
176
src/criteria/crit_graph/crit_upstream.rs
Normal file
176
src/criteria/crit_graph/crit_upstream.rs
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
criteria::{
|
||||||
|
CritDestroyListener, CritMatcherId,
|
||||||
|
crit_graph::{
|
||||||
|
WeakCritTargetOwner,
|
||||||
|
crit_downstream::CritDownstream,
|
||||||
|
crit_target::{CritTarget, CritTargetOwner},
|
||||||
|
},
|
||||||
|
crit_per_target_data::CritPerTargetData,
|
||||||
|
},
|
||||||
|
utils::copyhashmap::CopyHashMap,
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
cell::RefMut,
|
||||||
|
mem,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CritUpstreamData<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
downstream: CopyHashMap<CritMatcherId, Weak<dyn CritDownstream<Target>>>,
|
||||||
|
pub nodes: CritPerTargetData<Target, CritUpstreamNodeData<Target, T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CritUpstreamNodeData<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
matched: bool,
|
||||||
|
tl: Target::Owner,
|
||||||
|
data: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CritUpstreamNodeBase<Target>: 'static
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
type Data;
|
||||||
|
|
||||||
|
fn data(&self) -> &CritUpstreamData<Target, Self::Data>;
|
||||||
|
fn not(&self, mgr: &Target::Mgr) -> Rc<dyn CritUpstreamNode<Target>>;
|
||||||
|
fn pull(&self, target: &Target) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CritUpstreamNode<Target>: 'static
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
fn attach(&self, id: CritMatcherId, downstream: Rc<dyn CritDownstream<Target>>);
|
||||||
|
fn detach(&self, id: CritMatcherId);
|
||||||
|
fn not(&self, mgr: &Target::Mgr) -> Rc<dyn CritUpstreamNode<Target>>;
|
||||||
|
fn pull(&self, target: &Target) -> bool;
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn get(&self, target: &Target) -> bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> CritUpstreamNode<Target> for T
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritUpstreamNodeBase<Target>,
|
||||||
|
{
|
||||||
|
fn attach(&self, id: CritMatcherId, downstream: Rc<dyn CritDownstream<Target>>) {
|
||||||
|
let data = self.data();
|
||||||
|
for n in data.nodes.borrow_mut().values_mut() {
|
||||||
|
if !n.matched {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Some(target) = n.tl.upgrade() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
downstream.clone().update_matched(target.data(), true);
|
||||||
|
}
|
||||||
|
data.downstream.set(id, Rc::downgrade(&downstream));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn detach(&self, id: CritMatcherId) {
|
||||||
|
self.data().downstream.remove(&id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(&self, mgr: &Target::Mgr) -> Rc<dyn CritUpstreamNode<Target>> {
|
||||||
|
<T as CritUpstreamNodeBase<Target>>::not(self, mgr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pull(&self, target: &Target) -> bool {
|
||||||
|
<T as CritUpstreamNodeBase<Target>>::pull(self, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, target: &Target) -> bool {
|
||||||
|
<T as CritUpstreamNodeBase<Target>>::data(self).matched(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> Deref for CritUpstreamNodeData<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> DerefMut for CritUpstreamNodeData<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> CritUpstreamData<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
pub fn new(slf: &Weak<impl CritDestroyListener<Target>>, id: CritMatcherId) -> Self {
|
||||||
|
Self {
|
||||||
|
downstream: Default::default(),
|
||||||
|
nodes: CritPerTargetData::new(slf, id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_matched(
|
||||||
|
&self,
|
||||||
|
target: &Target,
|
||||||
|
mut node: RefMut<CritUpstreamNodeData<Target, T>>,
|
||||||
|
matched: bool,
|
||||||
|
remove: bool,
|
||||||
|
) {
|
||||||
|
let unchanged = mem::replace(&mut node.matched, matched) == matched;
|
||||||
|
drop(node);
|
||||||
|
if remove {
|
||||||
|
self.nodes.remove(target.id());
|
||||||
|
}
|
||||||
|
if unchanged {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for el in self.downstream.lock().values() {
|
||||||
|
if let Some(el) = el.upgrade() {
|
||||||
|
el.update_matched(target, matched);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_create(&self, target: &Target) -> RefMut<CritUpstreamNodeData<Target, T>>
|
||||||
|
where
|
||||||
|
T: Default,
|
||||||
|
{
|
||||||
|
self.nodes.get_or_create(target, || CritUpstreamNodeData {
|
||||||
|
matched: false,
|
||||||
|
tl: target.owner(),
|
||||||
|
data: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, target: &Target) -> Option<RefMut<CritUpstreamNodeData<Target, T>>> {
|
||||||
|
self.nodes.get(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_downstream(&self) -> bool {
|
||||||
|
self.downstream.is_not_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn matched(&self, target: &Target) -> bool {
|
||||||
|
let Some(node) = self.nodes.get(target) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
node.matched
|
||||||
|
}
|
||||||
|
}
|
||||||
156
src/criteria/crit_leaf.rs
Normal file
156
src/criteria/crit_leaf.rs
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
criteria::{
|
||||||
|
CritUpstreamNode,
|
||||||
|
crit_graph::{CritDownstream, CritDownstreamData, CritMgr, CritTarget},
|
||||||
|
crit_per_target_data::{CritDestroyListenerBase, CritPerTargetData},
|
||||||
|
},
|
||||||
|
utils::{cell_ext::CellExt, queue::AsyncQueue},
|
||||||
|
},
|
||||||
|
std::{
|
||||||
|
cell::Cell,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CritLeafMatcher<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
upstream: CritDownstreamData<Target>,
|
||||||
|
on_match: Box<dyn Fn(Target::LeafData) -> Box<dyn FnOnce()>>,
|
||||||
|
targets: CritPerTargetData<Target, NodeHolder<Target>>,
|
||||||
|
events: Rc<AsyncQueue<CritLeafEvent<Target>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(in crate::criteria) struct NodeHolder<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
node: Rc<Node<Target>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Node<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
leaf: Weak<CritLeafMatcher<Target>>,
|
||||||
|
target_id: Target::Id,
|
||||||
|
needs_event: Cell<bool>,
|
||||||
|
new_data: Cell<Option<Target::LeafData>>,
|
||||||
|
data: Cell<Option<Target::LeafData>>,
|
||||||
|
on_unmatch: Cell<Option<Box<dyn FnOnce()>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CritLeafEvent<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
node: Rc<Node<Target>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> CritDownstream<Target> for CritLeafMatcher<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
fn update_matched(self: Rc<Self>, data: &Target, matched: bool) {
|
||||||
|
let node = &self
|
||||||
|
.targets
|
||||||
|
.get_or_create(data, || {
|
||||||
|
let node = Rc::new(Node {
|
||||||
|
leaf: Rc::downgrade(&self),
|
||||||
|
target_id: data.id(),
|
||||||
|
needs_event: Cell::new(true),
|
||||||
|
new_data: Cell::new(None),
|
||||||
|
data: Cell::new(None),
|
||||||
|
on_unmatch: Cell::new(None),
|
||||||
|
});
|
||||||
|
NodeHolder { node: node.clone() }
|
||||||
|
})
|
||||||
|
.node;
|
||||||
|
self.push_event(node, matched.then_some(data.leaf_data()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> CritLeafMatcher<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
pub(in crate::criteria) fn new(
|
||||||
|
mgr: &Target::Mgr,
|
||||||
|
upstream: &Rc<dyn CritUpstreamNode<Target>>,
|
||||||
|
on_match: impl Fn(Target::LeafData) -> Box<dyn FnOnce()> + 'static,
|
||||||
|
) -> Rc<Self> {
|
||||||
|
let id = mgr.id();
|
||||||
|
let slf = Rc::new_cyclic(|slf| Self {
|
||||||
|
targets: CritPerTargetData::new(slf, id),
|
||||||
|
on_match: Box::new(on_match),
|
||||||
|
events: mgr.leaf_events().clone(),
|
||||||
|
upstream: CritDownstreamData::new(id, &[upstream.clone()]),
|
||||||
|
});
|
||||||
|
slf.upstream.attach(&slf);
|
||||||
|
slf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_event(&self, node: &Rc<Node<Target>>, new_data: Option<Target::LeafData>) {
|
||||||
|
node.new_data.set(new_data);
|
||||||
|
if node.needs_event.replace(false) {
|
||||||
|
self.events.push(CritLeafEvent { node: node.clone() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> CritLeafEvent<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub fn run(self) {
|
||||||
|
let n = self.node;
|
||||||
|
n.needs_event.set(true);
|
||||||
|
if n.new_data != n.data {
|
||||||
|
if let Some(on_unmatch) = n.on_unmatch.take() {
|
||||||
|
if n.leaf.strong_count() == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
on_unmatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n.data.set(n.new_data.get());
|
||||||
|
if n.data.is_some() != n.on_unmatch.is_some() {
|
||||||
|
let Some(leaf) = n.leaf.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(id) = n.data.get() {
|
||||||
|
n.on_unmatch.set(Some((leaf.on_match)(id)));
|
||||||
|
} else {
|
||||||
|
if let Some(on_unmatch) = n.on_unmatch.take() {
|
||||||
|
on_unmatch();
|
||||||
|
}
|
||||||
|
leaf.targets.remove(n.target_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> Drop for NodeHolder<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(leaf) = self.node.leaf.upgrade() {
|
||||||
|
leaf.push_event(&self.node, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> CritDestroyListenerBase<Target> for CritLeafMatcher<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
type Data = NodeHolder<Target>;
|
||||||
|
|
||||||
|
fn data(&self) -> &CritPerTargetData<Target, Self::Data> {
|
||||||
|
&self.targets
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/criteria/crit_matchers.rs
Normal file
4
src/criteria/crit_matchers.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod critm_any_or_all;
|
||||||
|
pub mod critm_constant;
|
||||||
|
pub mod critm_exactly;
|
||||||
|
pub mod critm_string;
|
||||||
73
src/criteria/crit_matchers/critm_any_or_all.rs
Normal file
73
src/criteria/crit_matchers/critm_any_or_all.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
use {
|
||||||
|
crate::criteria::crit_graph::{CritMiddleCriterion, CritTarget, CritUpstreamNode},
|
||||||
|
std::{marker::PhantomData, rc::Rc},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CritMatchAnyOrAll<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
all: bool,
|
||||||
|
total: usize,
|
||||||
|
_phantom: PhantomData<fn(&Target)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> CritMatchAnyOrAll<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
pub fn new(upstream: &[Rc<dyn CritUpstreamNode<Target>>], all: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
all,
|
||||||
|
total: upstream.len(),
|
||||||
|
_phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> CritMiddleCriterion<Target> for CritMatchAnyOrAll<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
type Data = usize;
|
||||||
|
type Not = Self;
|
||||||
|
|
||||||
|
fn update_matched(&self, _data: &Target, node: &mut usize, matched: bool) -> bool {
|
||||||
|
if matched {
|
||||||
|
*node += 1;
|
||||||
|
} else {
|
||||||
|
*node -= 1;
|
||||||
|
}
|
||||||
|
if self.all {
|
||||||
|
*node == self.total
|
||||||
|
} else {
|
||||||
|
*node > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pull(&self, upstream: &[Rc<dyn CritUpstreamNode<Target>>], node: &Target) -> bool {
|
||||||
|
for upstream in upstream {
|
||||||
|
if upstream.pull(node) {
|
||||||
|
if !self.all {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if self.all {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.all
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(&self) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
all: !self.all,
|
||||||
|
total: self.total,
|
||||||
|
_phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
59
src/criteria/crit_matchers/critm_constant.rs
Normal file
59
src/criteria/crit_matchers/critm_constant.rs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
use {
|
||||||
|
crate::criteria::{
|
||||||
|
CritMatcherIds, FixedRootMatcher,
|
||||||
|
crit_graph::{
|
||||||
|
CritFixedRootCriterion, CritFixedRootCriterionBase, CritMgr, CritRoot, CritRootFixed,
|
||||||
|
CritTarget,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
linearize::static_map,
|
||||||
|
std::{marker::PhantomData, rc::Rc},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CritMatchConstant<Target>(pub bool, pub PhantomData<fn(&Target)>);
|
||||||
|
|
||||||
|
impl<Target> CritMatchConstant<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub fn create(
|
||||||
|
roots: &Rc<Target::RootMatchers>,
|
||||||
|
ids: &CritMatcherIds,
|
||||||
|
) -> FixedRootMatcher<Target, CritMatchConstant<Target>> {
|
||||||
|
static_map! {
|
||||||
|
v => CritRoot::new(
|
||||||
|
roots,
|
||||||
|
ids.next(),
|
||||||
|
CritRootFixed(Self(v, PhantomData), PhantomData),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> CritFixedRootCriterionBase<Target> for CritMatchConstant<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
fn constant(&self) -> bool {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not<'a>(&self, mgr: &'a Target::Mgr) -> &'a FixedRootMatcher<Target, Self>
|
||||||
|
where
|
||||||
|
Self: CritFixedRootCriterion<Target>,
|
||||||
|
{
|
||||||
|
mgr.match_constant()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> CritFixedRootCriterion<Target> for CritMatchConstant<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
const COMPARE: bool = false;
|
||||||
|
|
||||||
|
fn matches(&self, _data: &Target) -> bool {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/criteria/crit_matchers/critm_exactly.rs
Normal file
61
src/criteria/crit_matchers/critm_exactly.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
use {
|
||||||
|
crate::criteria::crit_graph::{CritMiddleCriterion, CritTarget, CritUpstreamNode},
|
||||||
|
std::{marker::PhantomData, rc::Rc},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CritMatchExactly<Target> {
|
||||||
|
total: usize,
|
||||||
|
num: usize,
|
||||||
|
not: bool,
|
||||||
|
_phantom: PhantomData<fn(&Target)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> CritMatchExactly<Target> {
|
||||||
|
pub fn new(upstream: &[Rc<dyn CritUpstreamNode<Target>>], num: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
total: upstream.len(),
|
||||||
|
num,
|
||||||
|
not: false,
|
||||||
|
_phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target> CritMiddleCriterion<Target> for CritMatchExactly<Target>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
type Data = usize;
|
||||||
|
type Not = Self;
|
||||||
|
|
||||||
|
fn update_matched(&self, _data: &Target, node: &mut usize, matched: bool) -> bool {
|
||||||
|
if matched {
|
||||||
|
*node += 1;
|
||||||
|
} else {
|
||||||
|
*node -= 1;
|
||||||
|
}
|
||||||
|
(*node == self.num) ^ self.not
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pull(&self, upstream: &[Rc<dyn CritUpstreamNode<Target>>], node: &Target) -> bool {
|
||||||
|
let mut n = 0;
|
||||||
|
for upstream in upstream {
|
||||||
|
if upstream.pull(node) {
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(n == self.num) ^ self.not
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(&self) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Self {
|
||||||
|
total: self.total,
|
||||||
|
num: self.total - self.num,
|
||||||
|
not: !self.not,
|
||||||
|
_phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/criteria/crit_matchers/critm_string.rs
Normal file
46
src/criteria/crit_matchers/critm_string.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
use {
|
||||||
|
crate::criteria::{
|
||||||
|
CritLiteralOrRegex, RootMatcherMap,
|
||||||
|
crit_graph::{CritRootCriterion, CritTarget},
|
||||||
|
},
|
||||||
|
std::marker::PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CritMatchString<Target, A> {
|
||||||
|
string: CritLiteralOrRegex,
|
||||||
|
_phantom: PhantomData<(fn(&Target), A)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait StringAccess<Target>: Sized + 'static
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
fn with_string(data: &Target, f: impl FnOnce(&str) -> bool) -> bool;
|
||||||
|
fn nodes(
|
||||||
|
roots: &Target::RootMatchers,
|
||||||
|
) -> &RootMatcherMap<Target, CritMatchString<Target, Self>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, A> CritMatchString<Target, A> {
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub fn new(string: CritLiteralOrRegex) -> Self {
|
||||||
|
Self {
|
||||||
|
string,
|
||||||
|
_phantom: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, A> CritRootCriterion<Target> for CritMatchString<Target, A>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
A: StringAccess<Target>,
|
||||||
|
{
|
||||||
|
fn matches(&self, data: &Target) -> bool {
|
||||||
|
A::with_string(data, |s| self.string.matches(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn nodes(roots: &Target::RootMatchers) -> Option<&RootMatcherMap<Target, Self>> {
|
||||||
|
Some(A::nodes(roots))
|
||||||
|
}
|
||||||
|
}
|
||||||
136
src/criteria/crit_per_target_data.rs
Normal file
136
src/criteria/crit_per_target_data.rs
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
use {
|
||||||
|
crate::criteria::{
|
||||||
|
CritMatcherId,
|
||||||
|
crit_graph::{CritTarget, CritTargetOwner, WeakCritTargetOwner},
|
||||||
|
},
|
||||||
|
ahash::AHashMap,
|
||||||
|
std::{
|
||||||
|
cell::{RefCell, RefMut},
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
rc::Weak,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CritPerTargetData<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
id: CritMatcherId,
|
||||||
|
slf: Weak<dyn CritDestroyListener<Target>>,
|
||||||
|
data: RefCell<AHashMap<Target::Id, PerTreeNodeData<Target, T>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PerTreeNodeData<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
node: Target::Owner,
|
||||||
|
data: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) trait CritDestroyListenerBase<Target>: 'static
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
type Data;
|
||||||
|
|
||||||
|
fn data(&self) -> &CritPerTargetData<Target, Self::Data>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CritDestroyListener<Target>: 'static
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
#[expect(dead_code)]
|
||||||
|
fn destroyed(&self, target_id: Target::Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> CritPerTargetData<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
pub fn new(slf: &Weak<impl CritDestroyListener<Target>>, id: CritMatcherId) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
slf: slf.clone() as _,
|
||||||
|
data: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_or_create(&self, target: &Target, default: impl FnOnce() -> T) -> RefMut<T> {
|
||||||
|
RefMut::map(self.data.borrow_mut(), |d| {
|
||||||
|
&mut d
|
||||||
|
.entry(target.id())
|
||||||
|
.or_insert_with(|| {
|
||||||
|
target.destroyed().set(self.id, self.slf.clone());
|
||||||
|
PerTreeNodeData {
|
||||||
|
node: target.owner(),
|
||||||
|
data: default(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, target: &Target) -> Option<RefMut<T>> {
|
||||||
|
RefMut::filter_map(self.data.borrow_mut(), |d| {
|
||||||
|
d.get_mut(&target.id()).map(|d| &mut d.data)
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&self, target_id: Target::Id) {
|
||||||
|
if let Some(node) = self.data.borrow_mut().remove(&target_id) {
|
||||||
|
if let Some(node) = node.node.upgrade() {
|
||||||
|
node.data().destroyed().remove(&self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn borrow_mut(&self) -> RefMut<'_, AHashMap<Target::Id, PerTreeNodeData<Target, T>>> {
|
||||||
|
self.data.borrow_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> Drop for CritPerTargetData<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
for d in self.data.borrow().values() {
|
||||||
|
if let Some(n) = d.node.upgrade() {
|
||||||
|
n.data().destroyed().remove(&self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> CritDestroyListener<Target> for T
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
T: CritDestroyListenerBase<Target>,
|
||||||
|
{
|
||||||
|
fn destroyed(&self, target_id: Target::Id) {
|
||||||
|
let _v = self.data().data.borrow_mut().remove(&target_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> Deref for PerTreeNodeData<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
type Target = T;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Target, T> DerefMut for PerTreeNodeData<Target, T>
|
||||||
|
where
|
||||||
|
Target: CritTarget,
|
||||||
|
{
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -181,10 +181,10 @@ macro_rules! shared_ids {
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! linear_ids {
|
macro_rules! linear_ids {
|
||||||
($ids:ident, $id:ident $(,)?) => {
|
($(#[$attr1:meta])* $ids:ident, $id:ident $(,)?) => {
|
||||||
linear_ids!($ids, $id, u32);
|
linear_ids!($(#[$attr1])* $ids, $id, u32);
|
||||||
};
|
};
|
||||||
($ids:ident, $id:ident, $ty:ty $(,)?) => {
|
($(#[$attr1:meta])* $ids:ident, $id:ident, $ty:ty $(,)?) => {
|
||||||
pub struct $ids {
|
pub struct $ids {
|
||||||
next: crate::utils::numcell::NumCell<$ty>,
|
next: crate::utils::numcell::NumCell<$ty>,
|
||||||
}
|
}
|
||||||
|
|
@ -197,6 +197,7 @@ macro_rules! linear_ids {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$(#[$attr1])*
|
||||||
impl $ids {
|
impl $ids {
|
||||||
pub fn next(&self) -> $id {
|
pub fn next(&self) -> $id {
|
||||||
$id(self.next.fetch_add(1))
|
$id(self.next.fetch_add(1))
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ mod cmm;
|
||||||
mod compositor;
|
mod compositor;
|
||||||
mod config;
|
mod config;
|
||||||
mod cpu_worker;
|
mod cpu_worker;
|
||||||
|
mod criteria;
|
||||||
mod cursor;
|
mod cursor;
|
||||||
mod cursor_user;
|
mod cursor_user;
|
||||||
mod damage;
|
mod damage;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue