Improve InputVisitable derive macro
This commit is contained in:
parent
55b91944b2
commit
1279a1755d
@ -1,8 +1,9 @@
|
|||||||
|
use crate::input::{DynamicInput, Input};
|
||||||
use crate::node::{
|
use crate::node::{
|
||||||
AsyncConstNode, AsyncDynamicRuleNode, AsyncRuleNode, ConstNode, DynamicRuleNode, ErasedNode,
|
AsyncConstNode, AsyncDynamicRuleNode, AsyncRuleNode, ConstNode, DynamicRuleNode, ErasedNode,
|
||||||
InvalidatableConstNode, Node, NodeValue, RuleNode,
|
InvalidatableConstNode, Node, NodeValue, RuleNode,
|
||||||
};
|
};
|
||||||
use crate::rule::{AsyncDynamicRule, AsyncRule, DynamicInput, DynamicRule, Input, Rule};
|
use crate::rule::{AsyncDynamicRule, AsyncRule, DynamicRule, Rule};
|
||||||
use crate::synchronicity::{Asynchronous, Synchronicity, Synchronous};
|
use crate::synchronicity::{Asynchronous, Synchronicity, Synchronous};
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use crate::{Graph, InvalidationSignal, NodeGraph, NodeId, ValueInvalidationSignal};
|
use crate::{Graph, InvalidationSignal, NodeGraph, NodeId, ValueInvalidationSignal};
|
||||||
|
137
crates/compute_graph/src/input.rs
Normal file
137
crates/compute_graph/src/input.rs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
use std::{
|
||||||
|
cell::{Cell, Ref, RefCell},
|
||||||
|
ops::Deref,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{node::DynamicRuleOutput, rule::DynamicNodeFactory, NodeId};
|
||||||
|
|
||||||
|
pub use compute_graph_macros::InputVisitable;
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
pub struct Input<T> {
|
||||||
|
pub(crate) node_idx: Rc<Cell<Option<NodeId>>>,
|
||||||
|
pub(crate) value: Rc<RefCell<Option<T>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Input<T> {
|
||||||
|
/// 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<Target = T> + '_ {
|
||||||
|
Ref::map(self.value.borrow(), |opt| {
|
||||||
|
opt.as_ref()
|
||||||
|
.expect("node must be evaluated before reading value")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the ID of the node that this input refers to.
|
||||||
|
pub fn node_id(&self) -> NodeId {
|
||||||
|
self.node_idx.get().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// can't derive this impl because it incorectly adds the bound T: Clone
|
||||||
|
impl<T> Clone for Input<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
node_idx: Rc::clone(&self.node_idx),
|
||||||
|
value: Rc::clone(&self.value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> std::fmt::Debug for Input<T> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Input<{}>({:?})",
|
||||||
|
std::any::type_name::<T>(),
|
||||||
|
self.node_idx
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A placeholder for the output of a dynamic rule node, to be used as an input for another.
|
||||||
|
///
|
||||||
|
/// See [`GraphBuilder::add_dynamic_rule`](`crate::builder::GraphBuilder::add_dynamic_rule`).
|
||||||
|
///
|
||||||
|
/// A dependency on a dynamic input represents both a dependency on the dynamic node itself,
|
||||||
|
/// as well as dependencies on each of the nodes that are the output of the dynamic node.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DynamicInput<T> {
|
||||||
|
pub(crate) input: Input<DynamicRuleOutput<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> DynamicInput<T> {
|
||||||
|
/// Retrieves a reference to the current value of the dynamic node (i.e., the set of inputs
|
||||||
|
/// representing the nodes that are the outputs of the dynamic node).
|
||||||
|
pub fn value(&self) -> impl Deref<Target = DynamicRuleOutput<T>> + '_ {
|
||||||
|
self.input.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<T>(&mut self, input: &Input<T>);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Common supertrait of [`Rule`](`crate::rule::Rule`) and [`AsyncRule`](`crate::rule::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);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> InputVisitable for Input<T> {
|
||||||
|
fn visit_inputs(&self, visitor: &mut impl InputVisitor) {
|
||||||
|
visitor.visit(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> InputVisitable for DynamicInput<T> {
|
||||||
|
fn visit_inputs(&self, visitor: &mut impl InputVisitor) {
|
||||||
|
// Visit the dynamic node itself
|
||||||
|
visitor.visit(&self.input);
|
||||||
|
|
||||||
|
// And visit all the nodes it produces
|
||||||
|
let maybe_dynamic_output = self.input.value.borrow();
|
||||||
|
if let Some(dynamic_output) = maybe_dynamic_output.as_ref() {
|
||||||
|
// This might be slightly overzealous: it is possible for a node to only depend on the
|
||||||
|
// dynamic node itself, and not directly depend on any of the nodes the dynamic node produces.
|
||||||
|
for input in dynamic_output.inputs.iter() {
|
||||||
|
visitor.visit(input);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Haven't evaluated the dynamic node for the first time yet.
|
||||||
|
// Upon doing so, if the nodes it produces change, we'll modify the graph
|
||||||
|
// and end up back here in the other branch.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no-op impl, here for convenience so you don't have to have to have as many `#[skip_input]`s
|
||||||
|
impl<ID, T> InputVisitable for DynamicNodeFactory<ID, T> {
|
||||||
|
fn visit_inputs(&self, _visitor: &mut impl InputVisitor) {}
|
||||||
|
}
|
@ -44,16 +44,17 @@
|
|||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
|
pub mod input;
|
||||||
pub mod node;
|
pub mod node;
|
||||||
pub mod rule;
|
pub mod rule;
|
||||||
pub mod synchronicity;
|
pub mod synchronicity;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
use builder::{BuildGraphError, GraphBuilder};
|
use builder::{BuildGraphError, GraphBuilder};
|
||||||
|
use input::{Input, InputVisitor};
|
||||||
use node::{ErasedNode, NodeUpdateContext, NodeValue};
|
use node::{ErasedNode, NodeUpdateContext, NodeValue};
|
||||||
use petgraph::visit::{IntoEdgeReferences, IntoNodeReferences, NodeIndexable, NodeRef};
|
use petgraph::visit::{IntoEdgeReferences, IntoNodeReferences, NodeIndexable, NodeRef};
|
||||||
use petgraph::{stable_graph::StableDiGraph, visit::EdgeRef};
|
use petgraph::{stable_graph::StableDiGraph, visit::EdgeRef};
|
||||||
use rule::{Input, InputVisitor};
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
@ -524,9 +525,8 @@ mod tests {
|
|||||||
use rule::DynamicNodeFactory;
|
use rule::DynamicNodeFactory;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::rule::{
|
use crate::input::{DynamicInput, InputVisitable};
|
||||||
AsyncDynamicRule, AsyncRule, ConstantRule, DynamicInput, DynamicRule, InputVisitable, Rule,
|
use crate::rule::{AsyncDynamicRule, AsyncRule, ConstantRule, DynamicRule, Rule};
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn rule_output_with_no_inputs() {
|
fn rule_output_with_no_inputs() {
|
||||||
@ -831,7 +831,7 @@ mod tests {
|
|||||||
struct DynamicSum(DynamicInput<i32>);
|
struct DynamicSum(DynamicInput<i32>);
|
||||||
impl InputVisitable for DynamicSum {
|
impl InputVisitable for DynamicSum {
|
||||||
fn visit_inputs(&self, visitor: &mut impl InputVisitor) {
|
fn visit_inputs(&self, visitor: &mut impl InputVisitor) {
|
||||||
visitor.visit_dynamic(&self.0);
|
self.0.visit_inputs(visitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Rule for DynamicSum {
|
impl Rule for DynamicSum {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
|
use crate::input::InputVisitable;
|
||||||
use crate::rule::{
|
use crate::rule::{
|
||||||
AsyncDynamicRule, AsyncDynamicRuleContext, AsyncRule, DynamicInput, DynamicRule,
|
AsyncDynamicRule, AsyncDynamicRuleContext, AsyncRule, DynamicRule, DynamicRuleContext, Rule,
|
||||||
DynamicRuleContext, InputVisitable, Rule,
|
|
||||||
};
|
};
|
||||||
use crate::synchronicity::{Asynchronous, Synchronicity};
|
use crate::synchronicity::{Asynchronous, Synchronicity};
|
||||||
use crate::{Graph, Input, InputVisitor, InvalidationSignal, NodeGraph, NodeId, Synchronous};
|
use crate::{Graph, Input, InputVisitor, InvalidationSignal, NodeGraph, NodeId, Synchronous};
|
||||||
@ -257,24 +257,6 @@ fn visit_inputs<V: InputVisitable>(visitable: &V, visitor: &mut dyn FnMut(NodeId
|
|||||||
fn visit<T>(&mut self, input: &Input<T>) {
|
fn visit<T>(&mut self, input: &Input<T>) {
|
||||||
self.0(input.node_idx.get().unwrap());
|
self.0(input.node_idx.get().unwrap());
|
||||||
}
|
}
|
||||||
fn visit_dynamic<T>(&mut self, input: &DynamicInput<T>) {
|
|
||||||
// Visit the dynamic node itself
|
|
||||||
self.visit(&input.input);
|
|
||||||
|
|
||||||
// And visit all the nodes it produces
|
|
||||||
let maybe_dynamic_output = input.input.value.borrow();
|
|
||||||
if let Some(dynamic_output) = maybe_dynamic_output.as_ref() {
|
|
||||||
// This might be slightly overzealous: it is possible for a node to only depend on the
|
|
||||||
// dynamic node itself, and not directly depend on any of the nodes the dynamic node produces.
|
|
||||||
for input in dynamic_output.inputs.iter() {
|
|
||||||
self.visit(input);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Haven't evaluated the dynamic node for the first time yet.
|
|
||||||
// Upon doing so, if the nodes it produces change, we'll modify the graph
|
|
||||||
// and end up back here in the other branch.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
visitable.visit_inputs(&mut InputIndexVisitor(visitor));
|
visitable.visit_inputs(&mut InputIndexVisitor(visitor));
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
|
use crate::input::{Input, InputVisitable, InputVisitor};
|
||||||
use crate::node::{DynamicRuleOutput, NodeValue};
|
use crate::node::{DynamicRuleOutput, NodeValue};
|
||||||
use crate::{InvalidationSignal, NodeId};
|
use crate::{InvalidationSignal, NodeId};
|
||||||
pub use compute_graph_macros::InputVisitable;
|
|
||||||
use std::cell::{Cell, Ref, RefCell};
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::ops::Deref;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
/// A rule produces a value for a graph node using its [`Input`]s.
|
/// A rule produces a value for a graph node using its [`Input`]s.
|
||||||
///
|
///
|
||||||
@ -130,7 +127,7 @@ pub trait DynamicRuleContext {
|
|||||||
|
|
||||||
/// Helper type for working with [`DynamicRule`]s.
|
/// Helper type for working with [`DynamicRule`]s.
|
||||||
///
|
///
|
||||||
/// When implementing [`DynamicRule::evaluate`], call the [`DynamicNodeFactory::add_rule`] method for
|
/// When implementing [`DynamicRule::evaluate`], call the [`DynamicNodeFactory::add_node`] method for
|
||||||
/// each of the nodes that should be in the output. Then, call [`DynamicNodeFactory::all_nodes`] to produce
|
/// each of the nodes that should be in the output. Then, call [`DynamicNodeFactory::all_nodes`] to produce
|
||||||
/// the output (i.e., `Vec` of [`Input`]s) of the dynamic node.
|
/// the output (i.e., `Vec` of [`Input`]s) of the dynamic node.
|
||||||
///
|
///
|
||||||
@ -188,7 +185,7 @@ impl<ID: Hash + Eq + Clone, ChildOutput> DynamicNodeFactory<ID, ChildOutput> {
|
|||||||
input
|
input
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes any nodes that were previously output but which have not had [`DynamicNodeFactory::add_rule`]
|
/// Removes any nodes that were previously output but which have not had [`DynamicNodeFactory::add_node`]
|
||||||
/// called during this evaluation.
|
/// called during this evaluation.
|
||||||
///
|
///
|
||||||
/// Either this method or [`DynamicNodeFactory::all_nodes`] should only be called once per evaluation.
|
/// Either this method or [`DynamicNodeFactory::all_nodes`] should only be called once per evaluation.
|
||||||
@ -252,104 +249,6 @@ pub trait AsyncDynamicRuleContext: DynamicRuleContext {
|
|||||||
R: AsyncRule;
|
R: AsyncRule;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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.
|
|
||||||
pub struct Input<T> {
|
|
||||||
pub(crate) node_idx: Rc<Cell<Option<NodeId>>>,
|
|
||||||
pub(crate) value: Rc<RefCell<Option<T>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Input<T> {
|
|
||||||
/// 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<Target = T> + '_ {
|
|
||||||
Ref::map(self.value.borrow(), |opt| {
|
|
||||||
opt.as_ref()
|
|
||||||
.expect("node must be evaluated before reading value")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the ID of the node that this input refers to.
|
|
||||||
pub fn node_id(&self) -> NodeId {
|
|
||||||
self.node_idx.get().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// can't derive this impl because it incorectly adds the bound T: Clone
|
|
||||||
impl<T> Clone for Input<T> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
node_idx: Rc::clone(&self.node_idx),
|
|
||||||
value: Rc::clone(&self.value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> std::fmt::Debug for Input<T> {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Input<{}>({:?})",
|
|
||||||
std::any::type_name::<T>(),
|
|
||||||
self.node_idx
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A placeholder for the output of a dynamic rule node, to be used as an input for another.
|
|
||||||
///
|
|
||||||
/// See [`GraphBuilder::add_dynamic_rule`](`crate::builder::GraphBuilder::add_dynamic_rule`).
|
|
||||||
///
|
|
||||||
/// A dependency on a dynamic input represents both a dependency on the dynamic node itself,
|
|
||||||
/// as well as dependencies on each of the nodes that are the output of the dynamic node.
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct DynamicInput<T> {
|
|
||||||
pub(crate) input: Input<DynamicRuleOutput<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> DynamicInput<T> {
|
|
||||||
/// Retrieves a reference to the current value of the dynamic node (i.e., the set of inputs
|
|
||||||
/// representing the nodes that are the outputs of the dynamic node).
|
|
||||||
pub fn value(&self) -> impl Deref<Target = DynamicRuleOutput<T>> + '_ {
|
|
||||||
self.input.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<T>(&mut self, input: &Input<T>);
|
|
||||||
|
|
||||||
/// Visit a dynamic input whose child value is of type `T`.
|
|
||||||
fn visit_dynamic<T>(&mut self, input: &DynamicInput<T>);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A simple rule that provides a constant value.
|
/// 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`.
|
/// Note that, because [`Rule::evaluate`] returns an owned value, this rule's value type must implement `Clone`.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::Literal;
|
use proc_macro2::{Literal, Span};
|
||||||
use quote::{format_ident, quote, ToTokens};
|
use quote::{format_ident, quote, ToTokens};
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, Attribute, Data, DataStruct, DeriveInput, Field, Fields, GenericArgument,
|
parse_macro_input, Attribute, Data, DataEnum, DataStruct, DeriveInput, Field, Fields,
|
||||||
GenericParam, PathArguments, Type,
|
GenericArgument, GenericParam, Index, Member, PathArguments, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
@ -11,83 +11,41 @@ extern crate proc_macro;
|
|||||||
/// Derive an implementation of the `InputVisitable` trait and helper methods.
|
/// Derive an implementation of the `InputVisitable` trait and helper methods.
|
||||||
///
|
///
|
||||||
/// This macro generates an implementation of the `InputVisitable` trait and the `visit_inputs` method that
|
/// This macro generates an implementation of the `InputVisitable` trait and the `visit_inputs` method that
|
||||||
/// calls `visit` on each field of the struct that is of type `Input<T>` or `DynamicInput<T>` for any `T`.
|
/// calls `visit_inputs` on each field of the struct that is does not have the `#[skip_visit]` attribute.
|
||||||
///
|
///
|
||||||
/// The macro also generates helper methods for accessing the value of each input less verbosely.
|
/// For structs only, the macro also generates helper methods for accessing the value of each input less verbosely.
|
||||||
/// For unnamed struct fields, the methods generated have the form `input_0`, `input_1`, etc.
|
/// For unnamed struct fields, the methods generated have the form `input_0`, `input_1`, etc.
|
||||||
/// For named fields, the generated method name matches the field name. In both cases, the method
|
/// For named fields, the generated method name matches the field name. In both cases, the method
|
||||||
/// returns a reference to the input value. As with the `Input::value` method, calling the helper methods
|
/// returns a reference to the input value. As with the `Input::value` method, calling the helper methods
|
||||||
/// before the referenced node has been evaluated is forbidden.
|
/// before the referenced node has been evaluated is forbidden.
|
||||||
#[proc_macro_derive(InputVisitable, attributes(ignore_input))]
|
#[proc_macro_derive(InputVisitable, attributes(skip_visit))]
|
||||||
pub fn derive_rule(input: TokenStream) -> TokenStream {
|
pub fn derive_input_visitable(input: TokenStream) -> TokenStream {
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
if let Data::Struct(ref data) = input.data {
|
match input.data {
|
||||||
derive_rule_struct(&input, data)
|
Data::Struct(ref data) => derive_input_visitable_struct(&input, data),
|
||||||
} else {
|
Data::Enum(ref data) => derive_input_visitable_enum(&input, data),
|
||||||
TokenStream::from(
|
Data::Union(_) => TokenStream::from(
|
||||||
syn::Error::new(
|
syn::Error::new(input.ident.span(), "Unions cannot derive `InputVisitable`")
|
||||||
input.ident.span(),
|
.to_compile_error(),
|
||||||
"Only structs can derive `InputVisitable`",
|
),
|
||||||
)
|
|
||||||
.to_compile_error(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn derive_rule_struct(input: &DeriveInput, data: &DataStruct) -> TokenStream {
|
fn derive_input_visitable_struct(input: &DeriveInput, data: &DataStruct) -> TokenStream {
|
||||||
let name = &input.ident;
|
let visit_inputs = derive_visit_inputs(
|
||||||
let lt = &input.generics.lt_token;
|
&data.fields,
|
||||||
let params = &input.generics.params;
|
|field| {
|
||||||
let gt = &input.generics.gt_token;
|
let ident = field.ident.clone().unwrap();
|
||||||
let generics = quote!(#lt #params #gt);
|
quote!(&self.#ident)
|
||||||
let where_clause = &input.generics.where_clause;
|
},
|
||||||
let params_only_names = params.iter().map(|p| match p {
|
|i| {
|
||||||
GenericParam::Lifetime(_) => {
|
let member = Member::Unnamed(Index {
|
||||||
panic!("Lifetime generics aren't supported when deriving `InputVisitable`")
|
index: i as u32,
|
||||||
}
|
span: Span::call_site(),
|
||||||
GenericParam::Type(ty) => &ty.ident,
|
});
|
||||||
GenericParam::Const(_) => {
|
quote!(&self.#member)
|
||||||
panic!("Const generics aren't supported when deriving `InputVisitable`")
|
},
|
||||||
}
|
);
|
||||||
});
|
|
||||||
let generics_only_names = quote!(#lt #(#params_only_names),* #gt);
|
|
||||||
|
|
||||||
let visit_inputs = match data.fields {
|
|
||||||
Fields::Named(ref named) => named
|
|
||||||
.named
|
|
||||||
.iter()
|
|
||||||
.flat_map(|field| {
|
|
||||||
if let Some((_ty, is_dynamic)) = input_value_type(field) {
|
|
||||||
let ident = field.ident.as_ref().unwrap();
|
|
||||||
if is_dynamic {
|
|
||||||
Some(quote!(visitor.visit_dynamic(&self.#ident);))
|
|
||||||
} else {
|
|
||||||
Some(quote!(visitor.visit(&self.#ident);))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
Fields::Unnamed(ref unnamed) => unnamed
|
|
||||||
.unnamed
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.flat_map(|(i, field)| {
|
|
||||||
if let Some((_ty, is_dynamic)) = input_value_type(field) {
|
|
||||||
let idx_lit = Literal::usize_unsuffixed(i);
|
|
||||||
if is_dynamic {
|
|
||||||
Some(quote!(visitor.visit_dynamic(&self.#idx_lit);))
|
|
||||||
} else {
|
|
||||||
Some(quote!(visitor.visit(&self.#idx_lit);))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
Fields::Unit => vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
let input_value_methods = match data.fields {
|
let input_value_methods = match data.fields {
|
||||||
Fields::Named(ref named) => named
|
Fields::Named(ref named) => named
|
||||||
@ -139,23 +97,138 @@ fn derive_rule_struct(input: &DeriveInput, data: &DataStruct) -> TokenStream {
|
|||||||
Fields::Unit => vec![],
|
Fields::Unit => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
TokenStream::from(quote!(
|
let input_visitable_impl = make_derive_impl(
|
||||||
|
input,
|
||||||
impl #generics ::compute_graph::rule::InputVisitable for #name #generics_only_names #where_clause {
|
quote!(::compute_graph::input::InputVisitable for),
|
||||||
fn visit_inputs(&self, visitor: &mut impl ::compute_graph::rule::InputVisitor) {
|
quote!(
|
||||||
#(#visit_inputs)*
|
fn visit_inputs(&self, visitor: &mut impl ::compute_graph::input::InputVisitor) {
|
||||||
|
#visit_inputs
|
||||||
}
|
}
|
||||||
}
|
),
|
||||||
|
);
|
||||||
|
|
||||||
impl #generics #name #generics_only_names #where_clause {
|
let input_value_methods_impl = make_derive_impl(
|
||||||
|
input,
|
||||||
|
quote!(),
|
||||||
|
quote!(
|
||||||
#(#input_value_methods)*
|
#(#input_value_methods)*
|
||||||
}
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
TokenStream::from(quote!(
|
||||||
|
#input_visitable_impl
|
||||||
|
#input_value_methods_impl
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn derive_input_visitable_enum(input: &DeriveInput, data: &DataEnum) -> TokenStream {
|
||||||
|
let variants = data.variants.iter().map(|variant| {
|
||||||
|
let ident = &variant.ident;
|
||||||
|
let fields = match &variant.fields {
|
||||||
|
Fields::Named(fields) => {
|
||||||
|
let field_refs = fields.named.iter().filter(should_visit_field).map(|field| {
|
||||||
|
let ident = field.ident.as_ref().expect("named field must have ident");
|
||||||
|
quote!(ref #ident)
|
||||||
|
});
|
||||||
|
quote!({ #(#field_refs,)* .. })
|
||||||
|
}
|
||||||
|
Fields::Unnamed(fields) => {
|
||||||
|
let field_refs = fields.unnamed.iter().enumerate().map(|(i, field)| {
|
||||||
|
if should_visit_field(&field) {
|
||||||
|
let ident = format_ident!("field_{i}");
|
||||||
|
quote!(ref #ident)
|
||||||
|
} else {
|
||||||
|
quote!(_)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
quote!((#(#field_refs,)*))
|
||||||
|
}
|
||||||
|
Fields::Unit => quote!(),
|
||||||
|
};
|
||||||
|
let variant_visit_inputs = derive_visit_inputs(
|
||||||
|
&variant.fields,
|
||||||
|
|field| field.ident.clone().unwrap(),
|
||||||
|
|i| format_ident!("field_{i}"),
|
||||||
|
);
|
||||||
|
quote!(Self::#ident #fields => {
|
||||||
|
#variant_visit_inputs
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
make_derive_impl(
|
||||||
|
input,
|
||||||
|
quote!(::compute_graph::input::InputVisitable for),
|
||||||
|
quote!(
|
||||||
|
fn visit_inputs(&self, visitor: &mut impl ::compute_graph::input::InputVisitor) {
|
||||||
|
match self {
|
||||||
|
#(#variants)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn derive_visit_inputs<NamedAccessTokens: ToTokens, UnnamedAccessTokens: ToTokens>(
|
||||||
|
fields: &Fields,
|
||||||
|
named_field_access: impl Fn(&Field) -> NamedAccessTokens,
|
||||||
|
unnamed_field_access: impl Fn(usize) -> UnnamedAccessTokens,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
match fields {
|
||||||
|
Fields::Named(ref named) => {
|
||||||
|
let fields = named.named.iter().filter(should_visit_field).map(|field| {
|
||||||
|
let field_access = named_field_access(field);
|
||||||
|
quote!(::compute_graph::input::InputVisitable::visit_inputs(#field_access, visitor);)
|
||||||
|
});
|
||||||
|
quote!(#(#fields)*)
|
||||||
|
}
|
||||||
|
Fields::Unnamed(ref unnamed) => {
|
||||||
|
let fields = unnamed
|
||||||
|
.unnamed
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_i, field)| should_visit_field(field))
|
||||||
|
.map(|(i, _field)| {
|
||||||
|
let field_access = unnamed_field_access(i);
|
||||||
|
quote!(::compute_graph::input::InputVisitable::visit_inputs(#field_access, visitor);)
|
||||||
|
});
|
||||||
|
quote!(#(#fields)*)
|
||||||
|
}
|
||||||
|
Fields::Unit => proc_macro2::TokenStream::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_derive_impl(
|
||||||
|
input: &DeriveInput,
|
||||||
|
trait_for: proc_macro2::TokenStream,
|
||||||
|
body: proc_macro2::TokenStream,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let name = &input.ident;
|
||||||
|
let lt = &input.generics.lt_token;
|
||||||
|
let params = &input.generics.params;
|
||||||
|
let gt = &input.generics.gt_token;
|
||||||
|
let generics = quote!(#lt #params #gt);
|
||||||
|
let where_clause = &input.generics.where_clause;
|
||||||
|
let params_only_names = params.iter().map(|p| match p {
|
||||||
|
GenericParam::Lifetime(_) => {
|
||||||
|
panic!("Lifetime generics aren't supported when deriving `InputVisitable`")
|
||||||
|
}
|
||||||
|
GenericParam::Type(ty) => &ty.ident,
|
||||||
|
GenericParam::Const(_) => {
|
||||||
|
panic!("Const generics aren't supported when deriving `InputVisitable`")
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let generics_only_names = quote!(#lt #(#params_only_names),* #gt);
|
||||||
|
|
||||||
|
quote!(
|
||||||
|
impl #generics #trait_for #name #generics_only_names #where_clause {
|
||||||
|
#body
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn input_value_type(field: &Field) -> Option<(&Type, bool)> {
|
fn input_value_type(field: &Field) -> Option<(&Type, bool)> {
|
||||||
if field.attrs.iter().any(|attr| is_ignore_attr(attr)) {
|
if field.attrs.iter().any(is_skip_attr) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if let Type::Path(ref path) = field.ty {
|
if let Type::Path(ref path) = field.ty {
|
||||||
@ -183,9 +256,13 @@ fn input_value_type(field: &Field) -> Option<(&Type, bool)> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_ignore_attr(attr: &Attribute) -> bool {
|
fn should_visit_field(field: &&Field) -> bool {
|
||||||
|
!field.attrs.iter().any(is_skip_attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_skip_attr(attr: &Attribute) -> bool {
|
||||||
match attr.meta.require_path_only() {
|
match attr.meta.require_path_only() {
|
||||||
Ok(path) => path.is_ident("ignore_input"),
|
Ok(path) => path.is_ident("skip_visit"),
|
||||||
Err(_) => false,
|
Err(_) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
use compute_graph::input::{DynamicInput, Input, InputVisitable};
|
||||||
use compute_graph::node::NodeValue;
|
use compute_graph::node::NodeValue;
|
||||||
use compute_graph::rule::{DynamicInput, Input, InputVisitable, Rule};
|
use compute_graph::rule::Rule;
|
||||||
|
|
||||||
#[derive(InputVisitable)]
|
#[derive(InputVisitable)]
|
||||||
struct Add(Input<i32>, Input<i32>, i32);
|
struct Add(Input<i32>, Input<i32>, #[skip_visit] i32);
|
||||||
|
|
||||||
impl Rule for Add {
|
impl Rule for Add {
|
||||||
type Output = i32;
|
type Output = i32;
|
||||||
@ -15,6 +16,7 @@ impl Rule for Add {
|
|||||||
struct Add2 {
|
struct Add2 {
|
||||||
a: Input<i32>,
|
a: Input<i32>,
|
||||||
b: Input<i32>,
|
b: Input<i32>,
|
||||||
|
#[skip_visit]
|
||||||
c: i32,
|
c: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,11 +49,25 @@ impl Rule for Sum {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(InputVisitable)]
|
||||||
|
enum E {
|
||||||
|
A(#[skip_visit] i32, Input<i32>),
|
||||||
|
B {
|
||||||
|
#[skip_visit]
|
||||||
|
x: i32,
|
||||||
|
y: Input<i32>,
|
||||||
|
},
|
||||||
|
C {
|
||||||
|
x: Input<i32>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use compute_graph::{
|
use compute_graph::{
|
||||||
builder::GraphBuilder,
|
builder::GraphBuilder,
|
||||||
rule::{ConstantRule, DynamicRule, InputVisitor},
|
input::InputVisitor,
|
||||||
|
rule::{ConstantRule, DynamicRule},
|
||||||
synchronicity::Synchronous,
|
synchronicity::Synchronous,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -102,9 +118,10 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ignore() {
|
fn test_ignore() {
|
||||||
|
#[allow(unused)]
|
||||||
#[derive(InputVisitable)]
|
#[derive(InputVisitable)]
|
||||||
struct Ignore {
|
struct Ignore {
|
||||||
#[ignore_input]
|
#[skip_visit]
|
||||||
input: Input<i32>,
|
input: Input<i32>,
|
||||||
}
|
}
|
||||||
let mut builder = GraphBuilder::<i32, Synchronous>::new();
|
let mut builder = GraphBuilder::<i32, Synchronous>::new();
|
||||||
@ -113,13 +130,42 @@ mod tests {
|
|||||||
fn visit<T>(&mut self, _input: &Input<T>) {
|
fn visit<T>(&mut self, _input: &Input<T>) {
|
||||||
assert!(false);
|
assert!(false);
|
||||||
}
|
}
|
||||||
fn visit_dynamic<T>(&mut self, _input: &DynamicInput<T>) {
|
|
||||||
unreachable!();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ignore {
|
Ignore {
|
||||||
input: builder.add_value(0),
|
input: builder.add_value(0),
|
||||||
}
|
}
|
||||||
.visit_inputs(&mut Visitor);
|
.visit_inputs(&mut Visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_enum() {
|
||||||
|
let mut builder = GraphBuilder::<i32, Synchronous>::new();
|
||||||
|
let input = builder.add_value(1);
|
||||||
|
struct Visitor(bool, Input<i32>);
|
||||||
|
impl InputVisitor for Visitor {
|
||||||
|
fn visit<T>(&mut self, input: &Input<T>) {
|
||||||
|
assert_eq!(input.node_id(), self.1.node_id());
|
||||||
|
assert!(!self.0);
|
||||||
|
self.0 = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = E::A(0, input.clone());
|
||||||
|
let mut visitor = Visitor(false, input.clone());
|
||||||
|
InputVisitable::visit_inputs(&a, &mut visitor);
|
||||||
|
assert!(visitor.0);
|
||||||
|
|
||||||
|
let b = E::B {
|
||||||
|
x: 0,
|
||||||
|
y: input.clone(),
|
||||||
|
};
|
||||||
|
let mut visitor = Visitor(false, input.clone());
|
||||||
|
InputVisitable::visit_inputs(&b, &mut visitor);
|
||||||
|
assert!(visitor.0);
|
||||||
|
|
||||||
|
let c = E::C { x: input.clone() };
|
||||||
|
let mut visitor = Visitor(false, input);
|
||||||
|
InputVisitable::visit_inputs(&c, &mut visitor);
|
||||||
|
assert!(visitor.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,8 @@ use std::collections::HashMap;
|
|||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
use compute_graph::{
|
use compute_graph::{
|
||||||
builder::GraphBuilder,
|
builder::GraphBuilder,
|
||||||
rule::{Input, InputVisitable, Rule},
|
input::{Input, InputVisitable},
|
||||||
|
rule::Rule,
|
||||||
synchronicity::Asynchronous,
|
synchronicity::Asynchronous,
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
@ -10,7 +10,8 @@ use base64::{Engine, prelude::BASE64_STANDARD};
|
|||||||
use compute_graph::{
|
use compute_graph::{
|
||||||
InvalidationSignal,
|
InvalidationSignal,
|
||||||
builder::GraphBuilder,
|
builder::GraphBuilder,
|
||||||
rule::{Input, InputVisitable, Rule},
|
input::{Input, InputVisitable, InputVisitor},
|
||||||
|
rule::Rule,
|
||||||
synchronicity::Asynchronous,
|
synchronicity::Asynchronous,
|
||||||
};
|
};
|
||||||
use grass::{Fs, Options, OutputStyle};
|
use grass::{Fs, Options, OutputStyle};
|
||||||
@ -88,7 +89,7 @@ struct CompileScss {
|
|||||||
fonts: HashMap<&'static str, Input<String>>,
|
fonts: HashMap<&'static str, Input<String>>,
|
||||||
}
|
}
|
||||||
impl InputVisitable for CompileScss {
|
impl InputVisitable for CompileScss {
|
||||||
fn visit_inputs(&self, visitor: &mut impl compute_graph::rule::InputVisitor) {
|
fn visit_inputs(&self, visitor: &mut impl InputVisitor) {
|
||||||
for input in self.fonts.values() {
|
for input in self.fonts.values() {
|
||||||
visitor.visit(input);
|
visitor.visit(input);
|
||||||
}
|
}
|
||||||
@ -157,7 +158,7 @@ impl<'a> Fs for TrackingFs<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(InputVisitable)]
|
#[derive(InputVisitable)]
|
||||||
struct ReadFile(PathBuf);
|
struct ReadFile(#[skip_visit] PathBuf);
|
||||||
impl Rule for ReadFile {
|
impl Rule for ReadFile {
|
||||||
type Output = String;
|
type Output = String;
|
||||||
fn evaluate(&mut self) -> Self::Output {
|
fn evaluate(&mut self) -> Self::Output {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use compute_graph::{
|
use compute_graph::{
|
||||||
builder::GraphBuilder,
|
builder::GraphBuilder,
|
||||||
rule::{Input, InputVisitable, Rule},
|
input::{Input, InputVisitable},
|
||||||
|
rule::Rule,
|
||||||
synchronicity::Asynchronous,
|
synchronicity::Asynchronous,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,10 +7,8 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use compute_graph::{
|
use compute_graph::{
|
||||||
builder::GraphBuilder,
|
builder::GraphBuilder,
|
||||||
rule::{
|
input::{DynamicInput, Input, InputVisitable},
|
||||||
DynamicInput, DynamicNodeFactory, DynamicRule, DynamicRuleContext, Input, InputVisitable,
|
rule::{DynamicNodeFactory, DynamicRule, DynamicRuleContext, Rule},
|
||||||
Rule,
|
|
||||||
},
|
|
||||||
synchronicity::Asynchronous,
|
synchronicity::Asynchronous,
|
||||||
};
|
};
|
||||||
use content::{HtmlContent, Post};
|
use content::{HtmlContent, Post};
|
||||||
@ -97,6 +95,7 @@ fn find_index(path: PathBuf) -> Option<PathBuf> {
|
|||||||
#[derive(InputVisitable)]
|
#[derive(InputVisitable)]
|
||||||
struct MakeReadNodes {
|
struct MakeReadNodes {
|
||||||
files: Input<Vec<PathBuf>>,
|
files: Input<Vec<PathBuf>>,
|
||||||
|
#[skip_visit]
|
||||||
watcher: Rc<RefCell<FileWatcher>>,
|
watcher: Rc<RefCell<FileWatcher>>,
|
||||||
node_factory: DynamicNodeFactory<PathBuf, ReadPostOutput>,
|
node_factory: DynamicNodeFactory<PathBuf, ReadPostOutput>,
|
||||||
}
|
}
|
||||||
@ -129,6 +128,7 @@ pub type ReadPostOutput = Option<Post<HtmlContent>>;
|
|||||||
|
|
||||||
#[derive(InputVisitable)]
|
#[derive(InputVisitable)]
|
||||||
struct ReadPost {
|
struct ReadPost {
|
||||||
|
#[skip_visit]
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
}
|
}
|
||||||
impl Rule for ReadPost {
|
impl Rule for ReadPost {
|
||||||
|
@ -2,7 +2,8 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use compute_graph::{
|
use compute_graph::{
|
||||||
builder::GraphBuilder,
|
builder::GraphBuilder,
|
||||||
rule::{DynamicInput, DynamicNodeFactory, DynamicRule, Input, InputVisitable, Rule},
|
input::{DynamicInput, Input, InputVisitable},
|
||||||
|
rule::{DynamicNodeFactory, DynamicRule, Rule},
|
||||||
synchronicity::Asynchronous,
|
synchronicity::Asynchronous,
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
@ -77,6 +78,7 @@ impl DynamicRule for MakePostsByTags {
|
|||||||
#[derive(InputVisitable)]
|
#[derive(InputVisitable)]
|
||||||
struct PostsByTag {
|
struct PostsByTag {
|
||||||
posts: DynamicInput<ReadPostOutput>,
|
posts: DynamicInput<ReadPostOutput>,
|
||||||
|
#[skip_visit]
|
||||||
tag: Tag,
|
tag: Tag,
|
||||||
}
|
}
|
||||||
impl Rule for PostsByTag {
|
impl Rule for PostsByTag {
|
||||||
@ -131,7 +133,7 @@ struct Entry {
|
|||||||
#[derive(InputVisitable)]
|
#[derive(InputVisitable)]
|
||||||
struct MakeWriteTagPages {
|
struct MakeWriteTagPages {
|
||||||
tags: DynamicInput<TagAndPosts>,
|
tags: DynamicInput<TagAndPosts>,
|
||||||
#[ignore_input]
|
#[skip_visit]
|
||||||
templates: Input<Templates>,
|
templates: Input<Templates>,
|
||||||
build_context_factory: DynamicNodeFactory<String, Context>,
|
build_context_factory: DynamicNodeFactory<String, Context>,
|
||||||
render_factory: DynamicNodeFactory<String, ()>,
|
render_factory: DynamicNodeFactory<String, ()>,
|
||||||
|
@ -6,8 +6,9 @@ use std::{
|
|||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use compute_graph::{
|
use compute_graph::{
|
||||||
builder::GraphBuilder,
|
builder::GraphBuilder,
|
||||||
|
input::{Input, InputVisitable},
|
||||||
node::NodeValue,
|
node::NodeValue,
|
||||||
rule::{Input, InputVisitable, Rule},
|
rule::Rule,
|
||||||
synchronicity::Asynchronous,
|
synchronicity::Asynchronous,
|
||||||
};
|
};
|
||||||
use log::error;
|
use log::error;
|
||||||
@ -39,7 +40,11 @@ pub fn make_graph(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(InputVisitable)]
|
#[derive(InputVisitable)]
|
||||||
pub struct AddTemplate(String, PathBuf, Input<Templates>);
|
pub struct AddTemplate(
|
||||||
|
#[skip_visit] String,
|
||||||
|
#[skip_visit] PathBuf,
|
||||||
|
Input<Templates>,
|
||||||
|
);
|
||||||
impl AddTemplate {
|
impl AddTemplate {
|
||||||
pub fn new(name: &str, path: PathBuf, base: Input<Templates>) -> Self {
|
pub fn new(name: &str, path: PathBuf, base: Input<Templates>) -> Self {
|
||||||
Self(name.into(), path, base)
|
Self(name.into(), path, base)
|
||||||
@ -86,9 +91,11 @@ static CB: Lazy<u64> = Lazy::new(|| {
|
|||||||
.as_secs()
|
.as_secs()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#[derive(InputVisitable)]
|
||||||
pub struct BuildTemplateContext<T, F> {
|
pub struct BuildTemplateContext<T, F> {
|
||||||
permalink: TemplatePermalink,
|
permalink: TemplatePermalink,
|
||||||
input: Input<T>,
|
input: Input<T>,
|
||||||
|
#[skip_visit]
|
||||||
func: F,
|
func: F,
|
||||||
}
|
}
|
||||||
impl<T, F: Fn(&T, &mut Context) -> ()> BuildTemplateContext<T, F> {
|
impl<T, F: Fn(&T, &mut Context) -> ()> BuildTemplateContext<T, F> {
|
||||||
@ -100,14 +107,6 @@ impl<T, F: Fn(&T, &mut Context) -> ()> BuildTemplateContext<T, F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T, F> InputVisitable for BuildTemplateContext<T, F> {
|
|
||||||
fn visit_inputs(&self, visitor: &mut impl compute_graph::rule::InputVisitor) {
|
|
||||||
if let TemplatePermalink::Dynamic(ref input) = self.permalink {
|
|
||||||
visitor.visit(input);
|
|
||||||
}
|
|
||||||
visitor.visit(&self.input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T: 'static, F: Fn(&T, &mut Context) -> () + 'static> Rule for BuildTemplateContext<T, F> {
|
impl<T: 'static, F: Fn(&T, &mut Context) -> () + 'static> Rule for BuildTemplateContext<T, F> {
|
||||||
type Output = Context;
|
type Output = Context;
|
||||||
fn evaluate(&mut self) -> Self::Output {
|
fn evaluate(&mut self) -> Self::Output {
|
||||||
@ -126,9 +125,10 @@ impl<T: 'static, F: Fn(&T, &mut Context) -> () + 'static> Rule for BuildTemplate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(InputVisitable)]
|
||||||
pub enum TemplatePermalink {
|
pub enum TemplatePermalink {
|
||||||
Constant(&'static str),
|
Constant(#[skip_visit] &'static str),
|
||||||
ConstantOwned(String),
|
ConstantOwned(#[skip_visit] String),
|
||||||
Dynamic(Input<String>),
|
Dynamic(Input<String>),
|
||||||
}
|
}
|
||||||
impl From<&'static str> for TemplatePermalink {
|
impl From<&'static str> for TemplatePermalink {
|
||||||
@ -147,23 +147,14 @@ impl From<Input<String>> for TemplatePermalink {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(InputVisitable)]
|
||||||
pub struct RenderTemplate {
|
pub struct RenderTemplate {
|
||||||
|
#[skip_visit]
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
pub output_path: TemplateOutputPath,
|
pub output_path: TemplateOutputPath,
|
||||||
pub templates: Input<Templates>,
|
pub templates: Input<Templates>,
|
||||||
pub context: Input<Context>,
|
pub context: Input<Context>,
|
||||||
}
|
}
|
||||||
// TODO: derive InputVisitable for enums?
|
|
||||||
// make Input impl InputVisitable, then the derived impl can just call visit_inputs on everything
|
|
||||||
impl InputVisitable for RenderTemplate {
|
|
||||||
fn visit_inputs(&self, visitor: &mut impl compute_graph::rule::InputVisitor) {
|
|
||||||
if let TemplateOutputPath::Dynamic(ref input) = self.output_path {
|
|
||||||
visitor.visit(input);
|
|
||||||
}
|
|
||||||
visitor.visit(&self.templates);
|
|
||||||
visitor.visit(&self.context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Rule for RenderTemplate {
|
impl Rule for RenderTemplate {
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
@ -190,8 +181,9 @@ impl Rule for RenderTemplate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(InputVisitable)]
|
||||||
pub enum TemplateOutputPath {
|
pub enum TemplateOutputPath {
|
||||||
Constant(PathBuf),
|
Constant(#[skip_visit] PathBuf),
|
||||||
Dynamic(Input<PathBuf>),
|
Dynamic(Input<PathBuf>),
|
||||||
}
|
}
|
||||||
impl<'a> From<&'a str> for TemplateOutputPath {
|
impl<'a> From<&'a str> for TemplateOutputPath {
|
||||||
|
@ -8,9 +8,12 @@ use std::io::{BufWriter, Write};
|
|||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
|
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use compute_graph::builder::GraphBuilder;
|
use compute_graph::{
|
||||||
use compute_graph::rule::{DynamicInput, Input, InputVisitable, Rule};
|
builder::GraphBuilder,
|
||||||
use compute_graph::synchronicity::Synchronicity;
|
input::{DynamicInput, Input, InputVisitable},
|
||||||
|
rule::Rule,
|
||||||
|
synchronicity::Synchronicity,
|
||||||
|
};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
pub fn output_writer(path: impl AsRef<Path>) -> Result<impl Write, std::io::Error> {
|
pub fn output_writer(path: impl AsRef<Path>) -> Result<impl Write, std::io::Error> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user