use crate::node::NodeValue; use crate::NodeId; pub use compute_graph_macros::InputVisitable; use std::cell::{Ref, RefCell}; use std::ops::Deref; use std::rc::Rc; /// A rule produces a value for a graph node using its [`Input`]s. /// /// A rule for addition could be implemented like so: /// /// ```rust /// # use compute_graph::rule::{Rule, Input, InputVisitable}; /// #[derive(InputVisitable)] /// struct Add(Input, Input); /// /// impl Rule for Add { /// type Output = i32; /// /// fn evaluate(&mut self) -> Self::Output { /// *self.input_0() + *self.input_1() /// } /// } /// ``` pub trait Rule: InputVisitable + 'static { /// The type of the output value of the rule. type Output: NodeValue; /// Produces the value of this rule using its inputs. /// /// Note that the receiver of this method is a mutable reference to the rule itself. Rules are permitted /// to have internal state that they modify during evaluation. /// /// The following guarantees are made about rule evaluation: /// 1. A rule will only be evaluated when one or more of its dependencies has changed. Note that "changed" /// referes to returning `false` from [`NodeValue::node_value_eq`] for the dependency. /// 2. A rule will never be evaluated before _all_ of its dependencies up-to-date. That is, it will never /// be evaluated with mix of valid and invalid dependencies. fn evaluate(&mut self) -> Self::Output; } /// A rule produces a value for a graph node asynchronously. /// /// ```rust /// # use compute_graph::rule::{AsyncRule, Input, InputVisitable}; /// # async fn do_async_work(_: i32) -> i32 { 0 } /// #[derive(InputVisitable)] /// struct AsyncMath(Input); /// /// impl AsyncRule for AsyncMath { /// type Output = i32; /// /// async fn evaluate(&mut self) -> Self::Output { /// do_async_work(*self.input_0()).await /// } /// } /// ``` pub trait AsyncRule: InputVisitable + 'static { /// The type of the output value of the rule. type Output: NodeValue; /// Asynchronously produces the value of this rule using its inputs. /// /// See [`Rule::evaluate`] for additional details; the same considerations apply. async fn evaluate(&mut self) -> Self::Output; } /// Common supertrait of [`Rule`] and [`AsyncRule`] that defines how rule inputs are visited. /// /// The implementation of this trait can generally be derived using [`derive@InputVisitable`]. pub trait InputVisitable { /// Visits all the [`Input`]s of this rule. /// /// This method is called when the graph is built/modified in order to establish edges of the graph, /// representing the dependencies. Any input that the [`InputVisitor::visit`] is called with is /// considered a dependency of the rule's node. /// /// While it is permitted for the dependencies of a rule to change after it has been added to the graph, /// doing so only permitted before the graph has been built or during the callback of /// [`Graph::modify`](`crate::Graph::modify`). Changes to the rule's dependencies outside of that will /// not be detected and will not be represented in the graph. fn visit_inputs(&self, visitor: &mut impl InputVisitor); } /// A placeholder for the output of one node to be used as an input for another. /// /// To obtain an input, add a value or rule to a [`GraphBuilder`](`crate::builder::GraphBuilder`). /// /// Note that this type implements `Clone`, so can be cloned and used as an input for multiple nodes. #[derive(Debug)] pub struct Input { pub(crate) node_idx: NodeId, pub(crate) value: Rc>>, } impl Input { /// Retrieves a reference to the current value of the node the input represents. /// /// Calling this method before the node it represents has been evaluated will panic. pub fn value(&self) -> impl Deref + '_ { Ref::map(self.value.borrow(), |opt| { opt.as_ref() .expect("node must be evaluated before reading value") }) } } // can't derive this impl because it incorectly adds the bound T: Clone impl Clone for Input { fn clone(&self) -> Self { Self { node_idx: self.node_idx, value: Rc::clone(&self.value), } } } // TODO: i really want Input to be able to implement Deref somehow /// A type that can visit arbitrary [`Input`]s. /// /// You generally do not implement this trait yourself. An implementation is provided to /// [`InputVisitable::visit_inputs`]. pub trait InputVisitor { /// Visit an input whose value is of type `T`. fn visit(&mut self, input: &Input); } /// A simple rule that provides a constant value. /// /// Note that, because [`Rule::evaluate`] returns an owned value, this rule's value type must implement `Clone`. pub struct ConstantRule(T); impl ConstantRule { /// Constructs a new constant rule with the given value. pub fn new(value: T) -> Self { Self(value) } } impl Rule for ConstantRule { type Output = T; fn evaluate(&mut self) -> Self::Output { self.0.clone() } } impl InputVisitable for ConstantRule { fn visit_inputs(&self, _visitor: &mut impl InputVisitor) {} }