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::rule::{AsyncRule, Rule};
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use crate::{
|
use crate::{
|
||||||
Asynchronous, ErasedNode, Graph, Input, InvalidationSignal, NodeGraph, NodeId, Synchronicity,
|
Asynchronous, ErasedNode, Graph, Input, InvalidationSignal, NodeGraph, NodeId, Synchronicity,
|
||||||
Synchronous,
|
Synchronous, ValueInvalidationSignal,
|
||||||
};
|
};
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -57,6 +57,20 @@ impl<O: 'static, S: Synchronicity> GraphBuilder<O, S> {
|
|||||||
return self.add_node(ConstNode::new(value));
|
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>
|
pub fn add_rule<R>(&mut self, rule: R) -> Input<R::Output>
|
||||||
where
|
where
|
||||||
R: Rule,
|
R: Rule,
|
||||||
|
@ -5,7 +5,7 @@ mod synchronicity;
|
|||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use builder::{BuildGraphError, GraphBuilder};
|
use builder::{BuildGraphError, GraphBuilder};
|
||||||
use node::ErasedNode;
|
use node::{ErasedNode, NodeValue};
|
||||||
use petgraph::visit::{IntoEdgeReferences, NodeIndexable};
|
use petgraph::visit::{IntoEdgeReferences, NodeIndexable};
|
||||||
use petgraph::{stable_graph::StableDiGraph, visit::EdgeRef};
|
use petgraph::{stable_graph::StableDiGraph, visit::EdgeRef};
|
||||||
use rule::{AsyncRule, InputVisitor, Rule};
|
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
|
// TODO: i really want Input to be able to implement Deref somehow
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::rule::ConstantRule;
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rule_output_with_no_inputs() {
|
fn rule_output_with_no_inputs() {
|
||||||
let mut builder = GraphBuilder::new();
|
let mut builder = GraphBuilder::new();
|
||||||
builder.set_output(ConstantRule(1234));
|
builder.set_output(ConstantRule::new(1234));
|
||||||
assert_eq!(*builder.build().unwrap().evaluate(), 1234);
|
assert_eq!(*builder.build().unwrap().evaluate(), 1234);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_output_is_valid() {
|
fn test_output_is_valid() {
|
||||||
let mut builder = GraphBuilder::new();
|
let mut builder = GraphBuilder::new();
|
||||||
builder.set_output(ConstantRule(1));
|
builder.set_output(ConstantRule::new(1));
|
||||||
let mut graph = builder.build().unwrap();
|
let mut graph = builder.build().unwrap();
|
||||||
assert!(!graph.is_output_valid());
|
assert!(!graph.is_output_valid());
|
||||||
graph.evaluate();
|
graph.evaluate();
|
||||||
@ -298,6 +314,7 @@ mod tests {
|
|||||||
builder.set_output(Double(doubled));
|
builder.set_output(Double(doubled));
|
||||||
assert_eq!(*builder.build().unwrap().evaluate(), 168);
|
assert_eq!(*builder.build().unwrap().evaluate(), 168);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Inc(i32);
|
struct Inc(i32);
|
||||||
impl Rule for Inc {
|
impl Rule for Inc {
|
||||||
type Output = i32;
|
type Output = i32;
|
||||||
@ -406,12 +423,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn modify_graph() {
|
fn modify_graph() {
|
||||||
let mut builder = GraphBuilder::new();
|
let mut builder = GraphBuilder::new();
|
||||||
builder.set_output(ConstantRule(1));
|
builder.set_output(ConstantRule::new(1));
|
||||||
let mut graph = builder.build().unwrap();
|
let mut graph = builder.build().unwrap();
|
||||||
assert_eq!(*graph.evaluate(), 1);
|
assert_eq!(*graph.evaluate(), 1);
|
||||||
graph = graph
|
graph = graph
|
||||||
.modify(|g| {
|
.modify(|g| {
|
||||||
g.set_output(ConstantRule(2));
|
g.set_output(ConstantRule::new(2));
|
||||||
})
|
})
|
||||||
.expect("modify");
|
.expect("modify");
|
||||||
assert_eq!(*graph.evaluate(), 2);
|
assert_eq!(*graph.evaluate(), 2);
|
||||||
@ -438,7 +455,7 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn async_graph() {
|
async fn async_graph() {
|
||||||
let mut builder = GraphBuilder::new_async();
|
let mut builder = GraphBuilder::new_async();
|
||||||
builder.set_output(ConstantRule(42));
|
builder.set_output(ConstantRule::new(42));
|
||||||
let mut graph = builder.build().unwrap();
|
let mut graph = builder.build().unwrap();
|
||||||
assert_eq!(*graph.evaluate_async().await, 42);
|
assert_eq!(*graph.evaluate_async().await, 42);
|
||||||
}
|
}
|
||||||
@ -483,7 +500,7 @@ mod tests {
|
|||||||
let mut invalidate = None;
|
let mut invalidate = None;
|
||||||
let a = builder.add_invalidatable_rule(|inv| {
|
let a = builder.add_invalidatable_rule(|inv| {
|
||||||
invalidate = Some(inv);
|
invalidate = Some(inv);
|
||||||
ConstantRule(0)
|
ConstantRule::new(0)
|
||||||
});
|
});
|
||||||
struct IncAdd(Input<i32>, i32);
|
struct IncAdd(Input<i32>, i32);
|
||||||
impl Rule for IncAdd {
|
impl Rule for IncAdd {
|
||||||
@ -505,4 +522,17 @@ mod tests {
|
|||||||
assert!(!graph.is_output_valid());
|
assert!(!graph.is_output_valid());
|
||||||
assert_eq!(*graph.evaluate(), 1);
|
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> {
|
pub(crate) struct RuleNode<R, V, S> {
|
||||||
rule: R,
|
rule: R,
|
||||||
value: Rc<RefCell<Option<V>>>,
|
value: Rc<RefCell<Option<V>>>,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use crate::node::NodeValue;
|
use crate::node::NodeValue;
|
||||||
use crate::Input;
|
use crate::Input;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub trait Rule: 'static {
|
pub trait Rule: 'static {
|
||||||
type Output: NodeValue;
|
type Output: NodeValue;
|
||||||
@ -20,3 +22,21 @@ pub trait AsyncRule: 'static {
|
|||||||
pub trait InputVisitor {
|
pub trait InputVisitor {
|
||||||
fn visit<T>(&mut self, input: &Input<T>);
|
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