Add invalidatable value node
This commit is contained in:
parent
05348a5dbc
commit
02b5226a90
@ -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<O: 'static, S: Synchronicity> GraphBuilder<O, S> {
|
||||
return self.add_node(ConstNode::new(value));
|
||||
}
|
||||
|
||||
pub fn add_invalidatable_value<V: NodeValue>(
|
||||
&mut self,
|
||||
value: V,
|
||||
) -> (Input<V>, ValueInvalidationSignal<V, S>) {
|
||||
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<R>(&mut self, rule: R) -> Input<R::Output>
|
||||
where
|
||||
R: Rule,
|
||||
|
@ -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<S: Synchronicity> InvalidationSignal<S> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ValueInvalidationSignal<V, Synch: Synchronicity> {
|
||||
node_idx: NodeId,
|
||||
value: Rc<RefCell<Option<V>>>,
|
||||
graph: Rc<RefCell<NodeGraph<Synch>>>,
|
||||
graph_is_valid: Rc<Cell<bool>>,
|
||||
}
|
||||
|
||||
impl<V: NodeValue, S: Synchronicity> ValueInvalidationSignal<V, S> {
|
||||
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>(T);
|
||||
impl<T: Clone + crate::node::NodeValue> Rule for ConstantRule<T> {
|
||||
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>, 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);
|
||||
}
|
||||
}
|
||||
|
@ -108,6 +108,45 @@ impl<V: NodeValue, S: Synchronicity> Node<V, S> for ConstNode<V, S> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct InvalidatableConstNode<V, S> {
|
||||
value: Rc<RefCell<Option<V>>>,
|
||||
valid: bool,
|
||||
synchronicity: std::marker::PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<V, S> InvalidatableConstNode<V, S> {
|
||||
pub(crate) fn new(value: V) -> Self {
|
||||
Self {
|
||||
value: Rc::new(RefCell::new(Some(value))),
|
||||
valid: true,
|
||||
synchronicity: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: NodeValue, S: Synchronicity> Node<V, S> for InvalidatableConstNode<V, S> {
|
||||
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<RefCell<Option<V>>> {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct RuleNode<R, V, S> {
|
||||
rule: R,
|
||||
value: Rc<RefCell<Option<V>>>,
|
||||
|
@ -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<T>(&mut self, input: &Input<T>);
|
||||
}
|
||||
|
||||
pub struct ConstantRule<T>(T);
|
||||
|
||||
impl<T> ConstantRule<T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + NodeValue> Rule for ConstantRule<T> {
|
||||
type Output = T;
|
||||
|
||||
fn visit_inputs(&self, _visitor: &mut impl InputVisitor) {}
|
||||
|
||||
fn evaluate(&mut self) -> Self::Output {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user