use {
std::{
rc::{
Rc,
},
cell::{
RefCell,
},
collections::{
HashMap,
HashSet,
},
},
};
pub type Id = usize;
pub const NULL_ID: Id = 0;
pub trait ValueTrait {
fn next_links(&self) -> Vec<Link>;
}
pub struct Value(pub(crate) Rc<dyn ValueTrait>);
pub trait IntoValue {
fn into_value(&self) -> Value;
}
pub(crate) trait Cleanup {
fn clean(&self);
}
pub trait LinkTrait {
fn call(&self, pc: &mut ProcessingContext);
fn next_values(&self) -> Vec<Value>;
}
pub(crate) struct Link_ {
pub(crate) id: Id,
inner: Box<dyn LinkTrait>,
}
#[derive(Clone)]
pub struct Link(pub(crate) Rc<Link_>);
impl std::hash::Hash for Link {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.id.hash(state);
}
}
impl PartialEq for Link {
fn eq(&self, other: &Self) -> bool {
return self.0.id == other.0.id;
}
}
impl Eq for Link { }
impl Link {
#[must_use]
pub fn new(pc: &mut ProcessingContext, inner: impl LinkTrait + 'static) -> Self {
let id = pc.1.take_id();
let out = Link(Rc::new(Link_ {
id: id,
inner: Box::new(inner),
}));
pc.1.step1_stacked_links.push((true, out.clone()));
return out;
}
}
pub struct _Context {
pub(crate) step1_stacked_links: Vec<(bool, Link)>,
pub(crate) cleanup: Vec<Rc<dyn Cleanup>>,
pub(crate) processing: bool,
ids: usize,
}
impl _Context {
pub(crate) fn take_id(&mut self) -> Id {
let id = self.ids;
self.ids += 1;
return id;
}
}
#[derive(Clone)]
pub struct EventGraph(Rc<RefCell<_Context>>);
pub struct ProcessingContext<'a>(pub(crate) &'a EventGraph, pub(crate) &'a mut _Context);
impl EventGraph {
pub fn new() -> EventGraph {
return EventGraph(Rc::new(RefCell::new(_Context {
step1_stacked_links: Default::default(),
cleanup: vec![],
processing: false,
ids: 1,
})));
}
pub fn event<R>(&self, f: impl FnOnce(&mut ProcessingContext) -> R) -> Option<R> {
let Ok(mut s) = self.0.try_borrow_mut() else {
return None;
};
if s.processing {
return None;
}
let out = f(&mut ProcessingContext(self, &mut *s));
s.processing = true;
let mut involved_links = HashSet::new();
let mut processed_links = HashSet::new();
let mut step12_leaves = vec![];
let mut step2_upstream_dep_tree: HashMap<Id, HashSet<Link>> = HashMap::new();
let mut step2_stacked_links = vec![];
let mut step2_seen_up = HashSet::new();
while !s.step1_stacked_links.is_empty() {
struct Step1PathEntry {
link: Link,
downstream: usize,
}
let mut path_stack: Vec<Step1PathEntry> = vec![];
s.step1_stacked_links.reverse();
'stack_next: while let Some((first, link)) = s.step1_stacked_links.pop() {
if first {
if !involved_links.insert(link.0.id) {
continue;
}
let mut outputs = vec![];
for next_val in link.0.inner.next_values() {
for next_link in next_val.0.next_links() {
for path_entry in &path_stack {
if path_entry.link.0.id == next_link.0.id {
continue 'stack_next;
}
}
outputs.push(next_link.clone());
}
}
if let Some(parent) = path_stack.last_mut() {
parent.downstream += 1;
}
s.step1_stacked_links.push((false, link.clone()));
path_stack.push(Step1PathEntry {
link: link.clone(),
downstream: 0,
});
for next_link in outputs {
step2_upstream_dep_tree.entry(next_link.0.id).or_default().insert(link.clone());
s.step1_stacked_links.push((true, next_link));
}
} else {
let totals = path_stack.pop().unwrap();
if totals.downstream == 0 {
step12_leaves.push(link);
}
}
}
let queue_leaves: Vec<(bool, Link)> = step12_leaves.drain(0..).map(|l| (true, l)).collect();
step2_stacked_links.extend(queue_leaves);
while let Some((first, link)) = step2_stacked_links.pop() {
if first {
if !processed_links.insert(link.0.id) {
continue;
}
step2_stacked_links.push((false, link.clone()));
for prev_link in step2_upstream_dep_tree.remove(&link.0.id).unwrap_or_default() {
if !step2_seen_up.insert(prev_link.0.id) {
continue;
}
if !involved_links.contains(&prev_link.0.id) {
continue;
}
step2_stacked_links.push((true, prev_link));
}
step2_seen_up.clear();
} else {
(link.0.inner).call(&mut ProcessingContext(self, &mut s));
}
}
}
debug_assert!(step2_seen_up.is_empty());
for p in s.cleanup.drain(0..) {
p.clean();
}
s.processing = false;
return Some(out);
}
}
impl<'a> ProcessingContext<'a> {
pub fn eg(&self) -> EventGraph {
return self.0.clone();
}
}