diff --git a/crates/graph/src/builder.rs b/crates/graph/src/builder.rs index c5acb4f..ad1590a 100644 --- a/crates/graph/src/builder.rs +++ b/crates/graph/src/builder.rs @@ -1,9 +1,9 @@ -use crate::node::{AsyncRuleNode, ConstNode, Node, NodeValue, RuleNode}; +use crate::node::{AsyncRuleNode, ConstNode, InvalidatableConstNode, Node, NodeValue, RuleNode}; use crate::rule::{AsyncRule, Rule}; use crate::util; use crate::{ Asynchronous, ErasedNode, Graph, Input, InvalidationSignal, NodeGraph, NodeId, Synchronicity, - Synchronous, + Synchronous, ValueInvalidationSignal, }; use std::cell::{Cell, RefCell}; use std::rc::Rc; @@ -57,6 +57,20 @@ impl GraphBuilder { return self.add_node(ConstNode::new(value)); } + pub fn add_invalidatable_value( + &mut self, + value: V, + ) -> (Input, ValueInvalidationSignal) { + let input = self.add_node(InvalidatableConstNode::new(value)); + let signal = ValueInvalidationSignal { + node_idx: input.node_idx, + value: Rc::clone(&input.value), + graph: Rc::clone(&self.node_graph), + graph_is_valid: Rc::clone(&self.is_valid), + }; + (input, signal) + } + pub fn add_rule(&mut self, rule: R) -> Input where R: Rule, diff --git a/crates/graph/src/lib.rs b/crates/graph/src/lib.rs index 56249ed..6900b67 100644 --- a/crates/graph/src/lib.rs +++ b/crates/graph/src/lib.rs @@ -5,7 +5,7 @@ mod synchronicity; mod util; use builder::{BuildGraphError, GraphBuilder}; -use node::ErasedNode; +use node::{ErasedNode, NodeValue}; use petgraph::visit::{IntoEdgeReferences, NodeIndexable}; use petgraph::{stable_graph::StableDiGraph, visit::EdgeRef}; use rule::{AsyncRule, InputVisitor, Rule}; @@ -239,32 +239,48 @@ impl InvalidationSignal { } } +pub struct ValueInvalidationSignal { + node_idx: NodeId, + value: Rc>>, + graph: Rc>>, + graph_is_valid: Rc>, +} + +impl ValueInvalidationSignal { + pub fn set_value(&self, value: V) { + let mut current_value = self.value.borrow_mut(); + if !current_value + .as_ref() + .expect("invalidatable value node must be initialized with value") + .node_value_eq(&value) + { + *current_value = Some(value); + self.graph_is_valid.set(false); + let mut graph = self.graph.borrow_mut(); + let node = &mut graph[self.node_idx]; + node.invalidate(); + } + } +} + // TODO: i really want Input to be able to implement Deref somehow #[cfg(test)] mod tests { use super::*; - - struct ConstantRule(T); - impl Rule for ConstantRule { - type Output = T; - fn visit_inputs(&self, _visitor: &mut impl InputVisitor) {} - fn evaluate(&mut self) -> Self::Output { - self.0.clone() - } - } + use crate::rule::ConstantRule; #[test] fn rule_output_with_no_inputs() { let mut builder = GraphBuilder::new(); - builder.set_output(ConstantRule(1234)); + builder.set_output(ConstantRule::new(1234)); assert_eq!(*builder.build().unwrap().evaluate(), 1234); } #[test] fn test_output_is_valid() { let mut builder = GraphBuilder::new(); - builder.set_output(ConstantRule(1)); + builder.set_output(ConstantRule::new(1)); let mut graph = builder.build().unwrap(); assert!(!graph.is_output_valid()); graph.evaluate(); @@ -298,6 +314,7 @@ mod tests { builder.set_output(Double(doubled)); assert_eq!(*builder.build().unwrap().evaluate(), 168); } + struct Inc(i32); impl Rule for Inc { type Output = i32; @@ -406,12 +423,12 @@ mod tests { #[test] fn modify_graph() { let mut builder = GraphBuilder::new(); - builder.set_output(ConstantRule(1)); + builder.set_output(ConstantRule::new(1)); let mut graph = builder.build().unwrap(); assert_eq!(*graph.evaluate(), 1); graph = graph .modify(|g| { - g.set_output(ConstantRule(2)); + g.set_output(ConstantRule::new(2)); }) .expect("modify"); assert_eq!(*graph.evaluate(), 2); @@ -438,7 +455,7 @@ mod tests { #[tokio::test] async fn async_graph() { let mut builder = GraphBuilder::new_async(); - builder.set_output(ConstantRule(42)); + builder.set_output(ConstantRule::new(42)); let mut graph = builder.build().unwrap(); assert_eq!(*graph.evaluate_async().await, 42); } @@ -483,7 +500,7 @@ mod tests { let mut invalidate = None; let a = builder.add_invalidatable_rule(|inv| { invalidate = Some(inv); - ConstantRule(0) + ConstantRule::new(0) }); struct IncAdd(Input, i32); impl Rule for IncAdd { @@ -505,4 +522,17 @@ mod tests { assert!(!graph.is_output_valid()); assert_eq!(*graph.evaluate(), 1); } + + #[test] + fn invalidatable_value() { + let mut builder = GraphBuilder::new(); + let (a, invalidate) = builder.add_invalidatable_value(0); + let b = builder.add_value(1); + builder.set_output(Add(a, b)); + let mut graph = builder.build().unwrap(); + assert_eq!(*graph.evaluate(), 1); + invalidate.set_value(42); + assert!(!graph.is_output_valid()); + assert_eq!(*graph.evaluate(), 43); + } } diff --git a/crates/graph/src/node.rs b/crates/graph/src/node.rs index 15fafe5..8a2a7d5 100644 --- a/crates/graph/src/node.rs +++ b/crates/graph/src/node.rs @@ -108,6 +108,45 @@ impl Node for ConstNode { } } +pub(crate) struct InvalidatableConstNode { + value: Rc>>, + valid: bool, + synchronicity: std::marker::PhantomData, +} + +impl InvalidatableConstNode { + pub(crate) fn new(value: V) -> Self { + Self { + value: Rc::new(RefCell::new(Some(value))), + valid: true, + synchronicity: std::marker::PhantomData, + } + } +} + +impl Node for InvalidatableConstNode { + fn is_valid(&self) -> bool { + self.valid + } + + fn invalidate(&mut self) { + self.valid = false; + } + + fn visit_inputs(&self, _visitor: &mut dyn FnMut(NodeId) -> ()) {} + + fn update(&mut self) -> S::UpdateResult<'_> { + self.valid = true; + // This node is only invalidate when node_value_eq between the old/new value is false, + // so it is always the case that the update method has changed the value. + S::make_update_result(true) + } + + fn value_rc(&self) -> &Rc>> { + &self.value + } +} + pub(crate) struct RuleNode { rule: R, value: Rc>>, diff --git a/crates/graph/src/rule.rs b/crates/graph/src/rule.rs index 61d905a..2dfe0a0 100644 --- a/crates/graph/src/rule.rs +++ b/crates/graph/src/rule.rs @@ -1,5 +1,7 @@ use crate::node::NodeValue; use crate::Input; +use std::cell::RefCell; +use std::rc::Rc; pub trait Rule: 'static { type Output: NodeValue; @@ -20,3 +22,21 @@ pub trait AsyncRule: 'static { pub trait InputVisitor { fn visit(&mut self, input: &Input); } + +pub struct ConstantRule(T); + +impl ConstantRule { + pub fn new(value: T) -> Self { + Self(value) + } +} + +impl Rule for ConstantRule { + type Output = T; + + fn visit_inputs(&self, _visitor: &mut impl InputVisitor) {} + + fn evaluate(&mut self) -> Self::Output { + self.0.clone() + } +}