1
0
Fork 0
forked from wry/wry

criteria: add infrastructure

This commit is contained in:
Julian Orth 2025-05-02 16:52:04 +02:00
parent 597636fba6
commit 17e715cde4
18 changed files with 1214 additions and 3 deletions

View 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);
}
}
}

View 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
}
}

View 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);
}
}
}

View 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>;
}

View 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
}
}