#![allow(clippy::type_complexity)]
use std::marker::PhantomData;
use crate::Handler;
use crate::pipeline::{
AndBoolNode, ChainCall, ClonedNode, ClonedOptionNode, ClonedResultNode, DagAndThenOptionNode,
DagAndThenResultNode, DagCatchNode, DagMapOptionNode, DagMapResultNode, DagRouteNode,
DagThenNode, DedupNode, DiscardOptionNode, DispatchNode, FilterNode, GuardNode, IdentityNode,
InspectErrNode, InspectOptionNode, InspectResultNode, IntoProducer, IntoRefScanStep,
IntoRefStep, IntoStep, MapErrNode, NotNode, OkOrElseNode, OkOrNode, OkResultNode, OnNoneNode,
OrBoolNode, OrElseNode, RefScanNode, StepCall, TapNode, TeeNode, ThenNode,
UnwrapOrElseOptionNode, UnwrapOrElseResultNode, UnwrapOrOptionNode, UnwrapOrResultNode,
XorBoolNode,
};
use crate::world::{Registry, World};
#[doc(hidden)]
pub trait MergeStepCall<Inputs, Out> {
fn call(&mut self, world: &mut World, inputs: Inputs) -> Out;
}
#[doc(hidden)]
#[diagnostic::on_unimplemented(
message = "this function cannot be used as a merge step",
note = "merge steps take reference tuple inputs from the fork arms",
note = "closures with resource parameters are not supported — use a named `fn`"
)]
pub trait IntoMergeStep<Inputs, Out, Params> {
type Step: MergeStepCall<Inputs, Out>;
fn into_merge_step(self, registry: &Registry) -> Self::Step;
}
#[doc(hidden)]
pub struct MergeStep<F, Params: crate::handler::Param> {
f: F,
state: Params::State,
#[allow(dead_code)]
name: &'static str,
}
impl<A, B, Out, F> MergeStepCall<(&A, &B), Out> for MergeStep<F, ()>
where
F: FnMut(&A, &B) -> Out + 'static,
{
#[inline(always)]
fn call(&mut self, _world: &mut World, inputs: (&A, &B)) -> Out {
(self.f)(inputs.0, inputs.1)
}
}
impl<A, B, Out, F> IntoMergeStep<(&A, &B), Out, ()> for F
where
F: FnMut(&A, &B) -> Out + 'static,
{
type Step = MergeStep<F, ()>;
fn into_merge_step(self, registry: &Registry) -> Self::Step {
MergeStep {
f: self,
state: <() as crate::handler::Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_merge2_step {
($($P:ident),+) => {
impl<A, B, Out, F: 'static, $($P: crate::handler::Param + 'static),+>
MergeStepCall<(&A, &B), Out> for MergeStep<F, ($($P,)+)>
where
for<'a> &'a mut F:
FnMut($($P,)+ &A, &B) -> Out +
FnMut($($P::Item<'a>,)+ &A, &B) -> Out,
{
#[inline(always)]
#[allow(non_snake_case)]
fn call(&mut self, world: &mut World, inputs: (&A, &B)) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<$($P,)+ IA, IB, Output>(
mut f: impl FnMut($($P,)+ &IA, &IB) -> Output,
$($P: $P,)+
a: &IA, b: &IB,
) -> Output {
f($($P,)+ a, b)
}
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as crate::handler::Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+ inputs.0, inputs.1)
}
}
impl<A, B, Out, F: 'static, $($P: crate::handler::Param + 'static),+>
IntoMergeStep<(&A, &B), Out, ($($P,)+)> for F
where
for<'a> &'a mut F:
FnMut($($P,)+ &A, &B) -> Out +
FnMut($($P::Item<'a>,)+ &A, &B) -> Out,
{
type Step = MergeStep<F, ($($P,)+)>;
fn into_merge_step(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as crate::handler::Param>::init(registry);
{
#[allow(non_snake_case)]
let ($($P,)+) = &state;
registry.check_access(&[
$((<$P as crate::handler::Param>::resource_id($P),
std::any::type_name::<$P>()),)+
]);
}
MergeStep { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
impl<A, B, C, Out, F> MergeStepCall<(&A, &B, &C), Out> for MergeStep<F, ()>
where
F: FnMut(&A, &B, &C) -> Out + 'static,
{
#[inline(always)]
fn call(&mut self, _world: &mut World, inputs: (&A, &B, &C)) -> Out {
(self.f)(inputs.0, inputs.1, inputs.2)
}
}
impl<A, B, C, Out, F> IntoMergeStep<(&A, &B, &C), Out, ()> for F
where
F: FnMut(&A, &B, &C) -> Out + 'static,
{
type Step = MergeStep<F, ()>;
fn into_merge_step(self, registry: &Registry) -> Self::Step {
MergeStep {
f: self,
state: <() as crate::handler::Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_merge3_step {
($($P:ident),+) => {
impl<A, B, C, Out, F: 'static, $($P: crate::handler::Param + 'static),+>
MergeStepCall<(&A, &B, &C), Out> for MergeStep<F, ($($P,)+)>
where
for<'a> &'a mut F:
FnMut($($P,)+ &A, &B, &C) -> Out +
FnMut($($P::Item<'a>,)+ &A, &B, &C) -> Out,
{
#[inline(always)]
#[allow(non_snake_case)]
fn call(&mut self, world: &mut World, inputs: (&A, &B, &C)) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<$($P,)+ IA, IB, IC, Output>(
mut f: impl FnMut($($P,)+ &IA, &IB, &IC) -> Output,
$($P: $P,)+
a: &IA, b: &IB, c: &IC,
) -> Output {
f($($P,)+ a, b, c)
}
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as crate::handler::Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+ inputs.0, inputs.1, inputs.2)
}
}
impl<A, B, C, Out, F: 'static, $($P: crate::handler::Param + 'static),+>
IntoMergeStep<(&A, &B, &C), Out, ($($P,)+)> for F
where
for<'a> &'a mut F:
FnMut($($P,)+ &A, &B, &C) -> Out +
FnMut($($P::Item<'a>,)+ &A, &B, &C) -> Out,
{
type Step = MergeStep<F, ($($P,)+)>;
fn into_merge_step(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as crate::handler::Param>::init(registry);
{
#[allow(non_snake_case)]
let ($($P,)+) = &state;
registry.check_access(&[
$((<$P as crate::handler::Param>::resource_id($P),
std::any::type_name::<$P>()),)+
]);
}
MergeStep { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
impl<A, B, C, D, Out, F> MergeStepCall<(&A, &B, &C, &D), Out> for MergeStep<F, ()>
where
F: FnMut(&A, &B, &C, &D) -> Out + 'static,
{
#[inline(always)]
fn call(&mut self, _world: &mut World, i: (&A, &B, &C, &D)) -> Out {
(self.f)(i.0, i.1, i.2, i.3)
}
}
impl<A, B, C, D, Out, F> IntoMergeStep<(&A, &B, &C, &D), Out, ()> for F
where
F: FnMut(&A, &B, &C, &D) -> Out + 'static,
{
type Step = MergeStep<F, ()>;
fn into_merge_step(self, registry: &Registry) -> Self::Step {
MergeStep {
f: self,
state: <() as crate::handler::Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_merge4_step {
($($P:ident),+) => {
impl<A, B, C, D, Out, F: 'static, $($P: crate::handler::Param + 'static),+>
MergeStepCall<(&A, &B, &C, &D), Out> for MergeStep<F, ($($P,)+)>
where for<'a> &'a mut F:
FnMut($($P,)+ &A, &B, &C, &D) -> Out +
FnMut($($P::Item<'a>,)+ &A, &B, &C, &D) -> Out,
{
#[inline(always)]
#[allow(non_snake_case)]
fn call(&mut self, world: &mut World, i: (&A, &B, &C, &D)) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<$($P,)+ IA, IB, IC, ID, Output>(
mut f: impl FnMut($($P,)+ &IA, &IB, &IC, &ID) -> Output,
$($P: $P,)+ a: &IA, b: &IB, c: &IC, d: &ID,
) -> Output { f($($P,)+ a, b, c, d) }
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as crate::handler::Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+ i.0, i.1, i.2, i.3)
}
}
impl<A, B, C, D, Out, F: 'static, $($P: crate::handler::Param + 'static),+>
IntoMergeStep<(&A, &B, &C, &D), Out, ($($P,)+)> for F
where for<'a> &'a mut F:
FnMut($($P,)+ &A, &B, &C, &D) -> Out +
FnMut($($P::Item<'a>,)+ &A, &B, &C, &D) -> Out,
{
type Step = MergeStep<F, ($($P,)+)>;
fn into_merge_step(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as crate::handler::Param>::init(registry);
{ #[allow(non_snake_case)] let ($($P,)+) = &state;
registry.check_access(&[$((<$P as crate::handler::Param>::resource_id($P), std::any::type_name::<$P>()),)+]); }
MergeStep { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
impl<A, B, C, D, E, Out, F> MergeStepCall<(&A, &B, &C, &D, &E), Out> for MergeStep<F, ()>
where
F: FnMut(&A, &B, &C, &D, &E) -> Out + 'static,
{
#[inline(always)]
fn call(&mut self, _world: &mut World, i: (&A, &B, &C, &D, &E)) -> Out {
(self.f)(i.0, i.1, i.2, i.3, i.4)
}
}
impl<A, B, C, D, E, Out, F> IntoMergeStep<(&A, &B, &C, &D, &E), Out, ()> for F
where
F: FnMut(&A, &B, &C, &D, &E) -> Out + 'static,
{
type Step = MergeStep<F, ()>;
fn into_merge_step(self, registry: &Registry) -> Self::Step {
MergeStep {
f: self,
state: <() as crate::handler::Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_merge5_step {
($($P:ident),+) => {
impl<A, B, C, D, E, Out, F: 'static, $($P: crate::handler::Param + 'static),+>
MergeStepCall<(&A, &B, &C, &D, &E), Out> for MergeStep<F, ($($P,)+)>
where for<'a> &'a mut F:
FnMut($($P,)+ &A, &B, &C, &D, &E) -> Out +
FnMut($($P::Item<'a>,)+ &A, &B, &C, &D, &E) -> Out,
{
#[inline(always)]
#[allow(non_snake_case)]
fn call(&mut self, world: &mut World, i: (&A, &B, &C, &D, &E)) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<$($P,)+ IA, IB, IC, ID, IE, Output>(
mut f: impl FnMut($($P,)+ &IA, &IB, &IC, &ID, &IE) -> Output,
$($P: $P,)+ a: &IA, b: &IB, c: &IC, d: &ID, e: &IE,
) -> Output { f($($P,)+ a, b, c, d, e) }
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as crate::handler::Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+ i.0, i.1, i.2, i.3, i.4)
}
}
impl<A, B, C, D, E, Out, F: 'static, $($P: crate::handler::Param + 'static),+>
IntoMergeStep<(&A, &B, &C, &D, &E), Out, ($($P,)+)> for F
where for<'a> &'a mut F:
FnMut($($P,)+ &A, &B, &C, &D, &E) -> Out +
FnMut($($P::Item<'a>,)+ &A, &B, &C, &D, &E) -> Out,
{
type Step = MergeStep<F, ($($P,)+)>;
fn into_merge_step(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as crate::handler::Param>::init(registry);
{ #[allow(non_snake_case)] let ($($P,)+) = &state;
registry.check_access(&[$((<$P as crate::handler::Param>::resource_id($P), std::any::type_name::<$P>()),)+]); }
MergeStep { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
all_tuples!(impl_merge2_step);
all_tuples!(impl_merge3_step);
all_tuples!(impl_merge4_step);
all_tuples!(impl_merge5_step);
#[must_use = "a DAG builder does nothing unless you chain steps and call .build()"]
pub struct DagBuilder<E>(PhantomData<fn(E)>);
impl<E> DagBuilder<E> {
pub fn new() -> Self {
Self(PhantomData)
}
pub fn root<Out, Params, S>(
self,
f: S,
registry: &Registry,
) -> DagChain<E, Out, ThenNode<IdentityNode, S::Step>>
where
Out: 'static,
S: IntoStep<E, Out, Params>,
{
DagChain {
chain: ThenNode {
prev: IdentityNode,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
}
impl<E> Default for DagBuilder<E> {
fn default() -> Self {
Self::new()
}
}
#[must_use = "DAG chain does nothing until .build() is called"]
pub struct DagChain<E, Out, Chain> {
pub(crate) chain: Chain,
pub(crate) _marker: PhantomData<fn(E) -> Out>,
}
impl<E, Out: 'static, Chain> DagChain<E, Out, Chain> {
pub fn fork(self) -> DagChainFork<E, Out, Chain, ()> {
DagChainFork {
chain: self.chain,
arms: (),
_marker: PhantomData,
}
}
}
impl<E, Chain> DagChain<E, (), Chain>
where
Chain: ChainCall<E, Out = ()> + Send,
{
#[must_use = "building a DAG without storing it does nothing"]
pub fn build(self) -> Dag<Chain> {
Dag { chain: self.chain }
}
}
impl<E, Chain> DagChain<E, Option<()>, Chain>
where
Chain: ChainCall<E, Out = Option<()>> + Send,
{
#[must_use = "building a DAG without storing it does nothing"]
pub fn build(self) -> Dag<DiscardOptionNode<Chain>> {
Dag {
chain: DiscardOptionNode { prev: self.chain },
}
}
}
pub struct DagArmSeed<In>(PhantomData<fn(*const In)>);
impl<In: 'static> DagArmSeed<In> {
pub fn new() -> Self {
Self(PhantomData)
}
}
impl<In: 'static> Default for DagArmSeed<In> {
fn default() -> Self {
Self::new()
}
}
impl<In: 'static> DagArmSeed<In> {
pub fn then<Out, Params, S>(
self,
f: S,
registry: &Registry,
) -> DagArm<In, Out, ThenNode<IdentityNode, S::Step>>
where
Out: 'static,
S: IntoStep<&'static In, Out, Params>,
S::Step: for<'a> StepCall<&'a In, Out = Out>,
{
DagArm {
chain: ThenNode {
prev: IdentityNode,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
}
pub struct DagArm<In, Out, Chain> {
pub(crate) chain: Chain,
pub(crate) _marker: PhantomData<fn(*const In) -> Out>,
}
impl<In: 'static, Out: 'static, Chain> DagArm<In, Out, Chain> {
pub fn fork(self) -> DagArmFork<In, Out, Chain, ()> {
DagArmFork {
chain: self.chain,
arms: (),
_marker: PhantomData,
}
}
}
pub struct DagChainFork<E, ForkOut, Chain, Arms> {
chain: Chain,
arms: Arms,
_marker: PhantomData<fn(E) -> ForkOut>,
}
pub struct DagArmFork<In, ForkOut, Chain, Arms> {
chain: Chain,
arms: Arms,
_marker: PhantomData<fn(*const In) -> ForkOut>,
}
pub struct Dag<Chain> {
chain: Chain,
}
impl<E, Chain> Handler<E> for Dag<Chain>
where
Chain: ChainCall<E, Out = ()> + Send,
{
fn run(&mut self, world: &mut World, event: E) {
self.chain.call(world, event);
}
fn name(&self) -> &'static str {
"dag::Dag"
}
}
macro_rules! impl_dag_combinators {
(builder: $Builder:ident, upstream: $U:ident) => {
impl<$U, Out: 'static, Chain> $Builder<$U, Out, Chain> {
pub fn then<NewOut, Params, S>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, NewOut, DagThenNode<Chain, S::Step, NewOut>>
where
NewOut: 'static,
S: IntoStep<&'static Out, NewOut, Params>,
S::Step: for<'a> StepCall<&'a Out, Out = NewOut>,
{
$Builder {
chain: DagThenNode {
prev: self.chain,
step: f.into_step(registry),
_out: PhantomData,
},
_marker: PhantomData,
}
}
pub fn dispatch<H: Handler<Out>>(
self,
handler: H,
) -> $Builder<$U, (), DispatchNode<Chain, H>> {
$Builder {
chain: DispatchNode {
prev: self.chain,
handler,
},
_marker: PhantomData,
}
}
pub fn guard<Params, S: IntoRefStep<Out, bool, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Option<Out>, GuardNode<Chain, S::Step>> {
$Builder {
chain: GuardNode {
prev: self.chain,
step: f.into_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn view<V: crate::view::View<Out>>(
self,
) -> crate::view::ViewScope<$U, Out, V, Chain, ()> {
crate::view::ViewScope::new(self.chain)
}
pub fn tap<Params, S: IntoRefStep<Out, (), Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Out, TapNode<Chain, S::Step>> {
$Builder {
chain: TapNode {
prev: self.chain,
step: f.into_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn route<NewOut, C0, C1, Params, Pred: IntoRefStep<Out, bool, Params>>(
self,
pred: Pred,
registry: &Registry,
on_true: DagArm<Out, NewOut, C0>,
on_false: DagArm<Out, NewOut, C1>,
) -> $Builder<$U, NewOut, DagRouteNode<Chain, Pred::Step, C0, C1, NewOut>>
where
C0: for<'a> ChainCall<&'a Out, Out = NewOut>,
C1: for<'a> ChainCall<&'a Out, Out = NewOut>,
{
$Builder {
chain: DagRouteNode {
prev: self.chain,
pred: pred.into_ref_step(registry),
on_true: on_true.chain,
on_false: on_false.chain,
_out: PhantomData,
},
_marker: PhantomData,
}
}
pub fn tee<C>(self, side: DagArm<Out, (), C>) -> $Builder<$U, Out, TeeNode<Chain, C>>
where
C: for<'a> ChainCall<&'a Out, Out = ()>,
{
$Builder {
chain: TeeNode {
prev: self.chain,
side: side.chain,
},
_marker: PhantomData,
}
}
pub fn scan<Acc, NewOut, Params, S>(
self,
initial: Acc,
f: S,
registry: &Registry,
) -> $Builder<$U, NewOut, RefScanNode<Chain, S::Step, Acc>>
where
Acc: 'static,
NewOut: 'static,
S: IntoRefScanStep<Acc, Out, NewOut, Params>,
{
$Builder {
chain: RefScanNode {
prev: self.chain,
step: f.into_ref_scan_step(registry),
acc: initial,
},
_marker: PhantomData,
}
}
}
impl<$U, Out: PartialEq + Clone + 'static, Chain> $Builder<$U, Out, Chain> {
pub fn dedup(self) -> $Builder<$U, Option<Out>, DedupNode<Chain, Out>> {
$Builder {
chain: DedupNode {
prev: self.chain,
last: None,
},
_marker: PhantomData,
}
}
}
impl<$U, Chain> $Builder<$U, bool, Chain> {
#[allow(clippy::should_implement_trait)]
pub fn not(self) -> $Builder<$U, bool, NotNode<Chain>> {
$Builder {
chain: NotNode { prev: self.chain },
_marker: PhantomData,
}
}
pub fn and<Params, S: IntoProducer<bool, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, bool, AndBoolNode<Chain, S::Step>> {
$Builder {
chain: AndBoolNode {
prev: self.chain,
producer: f.into_producer(registry),
},
_marker: PhantomData,
}
}
pub fn or<Params, S: IntoProducer<bool, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, bool, OrBoolNode<Chain, S::Step>> {
$Builder {
chain: OrBoolNode {
prev: self.chain,
producer: f.into_producer(registry),
},
_marker: PhantomData,
}
}
pub fn xor<Params, S: IntoProducer<bool, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, bool, XorBoolNode<Chain, S::Step>> {
$Builder {
chain: XorBoolNode {
prev: self.chain,
producer: f.into_producer(registry),
},
_marker: PhantomData,
}
}
}
impl<'a, $U, T: Clone, Chain> $Builder<$U, &'a T, Chain> {
pub fn cloned(self) -> $Builder<$U, T, ClonedNode<Chain>> {
$Builder {
chain: ClonedNode { prev: self.chain },
_marker: PhantomData,
}
}
}
impl<'a, $U, T: Clone, Chain> $Builder<$U, Option<&'a T>, Chain> {
pub fn cloned(self) -> $Builder<$U, Option<T>, ClonedOptionNode<Chain>> {
$Builder {
chain: ClonedOptionNode { prev: self.chain },
_marker: PhantomData,
}
}
}
impl<'a, $U, T: Clone, Err, Chain> $Builder<$U, Result<&'a T, Err>, Chain> {
pub fn cloned(self) -> $Builder<$U, Result<T, Err>, ClonedResultNode<Chain>> {
$Builder {
chain: ClonedResultNode { prev: self.chain },
_marker: PhantomData,
}
}
}
impl<$U, T: 'static, Chain> $Builder<$U, Option<T>, Chain> {
pub fn map<U, Params, S: IntoStep<&'static T, U, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Option<U>, DagMapOptionNode<Chain, S::Step, U>>
where
U: 'static,
S::Step: for<'x> StepCall<&'x T, Out = U>,
{
$Builder {
chain: DagMapOptionNode {
prev: self.chain,
step: f.into_step(registry),
_out: PhantomData,
},
_marker: PhantomData,
}
}
pub fn and_then<U, Params, S: IntoStep<&'static T, Option<U>, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Option<U>, DagAndThenOptionNode<Chain, S::Step, U>>
where
U: 'static,
S::Step: for<'x> StepCall<&'x T, Out = Option<U>>,
{
$Builder {
chain: DagAndThenOptionNode {
prev: self.chain,
step: f.into_step(registry),
_out: PhantomData,
},
_marker: PhantomData,
}
}
pub fn on_none<Params, S: IntoProducer<(), Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Option<T>, OnNoneNode<Chain, S::Step>> {
$Builder {
chain: OnNoneNode {
prev: self.chain,
producer: f.into_producer(registry),
},
_marker: PhantomData,
}
}
pub fn filter<Params, S: IntoRefStep<T, bool, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Option<T>, FilterNode<Chain, S::Step>> {
$Builder {
chain: FilterNode {
prev: self.chain,
step: f.into_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn inspect<Params, S: IntoRefStep<T, (), Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Option<T>, InspectOptionNode<Chain, S::Step>> {
$Builder {
chain: InspectOptionNode {
prev: self.chain,
step: f.into_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn ok_or<Err: Clone>(
self,
err: Err,
) -> $Builder<$U, Result<T, Err>, OkOrNode<Chain, Err>> {
$Builder {
chain: OkOrNode {
prev: self.chain,
err,
},
_marker: PhantomData,
}
}
pub fn ok_or_else<Err, Params, S: IntoProducer<Err, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Result<T, Err>, OkOrElseNode<Chain, S::Step>> {
$Builder {
chain: OkOrElseNode {
prev: self.chain,
producer: f.into_producer(registry),
},
_marker: PhantomData,
}
}
pub fn unwrap_or(self, default: T) -> $Builder<$U, T, UnwrapOrOptionNode<Chain, T>>
where
T: Clone,
{
$Builder {
chain: UnwrapOrOptionNode {
prev: self.chain,
default,
},
_marker: PhantomData,
}
}
pub fn unwrap_or_else<Params, S: IntoProducer<T, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, T, UnwrapOrElseOptionNode<Chain, S::Step>> {
$Builder {
chain: UnwrapOrElseOptionNode {
prev: self.chain,
producer: f.into_producer(registry),
},
_marker: PhantomData,
}
}
}
impl<$U, T: 'static, Err: 'static, Chain> $Builder<$U, Result<T, Err>, Chain> {
pub fn map<U, Params, S: IntoStep<&'static T, U, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Result<U, Err>, DagMapResultNode<Chain, S::Step, U>>
where
U: 'static,
S::Step: for<'x> StepCall<&'x T, Out = U>,
{
$Builder {
chain: DagMapResultNode {
prev: self.chain,
step: f.into_step(registry),
_out: PhantomData,
},
_marker: PhantomData,
}
}
pub fn and_then<U, Params, S: IntoStep<&'static T, Result<U, Err>, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Result<U, Err>, DagAndThenResultNode<Chain, S::Step, U>>
where
U: 'static,
S::Step: for<'x> StepCall<&'x T, Out = Result<U, Err>>,
{
$Builder {
chain: DagAndThenResultNode {
prev: self.chain,
step: f.into_step(registry),
_out: PhantomData,
},
_marker: PhantomData,
}
}
pub fn catch<Params, S: IntoStep<&'static Err, (), Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Option<T>, DagCatchNode<Chain, S::Step>>
where
S::Step: for<'x> StepCall<&'x Err, Out = ()>,
{
$Builder {
chain: DagCatchNode {
prev: self.chain,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
pub fn map_err<Err2, Params, S: IntoStep<Err, Err2, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Result<T, Err2>, MapErrNode<Chain, S::Step>> {
$Builder {
chain: MapErrNode {
prev: self.chain,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
pub fn or_else<Err2, Params, S: IntoStep<Err, Result<T, Err2>, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Result<T, Err2>, OrElseNode<Chain, S::Step>> {
$Builder {
chain: OrElseNode {
prev: self.chain,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
pub fn inspect<Params, S: IntoRefStep<T, (), Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Result<T, Err>, InspectResultNode<Chain, S::Step>> {
$Builder {
chain: InspectResultNode {
prev: self.chain,
step: f.into_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn inspect_err<Params, S: IntoRefStep<Err, (), Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, Result<T, Err>, InspectErrNode<Chain, S::Step>> {
$Builder {
chain: InspectErrNode {
prev: self.chain,
step: f.into_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn ok(self) -> $Builder<$U, Option<T>, OkResultNode<Chain>> {
$Builder {
chain: OkResultNode { prev: self.chain },
_marker: PhantomData,
}
}
pub fn unwrap_or(self, default: T) -> $Builder<$U, T, UnwrapOrResultNode<Chain, T>>
where
T: Clone,
{
$Builder {
chain: UnwrapOrResultNode {
prev: self.chain,
default,
},
_marker: PhantomData,
}
}
pub fn unwrap_or_else<Params, S: IntoStep<Err, T, Params>>(
self,
f: S,
registry: &Registry,
) -> $Builder<$U, T, UnwrapOrElseResultNode<Chain, S::Step>> {
$Builder {
chain: UnwrapOrElseResultNode {
prev: self.chain,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
}
};
}
impl_dag_combinators!(builder: DagChain, upstream: E);
impl_dag_combinators!(builder: DagArm, upstream: In);
#[doc(hidden)]
pub struct MergeNode2<Chain, C0, C1, MS, ForkOut, A0, A1, MOut> {
pub(crate) chain: Chain,
pub(crate) arm0: C0,
pub(crate) arm1: C1,
pub(crate) merge: MS,
pub(crate) _marker: PhantomData<fn(ForkOut) -> (A0, A1, MOut)>,
}
impl<In, Chain, C0, C1, MS, ForkOut, A0, A1, MOut> ChainCall<In>
for MergeNode2<Chain, C0, C1, MS, ForkOut, A0, A1, MOut>
where
ForkOut: 'static,
A0: 'static,
A1: 'static,
Chain: ChainCall<In, Out = ForkOut>,
C0: for<'a> ChainCall<&'a ForkOut, Out = A0>,
C1: for<'a> ChainCall<&'a ForkOut, Out = A1>,
MS: for<'x> MergeStepCall<(&'x A0, &'x A1), MOut>,
{
type Out = MOut;
fn call(&mut self, world: &mut World, input: In) -> MOut {
let fork_out = self.chain.call(world, input);
let o0 = self.arm0.call(world, &fork_out);
let o1 = self.arm1.call(world, &fork_out);
self.merge.call(world, (&o0, &o1))
}
}
#[doc(hidden)]
pub struct MergeNode3<Chain, C0, C1, C2, MS, ForkOut, A0, A1, A2, MOut> {
pub(crate) chain: Chain,
pub(crate) arm0: C0,
pub(crate) arm1: C1,
pub(crate) arm2: C2,
pub(crate) merge: MS,
pub(crate) _marker: PhantomData<fn(ForkOut) -> (A0, A1, A2, MOut)>,
}
impl<In, Chain, C0, C1, C2, MS, ForkOut, A0, A1, A2, MOut> ChainCall<In>
for MergeNode3<Chain, C0, C1, C2, MS, ForkOut, A0, A1, A2, MOut>
where
ForkOut: 'static,
A0: 'static,
A1: 'static,
A2: 'static,
Chain: ChainCall<In, Out = ForkOut>,
C0: for<'a> ChainCall<&'a ForkOut, Out = A0>,
C1: for<'a> ChainCall<&'a ForkOut, Out = A1>,
C2: for<'a> ChainCall<&'a ForkOut, Out = A2>,
MS: for<'x> MergeStepCall<(&'x A0, &'x A1, &'x A2), MOut>,
{
type Out = MOut;
fn call(&mut self, world: &mut World, input: In) -> MOut {
let fork_out = self.chain.call(world, input);
let o0 = self.arm0.call(world, &fork_out);
let o1 = self.arm1.call(world, &fork_out);
let o2 = self.arm2.call(world, &fork_out);
self.merge.call(world, (&o0, &o1, &o2))
}
}
#[doc(hidden)]
pub struct MergeNode4<Chain, C0, C1, C2, C3, MS, ForkOut, A0, A1, A2, A3, MOut> {
pub(crate) chain: Chain,
pub(crate) arm0: C0,
pub(crate) arm1: C1,
pub(crate) arm2: C2,
pub(crate) arm3: C3,
pub(crate) merge: MS,
pub(crate) _marker: PhantomData<fn(ForkOut) -> (A0, A1, A2, A3, MOut)>,
}
#[allow(clippy::many_single_char_names)]
impl<In, Chain, C0, C1, C2, C3, MS, ForkOut, A0, A1, A2, A3, MOut> ChainCall<In>
for MergeNode4<Chain, C0, C1, C2, C3, MS, ForkOut, A0, A1, A2, A3, MOut>
where
ForkOut: 'static,
A0: 'static,
A1: 'static,
A2: 'static,
A3: 'static,
Chain: ChainCall<In, Out = ForkOut>,
C0: for<'a> ChainCall<&'a ForkOut, Out = A0>,
C1: for<'a> ChainCall<&'a ForkOut, Out = A1>,
C2: for<'a> ChainCall<&'a ForkOut, Out = A2>,
C3: for<'a> ChainCall<&'a ForkOut, Out = A3>,
MS: for<'x> MergeStepCall<(&'x A0, &'x A1, &'x A2, &'x A3), MOut>,
{
type Out = MOut;
fn call(&mut self, world: &mut World, input: In) -> MOut {
let fork_out = self.chain.call(world, input);
let o0 = self.arm0.call(world, &fork_out);
let o1 = self.arm1.call(world, &fork_out);
let o2 = self.arm2.call(world, &fork_out);
let o3 = self.arm3.call(world, &fork_out);
self.merge.call(world, (&o0, &o1, &o2, &o3))
}
}
#[doc(hidden)]
pub struct JoinNode2<Chain, C0, C1, ForkOut> {
pub(crate) chain: Chain,
pub(crate) arm0: C0,
pub(crate) arm1: C1,
pub(crate) _marker: PhantomData<fn() -> ForkOut>,
}
impl<In, Chain, C0, C1, ForkOut> ChainCall<In> for JoinNode2<Chain, C0, C1, ForkOut>
where
ForkOut: 'static,
Chain: ChainCall<In, Out = ForkOut>,
C0: for<'a> ChainCall<&'a ForkOut, Out = ()>,
C1: for<'a> ChainCall<&'a ForkOut, Out = ()>,
{
type Out = ();
fn call(&mut self, world: &mut World, input: In) {
let fork_out = self.chain.call(world, input);
self.arm0.call(world, &fork_out);
self.arm1.call(world, &fork_out);
}
}
#[doc(hidden)]
pub struct JoinNode3<Chain, C0, C1, C2, ForkOut> {
pub(crate) chain: Chain,
pub(crate) arm0: C0,
pub(crate) arm1: C1,
pub(crate) arm2: C2,
pub(crate) _marker: PhantomData<fn() -> ForkOut>,
}
impl<In, Chain, C0, C1, C2, ForkOut> ChainCall<In> for JoinNode3<Chain, C0, C1, C2, ForkOut>
where
ForkOut: 'static,
Chain: ChainCall<In, Out = ForkOut>,
C0: for<'a> ChainCall<&'a ForkOut, Out = ()>,
C1: for<'a> ChainCall<&'a ForkOut, Out = ()>,
C2: for<'a> ChainCall<&'a ForkOut, Out = ()>,
{
type Out = ();
fn call(&mut self, world: &mut World, input: In) {
let fork_out = self.chain.call(world, input);
self.arm0.call(world, &fork_out);
self.arm1.call(world, &fork_out);
self.arm2.call(world, &fork_out);
}
}
#[doc(hidden)]
pub struct JoinNode4<Chain, C0, C1, C2, C3, ForkOut> {
pub(crate) chain: Chain,
pub(crate) arm0: C0,
pub(crate) arm1: C1,
pub(crate) arm2: C2,
pub(crate) arm3: C3,
pub(crate) _marker: PhantomData<fn() -> ForkOut>,
}
#[allow(clippy::many_single_char_names)]
impl<In, Chain, C0, C1, C2, C3, ForkOut> ChainCall<In> for JoinNode4<Chain, C0, C1, C2, C3, ForkOut>
where
ForkOut: 'static,
Chain: ChainCall<In, Out = ForkOut>,
C0: for<'a> ChainCall<&'a ForkOut, Out = ()>,
C1: for<'a> ChainCall<&'a ForkOut, Out = ()>,
C2: for<'a> ChainCall<&'a ForkOut, Out = ()>,
C3: for<'a> ChainCall<&'a ForkOut, Out = ()>,
{
type Out = ();
fn call(&mut self, world: &mut World, input: In) {
let fork_out = self.chain.call(world, input);
self.arm0.call(world, &fork_out);
self.arm1.call(world, &fork_out);
self.arm2.call(world, &fork_out);
self.arm3.call(world, &fork_out);
}
}
macro_rules! define_dag_splat_builders {
(
$N:literal,
chain: $SplatChain:ident,
arm: $SplatArm:ident,
arm_start: $SplatArmStart:ident,
splat_then: $SplatThenNode:ident,
splat_arm_start: $SplatArmStartNode:ident,
($($T:ident),+),
($($idx:tt),+)
) => {
#[doc(hidden)]
pub struct $SplatThenNode<Chain, MS, $($T,)+ NewOut> {
pub(crate) chain: Chain,
pub(crate) merge: MS,
pub(crate) _marker: PhantomData<fn() -> ($($T,)+ NewOut)>,
}
impl<In, Chain, MS, $($T: 'static,)+ NewOut> ChainCall<In>
for $SplatThenNode<Chain, MS, $($T,)+ NewOut>
where
Chain: ChainCall<In, Out = ($($T,)+)>,
MS: for<'x> MergeStepCall<($(&'x $T,)+), NewOut>,
{
type Out = NewOut;
fn call(&mut self, world: &mut World, input: In) -> NewOut {
let tuple = self.chain.call(world, input);
self.merge.call(world, ($(&tuple.$idx,)+))
}
}
#[doc(hidden)]
pub struct $SplatArmStartNode<MS, $($T,)+ Out> {
pub(crate) merge: MS,
pub(crate) _marker: PhantomData<fn() -> ($($T,)+ Out)>,
}
impl<'inp, $($T: 'static,)+ MS, Out> ChainCall<&'inp ($($T,)+)>
for $SplatArmStartNode<MS, $($T,)+ Out>
where
MS: for<'x> MergeStepCall<($(&'x $T,)+), Out>,
{
type Out = Out;
fn call(&mut self, world: &mut World, input: &($($T,)+)) -> Out {
self.merge.call(world, ($(&input.$idx,)+))
}
}
#[doc(hidden)]
pub struct $SplatChain<E, $($T,)+ Chain> {
chain: Chain,
_marker: PhantomData<fn(E) -> ($($T,)+)>,
}
impl<E, $($T: 'static,)+ Chain> $SplatChain<E, $($T,)+ Chain> {
pub fn then<NewOut, Params, S>(
self,
f: S,
registry: &Registry,
) -> DagChain<E, NewOut, $SplatThenNode<Chain, S::Step, $($T,)+ NewOut>>
where
NewOut: 'static,
S: IntoMergeStep<($(&'static $T,)+), NewOut, Params>,
S::Step: for<'x> MergeStepCall<($(&'x $T,)+), NewOut>,
{
DagChain {
chain: $SplatThenNode {
chain: self.chain,
merge: f.into_merge_step(registry),
_marker: PhantomData,
},
_marker: PhantomData,
}
}
}
impl<E, $($T: 'static,)+ Chain> DagChain<E, ($($T,)+), Chain> {
pub fn splat(self) -> $SplatChain<E, $($T,)+ Chain> {
$SplatChain {
chain: self.chain,
_marker: PhantomData,
}
}
}
#[doc(hidden)]
pub struct $SplatArm<In, $($T,)+ Chain> {
chain: Chain,
_marker: PhantomData<fn(*const In) -> ($($T,)+)>,
}
impl<In: 'static, $($T: 'static,)+ Chain> $SplatArm<In, $($T,)+ Chain> {
pub fn then<NewOut, Params, S>(
self,
f: S,
registry: &Registry,
) -> DagArm<In, NewOut, $SplatThenNode<Chain, S::Step, $($T,)+ NewOut>>
where
NewOut: 'static,
S: IntoMergeStep<($(&'static $T,)+), NewOut, Params>,
S::Step: for<'x> MergeStepCall<($(&'x $T,)+), NewOut>,
{
DagArm {
chain: $SplatThenNode {
chain: self.chain,
merge: f.into_merge_step(registry),
_marker: PhantomData,
},
_marker: PhantomData,
}
}
}
impl<In: 'static, $($T: 'static,)+ Chain> DagArm<In, ($($T,)+), Chain> {
pub fn splat(self) -> $SplatArm<In, $($T,)+ Chain> {
$SplatArm {
chain: self.chain,
_marker: PhantomData,
}
}
}
#[doc(hidden)]
pub struct $SplatArmStart<$($T),+>(PhantomData<fn(($($T,)+))>);
impl<$($T: 'static),+> $SplatArmStart<$($T),+> {
pub fn then<Out, Params, S>(
self,
f: S,
registry: &Registry,
) -> DagArm<($($T,)+), Out, $SplatArmStartNode<S::Step, $($T,)+ Out>>
where
Out: 'static,
S: IntoMergeStep<($(&'static $T,)+), Out, Params>,
S::Step: for<'x> MergeStepCall<($(&'x $T,)+), Out>,
{
DagArm {
chain: $SplatArmStartNode {
merge: f.into_merge_step(registry),
_marker: PhantomData,
},
_marker: PhantomData,
}
}
}
impl<$($T: 'static),+> DagArmSeed<($($T,)+)> {
pub fn splat(self) -> $SplatArmStart<$($T),+> {
$SplatArmStart(PhantomData)
}
}
};
}
define_dag_splat_builders!(2,
chain: DagSplatChain2,
arm: DagSplatArm2,
arm_start: DagSplatArmStart2,
splat_then: SplatThenNode2,
splat_arm_start: SplatArmStartNode2,
(T0, T1),
(0, 1)
);
define_dag_splat_builders!(3,
chain: DagSplatChain3,
arm: DagSplatArm3,
arm_start: DagSplatArmStart3,
splat_then: SplatThenNode3,
splat_arm_start: SplatArmStartNode3,
(T0, T1, T2),
(0, 1, 2)
);
define_dag_splat_builders!(4,
chain: DagSplatChain4,
arm: DagSplatArm4,
arm_start: DagSplatArmStart4,
splat_then: SplatThenNode4,
splat_arm_start: SplatArmStartNode4,
(T0, T1, T2, T3),
(0, 1, 2, 3)
);
define_dag_splat_builders!(5,
chain: DagSplatChain5,
arm: DagSplatArm5,
arm_start: DagSplatArmStart5,
splat_then: SplatThenNode5,
splat_arm_start: SplatArmStartNode5,
(T0, T1, T2, T3, T4),
(0, 1, 2, 3, 4)
);
macro_rules! impl_dag_fork {
(
fork: $Fork:ident,
output: $Output:ident,
upstream: $U:ident
) => {
impl<$U, ForkOut, Chain> $Fork<$U, ForkOut, Chain, ()> {
pub fn arm<AOut, ACh>(
self,
f: impl FnOnce(DagArmSeed<ForkOut>) -> DagArm<ForkOut, AOut, ACh>,
) -> $Fork<$U, ForkOut, Chain, (DagArm<ForkOut, AOut, ACh>,)> {
let arm = f(DagArmSeed(PhantomData));
$Fork {
chain: self.chain,
arms: (arm,),
_marker: PhantomData,
}
}
}
impl<$U, ForkOut, Chain, A0, C0> $Fork<$U, ForkOut, Chain, (DagArm<ForkOut, A0, C0>,)> {
pub fn arm<AOut, ACh>(
self,
f: impl FnOnce(DagArmSeed<ForkOut>) -> DagArm<ForkOut, AOut, ACh>,
) -> $Fork<$U, ForkOut, Chain, (DagArm<ForkOut, A0, C0>, DagArm<ForkOut, AOut, ACh>)>
{
let arm = f(DagArmSeed(PhantomData));
let (a0,) = self.arms;
$Fork {
chain: self.chain,
arms: (a0, arm),
_marker: PhantomData,
}
}
}
impl<$U, ForkOut, Chain, A0, C0, A1, C1>
$Fork<$U, ForkOut, Chain, (DagArm<ForkOut, A0, C0>, DagArm<ForkOut, A1, C1>)>
{
pub fn arm<AOut, ACh>(
self,
f: impl FnOnce(DagArmSeed<ForkOut>) -> DagArm<ForkOut, AOut, ACh>,
) -> $Fork<
$U,
ForkOut,
Chain,
(
DagArm<ForkOut, A0, C0>,
DagArm<ForkOut, A1, C1>,
DagArm<ForkOut, AOut, ACh>,
),
> {
let arm = f(DagArmSeed(PhantomData));
let (a0, a1) = self.arms;
$Fork {
chain: self.chain,
arms: (a0, a1, arm),
_marker: PhantomData,
}
}
}
impl<$U, ForkOut, Chain, A0, C0, A1, C1, A2, C2>
$Fork<
$U,
ForkOut,
Chain,
(
DagArm<ForkOut, A0, C0>,
DagArm<ForkOut, A1, C1>,
DagArm<ForkOut, A2, C2>,
),
>
{
pub fn arm<AOut, ACh>(
self,
f: impl FnOnce(DagArmSeed<ForkOut>) -> DagArm<ForkOut, AOut, ACh>,
) -> $Fork<
$U,
ForkOut,
Chain,
(
DagArm<ForkOut, A0, C0>,
DagArm<ForkOut, A1, C1>,
DagArm<ForkOut, A2, C2>,
DagArm<ForkOut, AOut, ACh>,
),
> {
let arm = f(DagArmSeed(PhantomData));
let (a0, a1, a2) = self.arms;
$Fork {
chain: self.chain,
arms: (a0, a1, a2, arm),
_marker: PhantomData,
}
}
}
impl<$U, ForkOut: 'static, Chain, A0: 'static, C0, A1: 'static, C1>
$Fork<$U, ForkOut, Chain, (DagArm<ForkOut, A0, C0>, DagArm<ForkOut, A1, C1>)>
{
pub fn merge<MOut, Params, S>(
self,
f: S,
registry: &Registry,
) -> $Output<
$U,
MOut,
MergeNode2<Chain, C0, C1, S::Step, ForkOut, A0, A1, MOut>,
>
where
MOut: 'static,
S: IntoMergeStep<(&'static A0, &'static A1), MOut, Params>,
S::Step: for<'x> MergeStepCall<(&'x A0, &'x A1), MOut>,
{
let (a0, a1) = self.arms;
$Output {
chain: MergeNode2 {
chain: self.chain,
arm0: a0.chain,
arm1: a1.chain,
merge: f.into_merge_step(registry),
_marker: PhantomData,
},
_marker: PhantomData,
}
}
}
impl<$U, ForkOut: 'static, Chain, C0, C1>
$Fork<$U, ForkOut, Chain, (DagArm<ForkOut, (), C0>, DagArm<ForkOut, (), C1>)>
{
pub fn join(
self,
) -> $Output<$U, (), JoinNode2<Chain, C0, C1, ForkOut>> {
let (a0, a1) = self.arms;
$Output {
chain: JoinNode2 {
chain: self.chain,
arm0: a0.chain,
arm1: a1.chain,
_marker: PhantomData,
},
_marker: PhantomData,
}
}
}
impl<$U, ForkOut: 'static, Chain, A0: 'static, C0, A1: 'static, C1, A2: 'static, C2>
$Fork<
$U,
ForkOut,
Chain,
(
DagArm<ForkOut, A0, C0>,
DagArm<ForkOut, A1, C1>,
DagArm<ForkOut, A2, C2>,
),
>
{
pub fn merge<MOut, Params, S>(
self,
f: S,
registry: &Registry,
) -> $Output<
$U,
MOut,
MergeNode3<Chain, C0, C1, C2, S::Step, ForkOut, A0, A1, A2, MOut>,
>
where
MOut: 'static,
S: IntoMergeStep<(&'static A0, &'static A1, &'static A2), MOut, Params>,
S::Step: for<'x> MergeStepCall<(&'x A0, &'x A1, &'x A2), MOut>,
{
let (a0, a1, a2) = self.arms;
$Output {
chain: MergeNode3 {
chain: self.chain,
arm0: a0.chain,
arm1: a1.chain,
arm2: a2.chain,
merge: f.into_merge_step(registry),
_marker: PhantomData,
},
_marker: PhantomData,
}
}
}
impl<$U, ForkOut: 'static, Chain, C0, C1, C2>
$Fork<
$U,
ForkOut,
Chain,
(
DagArm<ForkOut, (), C0>,
DagArm<ForkOut, (), C1>,
DagArm<ForkOut, (), C2>,
),
>
{
pub fn join(
self,
) -> $Output<$U, (), JoinNode3<Chain, C0, C1, C2, ForkOut>> {
let (a0, a1, a2) = self.arms;
$Output {
chain: JoinNode3 {
chain: self.chain,
arm0: a0.chain,
arm1: a1.chain,
arm2: a2.chain,
_marker: PhantomData,
},
_marker: PhantomData,
}
}
}
#[allow(clippy::many_single_char_names)]
impl<
$U,
ForkOut: 'static,
Chain,
A0: 'static,
C0,
A1: 'static,
C1,
A2: 'static,
C2,
A3: 'static,
C3,
>
$Fork<
$U,
ForkOut,
Chain,
(
DagArm<ForkOut, A0, C0>,
DagArm<ForkOut, A1, C1>,
DagArm<ForkOut, A2, C2>,
DagArm<ForkOut, A3, C3>,
),
>
{
pub fn merge<MOut, Params, S>(
self,
f: S,
registry: &Registry,
) -> $Output<
$U,
MOut,
MergeNode4<Chain, C0, C1, C2, C3, S::Step, ForkOut, A0, A1, A2, A3, MOut>,
>
where
MOut: 'static,
S: IntoMergeStep<
(&'static A0, &'static A1, &'static A2, &'static A3),
MOut,
Params,
>,
S::Step: for<'x> MergeStepCall<(&'x A0, &'x A1, &'x A2, &'x A3), MOut>,
{
let (a0, a1, a2, a3) = self.arms;
$Output {
chain: MergeNode4 {
chain: self.chain,
arm0: a0.chain,
arm1: a1.chain,
arm2: a2.chain,
arm3: a3.chain,
merge: f.into_merge_step(registry),
_marker: PhantomData,
},
_marker: PhantomData,
}
}
}
impl<$U, ForkOut: 'static, Chain, C0, C1, C2, C3>
$Fork<
$U,
ForkOut,
Chain,
(
DagArm<ForkOut, (), C0>,
DagArm<ForkOut, (), C1>,
DagArm<ForkOut, (), C2>,
DagArm<ForkOut, (), C3>,
),
>
{
pub fn join(
self,
) -> $Output<$U, (), JoinNode4<Chain, C0, C1, C2, C3, ForkOut>> {
let (a0, a1, a2, a3) = self.arms;
$Output {
chain: JoinNode4 {
chain: self.chain,
arm0: a0.chain,
arm1: a1.chain,
arm2: a2.chain,
arm3: a3.chain,
_marker: PhantomData,
},
_marker: PhantomData,
}
}
}
};
}
impl_dag_fork!(fork: DagChainFork, output: DagChain, upstream: E);
impl_dag_fork!(fork: DagArmFork, output: DagArm, upstream: In);
impl<E, Out: crate::PipelineOutput, Chain: ChainCall<E, Out = Out>> DagChain<E, Out, Chain> {
#[must_use = "building a DAG without storing it does nothing"]
pub fn build_batch(self, capacity: usize) -> BatchDag<E, Chain> {
BatchDag {
input: Vec::with_capacity(capacity),
chain: self.chain,
}
}
}
pub struct BatchDag<E, F> {
input: Vec<E>,
chain: F,
}
impl<E, Out: crate::PipelineOutput, F: ChainCall<E, Out = Out>> BatchDag<E, F> {
pub fn input_mut(&mut self) -> &mut Vec<E> {
&mut self.input
}
pub fn input(&self) -> &[E] {
&self.input
}
pub fn run(&mut self, world: &mut World) {
for item in self.input.drain(..) {
let _ = self.chain.call(world, item);
}
}
}
pub fn resolve_arm<In, Out, Params, S>(
f: S,
registry: &Registry,
) -> impl FnMut(&mut World, &In) -> Out + use<In, Out, Params, S>
where
In: 'static,
Out: 'static,
S: IntoStep<&'static In, Out, Params>,
S::Step: for<'a> StepCall<&'a In, Out = Out>,
{
let mut resolved = f.into_step(registry);
move |world: &mut World, input: &In| resolved.call(world, input)
}
#[cfg(test)]
#[allow(
clippy::ref_option,
clippy::unnecessary_wraps,
clippy::needless_pass_by_value,
clippy::trivially_copy_pass_by_ref,
clippy::ptr_arg
)]
mod tests {
use super::*;
use crate::{IntoHandler, Res, ResMut, Virtual, WorldBuilder};
#[test]
fn dag_linear_2() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn root_mul2(x: u32) -> u64 {
x as u64 * 2
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut dag = DagBuilder::<u32>::new()
.root(root_mul2, reg)
.then(store, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 10);
}
#[test]
fn dag_linear_3() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn root_mul2(x: u32) -> u64 {
x as u64 * 2
}
fn add_one(val: &u64) -> u64 {
*val + 1
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut dag = DagBuilder::<u32>::new()
.root(root_mul2, reg)
.then(add_one, reg)
.then(store, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 11); }
#[test]
fn dag_linear_5() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn root_id(x: u32) -> u64 {
x as u64
}
fn add_one(val: &u64) -> u64 {
*val + 1
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut dag = DagBuilder::<u32>::new()
.root(root_id, reg)
.then(add_one, reg)
.then(add_one, reg)
.then(add_one, reg)
.then(store, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 3); }
#[test]
fn dag_diamond() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn root_mul2(x: u32) -> u32 {
x.wrapping_mul(2)
}
fn add_one(val: &u32) -> u32 {
val.wrapping_add(1)
}
fn mul3(val: &u32) -> u32 {
val.wrapping_mul(3)
}
fn merge_add(a: &u32, b: &u32) -> u32 {
a.wrapping_add(*b)
}
fn store(mut out: ResMut<u64>, val: &u32) {
*out = *val as u64;
}
let mut dag = DagBuilder::<u32>::new()
.root(root_mul2, reg)
.fork()
.arm(|a| a.then(add_one, reg))
.arm(|b| b.then(mul3, reg))
.merge(merge_add, reg)
.then(store, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 41);
}
#[test]
fn dag_fan_out_join() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
wb.register::<i64>(0);
let mut world = wb.build();
let reg = world.registry();
fn root_id(x: u32) -> u64 {
x as u64
}
fn sink_u64(mut out: ResMut<u64>, val: &u64) {
*out = *val * 2;
}
fn sink_i64(mut out: ResMut<i64>, val: &u64) {
*out = *val as i64 * 3;
}
let mut dag = DagBuilder::<u32>::new()
.root(root_id, reg)
.fork()
.arm(|a| a.then(sink_u64, reg))
.arm(|b| b.then(sink_i64, reg))
.join()
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 10);
assert_eq!(*world.resource::<i64>(), 15);
}
#[test]
fn dag_nested_fork() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn root_id(x: u32) -> u32 {
x
}
fn add_10(val: &u32) -> u32 {
val.wrapping_add(10)
}
fn mul2(val: &u32) -> u32 {
val.wrapping_mul(2)
}
fn mul3(val: &u32) -> u32 {
val.wrapping_mul(3)
}
fn inner_merge(a: &u32, b: &u32) -> u32 {
a.wrapping_add(*b)
}
fn outer_merge(a: &u32, b: &u32) -> u32 {
a.wrapping_add(*b)
}
fn store(mut out: ResMut<u64>, val: &u32) {
*out = *val as u64;
}
let mut dag = DagBuilder::<u32>::new()
.root(root_id, reg)
.fork()
.arm(|a| {
a.then(add_10, reg)
.fork()
.arm(|c| c.then(mul2, reg))
.arm(|d| d.then(mul3, reg))
.merge(inner_merge, reg)
})
.arm(|b| b.then(mul3, reg))
.merge(outer_merge, reg)
.then(store, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 90);
}
#[test]
fn dag_complex_topology() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn root_mul2(x: u32) -> u32 {
x.wrapping_mul(2)
}
fn add_one(val: &u32) -> u32 {
val.wrapping_add(1)
}
fn add_then_mul2(val: &u32) -> u32 {
val.wrapping_add(1).wrapping_mul(2)
}
fn mul3(val: &u32) -> u32 {
val.wrapping_mul(3)
}
fn merge_add(a: &u32, b: &u32) -> u32 {
a.wrapping_add(*b)
}
fn store(mut out: ResMut<u64>, val: &u32) {
*out = *val as u64;
}
let mut dag = DagBuilder::<u32>::new()
.root(root_mul2, reg)
.fork()
.arm(|a| a.then(add_one, reg).then(add_then_mul2, reg))
.arm(|b| b.then(mul3, reg))
.merge(merge_add, reg)
.then(store, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 54);
}
#[test]
fn dag_boxable() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn root_id(x: u32) -> u64 {
x as u64
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut boxed: Virtual<u32> = Box::new(
DagBuilder::<u32>::new()
.root(root_id, reg)
.then(store, reg)
.build(),
);
boxed.run(&mut world, 77u32);
assert_eq!(*world.resource::<u64>(), 77);
}
#[test]
fn dag_world_access() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(10); wb.register::<String>(String::new());
let mut world = wb.build();
let reg = world.registry();
fn scale(factor: Res<u64>, val: &u32) -> u64 {
*factor * (*val as u64)
}
fn store(mut out: ResMut<String>, val: &u64) {
*out = val.to_string();
}
let mut dag = DagBuilder::<u32>::new()
.root(|x: u32| x, reg)
.then(scale, reg)
.then(store, reg)
.build();
dag.run(&mut world, 7u32);
assert_eq!(world.resource::<String>().as_str(), "70");
}
#[test]
fn dag_root_only() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(
|mut out: ResMut<u64>, x: u32| {
*out = x as u64;
},
reg,
)
.build();
dag.run(&mut world, 42u32);
assert_eq!(*world.resource::<u64>(), 42);
}
#[test]
fn dag_multiple_dispatches() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn root_id(x: u32) -> u64 {
x as u64
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut dag = DagBuilder::<u32>::new()
.root(root_id, reg)
.then(store, reg)
.build();
dag.run(&mut world, 1u32);
assert_eq!(*world.resource::<u64>(), 1);
dag.run(&mut world, 2u32);
assert_eq!(*world.resource::<u64>(), 2);
dag.run(&mut world, 3u32);
assert_eq!(*world.resource::<u64>(), 3);
}
#[test]
fn dag_3way_merge() {
let mut wb = WorldBuilder::new();
wb.register::<String>(String::new());
let mut world = wb.build();
let reg = world.registry();
fn root_id(x: u32) -> u64 {
x as u64
}
fn mul1(val: &u64) -> u64 {
*val
}
fn mul2(val: &u64) -> u64 {
*val * 2
}
fn mul3(val: &u64) -> u64 {
*val * 3
}
fn merge3_fmt(mut out: ResMut<String>, a: &u64, b: &u64, c: &u64) {
*out = format!("{},{},{}", a, b, c);
}
let mut dag = DagBuilder::<u32>::new()
.root(root_id, reg)
.fork()
.arm(|a| a.then(mul1, reg))
.arm(|b| b.then(mul2, reg))
.arm(|c| c.then(mul3, reg))
.merge(merge3_fmt, reg)
.build();
dag.run(&mut world, 10u32);
assert_eq!(world.resource::<String>().as_str(), "10,20,30");
}
#[test]
fn dag_dispatch() {
fn root(x: u32) -> u64 {
x as u64 + 42
}
fn sink(mut out: ResMut<u64>, event: u64) {
*out = event;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.dispatch(sink.into_handler(reg))
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 42);
}
#[test]
fn dag_option_map() {
fn root(_x: u32) -> Option<u64> {
Some(10)
}
fn double(val: &u64) -> u64 {
*val * 2
}
fn sink(mut out: ResMut<u64>, val: &Option<u64>) {
*out = val.unwrap_or(0);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.map(double, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 20);
}
#[test]
fn dag_option_map_none() {
fn root(_x: u32) -> Option<u64> {
None
}
fn double(val: &u64) -> u64 {
*val * 2
}
fn sink(mut out: ResMut<u64>, val: &Option<u64>) {
*out = val.unwrap_or(999);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.map(double, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 999);
}
#[test]
fn dag_option_and_then() {
fn root(_x: u32) -> Option<u64> {
Some(5)
}
fn check(val: &u64) -> Option<u64> {
if *val > 3 { Some(*val * 10) } else { None }
}
fn sink(mut out: ResMut<u64>, val: &Option<u64>) {
*out = val.unwrap_or(0);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.and_then(check, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 50);
}
#[test]
fn dag_option_filter_keeps() {
fn root(_x: u32) -> Option<u64> {
Some(5)
}
fn sink(mut out: ResMut<u64>, val: &Option<u64>) {
*out = val.unwrap_or(0);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let mut dag = DagBuilder::<u32>::new()
.root(root, world.registry())
.filter(|v: &u64| *v > 3, world.registry())
.then(sink, world.registry())
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 5);
}
#[test]
fn dag_option_filter_drops() {
fn root(_x: u32) -> Option<u64> {
Some(5)
}
fn sink(mut out: ResMut<u64>, val: &Option<u64>) {
*out = val.unwrap_or(0);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let mut dag = DagBuilder::<u32>::new()
.root(root, world.registry())
.filter(|v: &u64| *v > 10, world.registry())
.then(sink, world.registry())
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 0);
}
#[test]
fn dag_option_on_none() {
fn root(_x: u32) -> Option<u64> {
None
}
fn sink(_val: &Option<u64>) {}
let mut wb = WorldBuilder::new();
wb.register::<bool>(false);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.on_none(
|w: &mut World| {
*w.resource_mut::<bool>() = true;
},
reg,
)
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert!(*world.resource::<bool>());
}
#[test]
fn dag_option_unwrap_or() {
fn root(_x: u32) -> Option<u64> {
None
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.unwrap_or(42u64)
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 42);
}
#[test]
fn dag_option_ok_or() {
fn root(_x: u32) -> Option<u64> {
None
}
fn sink(mut out: ResMut<u64>, val: &Result<u64, &str>) {
*out = val.as_ref().map_or(999, |v| *v);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.ok_or("missing")
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 999);
}
#[test]
fn dag_result_map() {
fn root(_x: u32) -> Result<u64, &'static str> {
Ok(10)
}
fn double(val: &u64) -> u64 {
*val * 2
}
fn sink(mut out: ResMut<u64>, val: &Result<u64, &str>) {
*out = val.as_ref().copied().unwrap_or(0);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.map(double, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 20);
}
#[test]
fn dag_result_and_then() {
fn root(_x: u32) -> Result<u64, &'static str> {
Ok(5)
}
fn check(val: &u64) -> Result<u64, &'static str> {
if *val > 3 {
Ok(*val * 10)
} else {
Err("too small")
}
}
fn sink(mut out: ResMut<u64>, val: &Result<u64, &str>) {
*out = val.as_ref().copied().unwrap_or(0);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.and_then(check, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 50);
}
#[test]
fn dag_result_catch() {
fn root(_x: u32) -> Result<u64, String> {
Err("oops".into())
}
fn handle_err(mut log: ResMut<String>, err: &String) {
*log = err.clone();
}
fn sink(mut out: ResMut<u64>, val: &Option<u64>) {
*out = val.unwrap_or(0);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
wb.register::<String>(String::new());
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.catch(handle_err, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 0);
assert_eq!(world.resource::<String>().as_str(), "oops");
}
#[test]
fn dag_result_ok() {
fn root(_x: u32) -> Result<u64, &'static str> {
Err("fail")
}
fn sink(mut out: ResMut<u64>, val: &Option<u64>) {
*out = val.unwrap_or(0);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.ok()
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 0);
}
#[test]
fn dag_result_unwrap_or_else() {
fn root(_x: u32) -> Result<u64, &'static str> {
Err("fail")
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.unwrap_or_else(|_err: &str| 42u64, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 42);
}
#[test]
fn dag_result_map_err() {
fn root(_x: u32) -> Result<u64, u32> {
Err(5)
}
fn sink(mut out: ResMut<u64>, val: &Result<u64, String>) {
*out = match val {
Ok(v) => *v,
Err(e) => e.len() as u64,
};
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.map_err(|e: u32| format!("err:{e}"), reg)
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 5);
}
#[test]
fn dag_arm_combinators() {
fn root(x: u32) -> u64 {
x as u64 + 10
}
fn arm_step(val: &u64) -> Option<u64> {
if *val > 5 { Some(*val * 3) } else { None }
}
fn double(val: &u64) -> u64 {
*val * 2
}
fn merge_fn(a: &u64, b: &u64) -> String {
format!("{a},{b}")
}
fn sink(mut out: ResMut<String>, val: &String) {
*out = val.clone();
}
let mut wb = WorldBuilder::new();
wb.register::<String>(String::new());
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.fork()
.arm(|a| a.then(arm_step, reg).unwrap_or(0u64))
.arm(|b| b.then(double, reg))
.merge(merge_fn, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(world.resource::<String>().as_str(), "30,20");
}
#[test]
fn dag_option_inspect() {
fn root(_x: u32) -> Option<u64> {
Some(42)
}
fn sink(mut out: ResMut<u64>, val: &Option<u64>) {
*out = val.unwrap_or(0);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
wb.register::<bool>(false);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.inspect(
|w: &mut World, _val: &u64| {
*w.resource_mut::<bool>() = true;
},
reg,
)
.then(sink, reg)
.build();
dag.run(&mut world, 0u32);
assert_eq!(*world.resource::<u64>(), 42);
assert!(*world.resource::<bool>());
}
#[test]
fn dag_guard_keeps() {
fn root(x: u32) -> u64 {
x as u64
}
fn sink(mut out: ResMut<u64>, val: &Option<u64>) {
*out = val.unwrap_or(0);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.guard(|v: &u64| *v > 3, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 5);
}
#[test]
fn dag_guard_drops() {
fn root(x: u32) -> u64 {
x as u64
}
fn sink(mut out: ResMut<u64>, val: &Option<u64>) {
*out = val.unwrap_or(999);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.guard(|v: &u64| *v > 10, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 999);
}
#[test]
fn dag_arm_guard() {
fn root(x: u32) -> u64 {
x as u64
}
fn double(val: &u64) -> u64 {
*val * 2
}
fn merge_fn(a: &Option<u64>, b: &u64) -> String {
format!("{:?},{}", a, b)
}
fn sink(mut out: ResMut<String>, val: &String) {
*out = val.clone();
}
let mut wb = WorldBuilder::new();
wb.register::<String>(String::new());
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.fork()
.arm(|a| a.then(double, reg).guard(|v: &u64| *v > 100, reg))
.arm(|b| b.then(double, reg))
.merge(merge_fn, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(world.resource::<String>().as_str(), "None,10");
}
#[test]
fn dag_tap_observes_without_changing() {
fn root(x: u32) -> u64 {
x as u64 * 2
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
wb.register::<bool>(false);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.tap(
|w: &mut World, val: &u64| {
*w.resource_mut::<bool>() = *val == 10;
},
reg,
)
.then(sink, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 10); assert!(*world.resource::<bool>()); }
#[test]
fn dag_arm_tap() {
fn root(x: u32) -> u64 {
x as u64
}
fn double(val: &u64) -> u64 {
*val * 2
}
fn merge_add(a: &u64, b: &u64) -> u64 {
*a + *b
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
wb.register::<bool>(false);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.fork()
.arm(|a| {
a.then(double, reg).tap(
|w: &mut World, _v: &u64| {
*w.resource_mut::<bool>() = true;
},
reg,
)
})
.arm(|b| b.then(double, reg))
.merge(merge_add, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 20);
assert!(*world.resource::<bool>()); }
#[test]
fn dag_route_true_arm() {
fn root(x: u32) -> u64 {
x as u64
}
fn double(val: &u64) -> u64 {
*val * 2
}
fn triple(val: &u64) -> u64 {
*val * 3
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let arm_t = DagArmSeed::new().then(double, reg);
let arm_f = DagArmSeed::new().then(triple, reg);
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.route(|v: &u64| *v > 3, reg, arm_t, arm_f)
.then(sink, reg)
.build();
dag.run(&mut world, 5u32); assert_eq!(*world.resource::<u64>(), 10);
}
#[test]
fn dag_route_false_arm() {
fn root(x: u32) -> u64 {
x as u64
}
fn double(val: &u64) -> u64 {
*val * 2
}
fn triple(val: &u64) -> u64 {
*val * 3
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let arm_t = DagArmSeed::new().then(double, reg);
let arm_f = DagArmSeed::new().then(triple, reg);
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.route(|v: &u64| *v > 10, reg, arm_t, arm_f)
.then(sink, reg)
.build();
dag.run(&mut world, 5u32); assert_eq!(*world.resource::<u64>(), 15);
}
#[test]
fn dag_route_nested() {
fn root(x: u32) -> u64 {
x as u64
}
fn pass(val: &u64) -> u64 {
*val
}
fn add_100(val: &u64) -> u64 {
*val + 100
}
fn add_200(val: &u64) -> u64 {
*val + 200
}
fn add_300(val: &u64) -> u64 {
*val + 300
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let inner_t = DagArmSeed::new().then(add_200, reg);
let inner_f = DagArmSeed::new().then(add_300, reg);
let outer_t = DagArmSeed::new().then(add_100, reg);
let outer_f =
DagArmSeed::new()
.then(pass, reg)
.route(|v: &u64| *v < 10, reg, inner_t, inner_f);
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.route(|v: &u64| *v < 5, reg, outer_t, outer_f)
.then(sink, reg)
.build();
dag.run(&mut world, 3u32); assert_eq!(*world.resource::<u64>(), 103);
dag.run(&mut world, 7u32); assert_eq!(*world.resource::<u64>(), 207);
dag.run(&mut world, 15u32); assert_eq!(*world.resource::<u64>(), 315);
}
#[test]
fn dag_tee_side_effect_chain() {
fn root(x: u32) -> u64 {
x as u64 * 2
}
fn log_step(mut counter: ResMut<u32>, _val: &u64) {
*counter += 1;
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
wb.register::<u32>(0);
let mut world = wb.build();
let reg = world.registry();
let side = DagArmSeed::new().then(log_step, reg);
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.tee(side)
.then(sink, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 10); assert_eq!(*world.resource::<u32>(), 1);
dag.run(&mut world, 7u32);
assert_eq!(*world.resource::<u64>(), 14);
assert_eq!(*world.resource::<u32>(), 2); }
#[test]
fn dag_dedup_suppresses_unchanged() {
fn root(x: u32) -> u64 {
x as u64 / 2 }
fn sink(mut out: ResMut<u32>, val: &Option<u64>) {
if val.is_some() {
*out += 1;
}
}
let mut wb = WorldBuilder::new();
wb.register::<u32>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.dedup()
.then(sink, reg)
.build();
dag.run(&mut world, 4u32); assert_eq!(*world.resource::<u32>(), 1);
dag.run(&mut world, 5u32); assert_eq!(*world.resource::<u32>(), 1);
dag.run(&mut world, 6u32); assert_eq!(*world.resource::<u32>(), 2);
}
#[test]
fn dag_not() {
fn root(x: u32) -> bool {
x > 5
}
fn sink(mut out: ResMut<bool>, val: &bool) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<bool>(false);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.not()
.then(sink, reg)
.build();
dag.run(&mut world, 3u32); assert!(*world.resource::<bool>());
dag.run(&mut world, 10u32); assert!(!*world.resource::<bool>());
}
#[test]
fn dag_and() {
fn root(x: u32) -> bool {
x > 5
}
fn sink(mut out: ResMut<bool>, val: &bool) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<bool>(true); let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.and(|w: &mut World| *w.resource::<bool>(), reg)
.then(sink, reg)
.build();
dag.run(&mut world, 10u32); assert!(*world.resource::<bool>());
*world.resource_mut::<bool>() = false; dag.run(&mut world, 10u32); assert!(!*world.resource::<bool>());
}
#[test]
fn dag_or() {
fn root(x: u32) -> bool {
x > 5
}
fn sink(mut out: ResMut<bool>, val: &bool) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<bool>(false);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.or(|w: &mut World| *w.resource::<bool>(), reg)
.then(sink, reg)
.build();
dag.run(&mut world, 3u32); assert!(!*world.resource::<bool>());
*world.resource_mut::<bool>() = true;
dag.run(&mut world, 3u32); assert!(*world.resource::<bool>());
}
#[test]
fn dag_xor() {
fn root(x: u32) -> bool {
x > 5
}
fn sink(mut out: ResMut<bool>, val: &bool) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<bool>(true);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.xor(|w: &mut World| *w.resource::<bool>(), reg)
.then(sink, reg)
.build();
dag.run(&mut world, 10u32); assert!(!*world.resource::<bool>());
}
#[test]
fn dag_splat2_on_chain() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn split(x: u32) -> (u32, u32) {
(x, x * 2)
}
fn store(mut out: ResMut<u64>, a: &u32, b: &u32) {
*out = *a as u64 + *b as u64;
}
let mut dag = DagBuilder::<u32>::new()
.root(split, reg)
.splat()
.then(store, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 15); }
#[test]
fn dag_splat3_on_chain() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn split3(x: u32) -> (u32, u32, u32) {
(x, x + 1, x + 2)
}
fn sum3(a: &u32, b: &u32, c: &u32) -> u64 {
*a as u64 + *b as u64 + *c as u64
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut dag = DagBuilder::<u32>::new()
.root(split3, reg)
.splat()
.then(sum3, reg)
.then(store, reg)
.build();
dag.run(&mut world, 10u32);
assert_eq!(*world.resource::<u64>(), 33); }
#[test]
fn dag_splat2_with_param() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(100);
let mut world = wb.build();
let reg = world.registry();
fn split(x: u32) -> (u32, u32) {
(x, x * 3)
}
fn add_base(base: Res<u64>, a: &u32, b: &u32) -> u64 {
*base + *a as u64 + *b as u64
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut dag = DagBuilder::<u32>::new()
.root(split, reg)
.splat()
.then(add_base, reg)
.then(store, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 120); }
#[test]
fn dag_splat_on_arm_start() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn split(x: u32) -> (u32, u32) {
(x, x + 10)
}
fn sum2(a: &u32, b: &u32) -> u64 {
*a as u64 + *b as u64
}
fn identity(x: &(u32, u32)) -> u64 {
x.0 as u64 * x.1 as u64
}
fn merge_add(a: &u64, b: &u64) -> u64 {
*a + *b
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut dag = DagBuilder::<u32>::new()
.root(split, reg)
.fork()
.arm(|a| a.splat().then(sum2, reg))
.arm(|b| b.then(identity, reg))
.merge(merge_add, reg)
.then(store, reg)
.build();
dag.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 95);
}
#[test]
fn dag_splat_on_arm() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn root_id(x: u32) -> u32 {
x
}
fn make_pair(val: &u32) -> (u32, u32) {
(*val, *val + 100)
}
fn sum2(a: &u32, b: &u32) -> u64 {
*a as u64 + *b as u64
}
fn double(val: &u32) -> u64 {
*val as u64 * 2
}
fn merge_add(a: &u64, b: &u64) -> u64 {
*a + *b
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut dag = DagBuilder::<u32>::new()
.root(root_id, reg)
.fork()
.arm(|a| a.then(make_pair, reg).splat().then(sum2, reg))
.arm(|b| b.then(double, reg))
.merge(merge_add, reg)
.then(store, reg)
.build();
dag.run(&mut world, 7u32);
assert_eq!(*world.resource::<u64>(), 128);
}
#[test]
fn dag_splat4_on_chain() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn split4(x: u32) -> (u32, u32, u32, u32) {
(x, x + 1, x + 2, x + 3)
}
fn sum4(a: &u32, b: &u32, c: &u32, d: &u32) -> u64 {
(*a + *b + *c + *d) as u64
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut dag = DagBuilder::<u32>::new()
.root(split4, reg)
.splat()
.then(sum4, reg)
.then(store, reg)
.build();
dag.run(&mut world, 10u32);
assert_eq!(*world.resource::<u64>(), 46); }
#[test]
fn dag_splat5_on_chain() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn split5(x: u32) -> (u8, u8, u8, u8, u8) {
let x = x as u8;
(x, x + 1, x + 2, x + 3, x + 4)
}
#[allow(clippy::many_single_char_names)]
fn sum5(a: &u8, b: &u8, c: &u8, d: &u8, e: &u8) -> u64 {
(*a as u64) + (*b as u64) + (*c as u64) + (*d as u64) + (*e as u64)
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut dag = DagBuilder::<u32>::new()
.root(split5, reg)
.splat()
.then(sum5, reg)
.then(store, reg)
.build();
dag.run(&mut world, 1u32);
assert_eq!(*world.resource::<u64>(), 15); }
#[test]
fn dag_splat_boxable() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn split(x: u32) -> (u32, u32) {
(x, x * 2)
}
fn store(mut out: ResMut<u64>, a: &u32, b: &u32) {
*out = *a as u64 + *b as u64;
}
let dag = DagBuilder::<u32>::new()
.root(split, reg)
.splat()
.then(store, reg)
.build();
let mut boxed: Virtual<u32> = Box::new(dag);
boxed.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 15);
}
#[test]
fn batch_dag_basic() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn double(x: u32) -> u64 {
x as u64 * 2
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out += *val;
}
let mut batch = DagBuilder::<u32>::new()
.root(double, reg)
.then(store, reg)
.build_batch(8);
batch.input_mut().extend([1, 2, 3]);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 12); assert!(batch.input().is_empty());
}
#[test]
fn batch_dag_option_terminal() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn double(x: u32) -> u64 {
x as u64 * 2
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out += *val;
}
let mut batch = DagBuilder::<u32>::new()
.root(double, reg)
.guard(|val: &u64| *val > 5, reg)
.map(store, reg)
.unwrap_or(())
.build_batch(8);
batch.input_mut().extend([1, 2, 3, 4, 5]);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 24); }
#[test]
fn batch_dag_buffer_reuse() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn double(x: u32) -> u64 {
x as u64 * 2
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out += *val;
}
let mut batch = DagBuilder::<u32>::new()
.root(double, reg)
.then(store, reg)
.build_batch(8);
batch.input_mut().extend([1, 2]);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 6); assert!(batch.input().is_empty());
batch.input_mut().extend([10, 20]);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 66); }
#[test]
fn batch_dag_retains_allocation() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
fn noop(_x: u32) {}
let mut batch = DagBuilder::<u32>::new().root(noop, reg).build_batch(64);
batch.input_mut().extend([1, 2, 3]);
batch.run(&mut world);
assert!(batch.input().is_empty());
assert!(batch.input_mut().capacity() >= 64);
}
#[test]
fn batch_dag_empty_is_noop() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn double(x: u32) -> u64 {
x as u64 * 2
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out += *val;
}
let mut batch = DagBuilder::<u32>::new()
.root(double, reg)
.then(store, reg)
.build_batch(8);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 0);
}
#[test]
fn batch_dag_with_splat() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn split(x: u32) -> (u64, u64) {
(x as u64, x as u64 * 10)
}
fn combine(a: &u64, b: &u64) -> u64 {
*a + *b
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out += *val;
}
let mut batch = DagBuilder::<u32>::new()
.root(split, reg)
.splat()
.then(combine, reg)
.then(store, reg)
.build_batch(4);
batch.input_mut().extend([1, 2]);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 33); }
#[test]
fn dag_then_conditional_basic() {
fn root(x: u32) -> u64 {
x as u64
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.then(|val: &u64| if *val > 5 { *val * 10 } else { *val + 1 }, reg)
.then(sink, reg)
.build();
dag.run(&mut world, 10u32); assert_eq!(*world.resource::<u64>(), 100);
dag.run(&mut world, 3u32); assert_eq!(*world.resource::<u64>(), 4);
}
#[test]
fn dag_then_conditional_3_way() {
fn root(x: u32) -> u32 {
x
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.then(
|val: &u32| match *val % 3 {
0 => *val as u64 + 100,
1 => *val as u64 + 200,
_ => *val as u64 + 300,
},
reg,
)
.then(sink, reg)
.build();
dag.run(&mut world, 6u32); assert_eq!(*world.resource::<u64>(), 106);
dag.run(&mut world, 7u32); assert_eq!(*world.resource::<u64>(), 207);
dag.run(&mut world, 8u32); assert_eq!(*world.resource::<u64>(), 308);
}
#[test]
fn dag_then_with_resolve_arm() {
fn root(x: u32) -> u32 {
x
}
fn double(val: &u32) -> u64 {
*val as u64 * 2
}
fn triple(val: &u32) -> u64 {
*val as u64 * 3
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut arm_even = resolve_arm(double, reg);
let mut arm_odd = resolve_arm(triple, reg);
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.then(
move |world: &mut World, val: &u32| {
if *val % 2 == 0 {
arm_even(world, val)
} else {
arm_odd(world, val)
}
},
reg,
)
.then(sink, reg)
.build();
dag.run(&mut world, 4u32); assert_eq!(*world.resource::<u64>(), 8);
dag.run(&mut world, 5u32); assert_eq!(*world.resource::<u64>(), 15);
}
#[test]
fn dag_resolve_arm_with_params() {
fn root(x: u32) -> u32 {
x
}
fn add_offset(offset: Res<i64>, val: &u32) -> u64 {
(*offset + *val as i64) as u64
}
fn plain_double(val: &u32) -> u64 {
*val as u64 * 2
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
wb.register::<i64>(100);
let mut world = wb.build();
let reg = world.registry();
let mut arm_offset = resolve_arm(add_offset, reg);
let mut arm_double = resolve_arm(plain_double, reg);
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.then(
move |world: &mut World, val: &u32| {
if *val > 10 {
arm_offset(world, val)
} else {
arm_double(world, val)
}
},
reg,
)
.then(sink, reg)
.build();
dag.run(&mut world, 20u32); assert_eq!(*world.resource::<u64>(), 120);
dag.run(&mut world, 5u32); assert_eq!(*world.resource::<u64>(), 10);
}
#[test]
fn dag_then_conditional_in_fork_arm() {
fn root(x: u32) -> u32 {
x
}
fn pass(val: &u32) -> u32 {
*val
}
fn sink_u64(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
fn sink_i64(mut out: ResMut<i64>, val: &u32) {
*out = -(*val as i64);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
wb.register::<i64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut dag = DagBuilder::<u32>::new()
.root(root, reg)
.fork()
.arm(|a| {
a.then(pass, reg)
.then(
|val: &u32| {
if *val > 5 {
*val as u64 * 10
} else {
*val as u64
}
},
reg,
)
.then(sink_u64, reg)
})
.arm(|a| a.then(sink_i64, reg))
.join()
.build();
dag.run(&mut world, 10u32); assert_eq!(*world.resource::<u64>(), 100);
assert_eq!(*world.resource::<i64>(), -10);
dag.run(&mut world, 3u32); assert_eq!(*world.resource::<u64>(), 3);
assert_eq!(*world.resource::<i64>(), -3);
}
#[test]
fn batch_dag_then_conditional() {
fn root(x: u32) -> u32 {
x
}
fn sink(mut out: ResMut<u64>, val: &u64) {
*out += *val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut batch = DagBuilder::<u32>::new()
.root(root, reg)
.then(
|val: &u32| {
if *val % 2 == 0 {
*val as u64 * 10
} else {
*val as u64
}
},
reg,
)
.then(sink, reg)
.build_batch(8);
batch.input_mut().extend([1, 2, 3, 4]);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 64);
}
#[test]
fn dag_scan_arity0_closure() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let mut dag = DagBuilder::<u64>::new()
.root(|x: u64| x, reg)
.scan(
0u64,
|acc: &mut u64, val: &u64| {
*acc += val;
*acc
},
reg,
)
.then(store, reg)
.build();
dag.run(&mut world, 10);
assert_eq!(*world.resource::<u64>(), 10);
dag.run(&mut world, 20);
assert_eq!(*world.resource::<u64>(), 30);
dag.run(&mut world, 5);
assert_eq!(*world.resource::<u64>(), 35);
}
#[test]
fn dag_scan_named_fn_with_param() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(100);
wb.register::<String>(String::new());
let mut world = wb.build();
let reg = world.registry();
fn threshold(limit: Res<u64>, acc: &mut u64, val: &u64) -> Option<u64> {
*acc += val;
if *acc > *limit { Some(*acc) } else { None }
}
fn store_opt(mut out: ResMut<String>, val: &Option<u64>) {
*out = val
.as_ref()
.map_or_else(|| "below".into(), |v| format!("hit:{v}"));
}
let mut dag = DagBuilder::<u64>::new()
.root(|x: u64| x, reg)
.scan(0u64, threshold, reg)
.then(store_opt, reg)
.build();
dag.run(&mut world, 50);
assert_eq!(world.resource::<String>().as_str(), "below");
dag.run(&mut world, 60);
assert_eq!(world.resource::<String>().as_str(), "hit:110");
}
#[test]
fn dag_arm_scan() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let scan_arm = DagArmSeed::<u64>::new()
.then(|v: &u64| *v, reg)
.scan(
0u64,
|acc: &mut u64, val: &u64| {
*acc += val;
*acc
},
reg,
)
.then(store, reg);
let pass_arm = DagArmSeed::<u64>::new().then(|_: &u64| {}, reg);
let mut dag = DagBuilder::<u64>::new()
.root(|x: u64| x, reg)
.fork()
.arm(|_| scan_arm)
.arm(|_| pass_arm)
.merge(|(): &(), (): &()| {}, reg)
.build();
dag.run(&mut world, 10);
assert_eq!(*world.resource::<u64>(), 10);
dag.run(&mut world, 20);
assert_eq!(*world.resource::<u64>(), 30);
}
#[test]
fn build_option_unit_terminal() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn check(x: u32) -> u64 {
x as u64
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out += *val;
}
let mut dag = DagBuilder::<u32>::new()
.root(check, reg)
.guard(|val: &u64| *val > 5, reg)
.map(store, reg)
.build();
dag.run(&mut world, 3); assert_eq!(*world.resource::<u64>(), 0);
dag.run(&mut world, 7); assert_eq!(*world.resource::<u64>(), 7);
}
#[test]
fn build_borrowed_event_direct() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn decode(msg: &[u8]) -> u64 {
msg.len() as u64
}
fn store(mut out: ResMut<u64>, val: &u64) {
*out = *val;
}
let msg = vec![1u8, 2, 3];
let reg = world.registry();
let mut dag = DagBuilder::<&[u8]>::new()
.root(decode, reg)
.then(store, reg)
.build();
dag.run(&mut world, &msg);
assert_eq!(*world.resource::<u64>(), 3);
}
}