#![allow(clippy::type_complexity)]
use std::marker::PhantomData;
use crate::Handler;
use crate::dag::DagArm;
use crate::handler::{Opaque, Param};
use crate::world::{Registry, World};
#[doc(hidden)]
pub struct Step<F, Params: Param> {
f: F,
state: Params::State,
#[allow(dead_code)]
name: &'static str,
}
#[doc(hidden)]
pub trait StepCall<In> {
type Out;
fn call(&mut self, world: &mut World, input: In) -> Self::Out;
}
#[diagnostic::on_unimplemented(
message = "this function cannot be used as a pipeline step for this input type",
note = "if the pipeline output is `Option<T>`, use `.map()` to operate on the inner `T`",
note = "if the pipeline output is `Result<T, E>`, use `.map()` for `Ok` or `.catch()` for `Err`",
note = "if using a closure with resource params (Res<T>, ResMut<T>), that isn't supported — use a named `fn`"
)]
pub trait IntoStep<In, Out, Params> {
type Step: StepCall<In, Out = Out>;
fn into_step(self, registry: &Registry) -> Self::Step;
}
impl<In, Out, F: FnMut(In) -> Out + 'static> StepCall<In> for Step<F, ()> {
type Out = Out;
#[inline(always)]
fn call(&mut self, _world: &mut World, input: In) -> Out {
(self.f)(input)
}
}
impl<In, Out, F: FnMut(In) -> Out + 'static> IntoStep<In, Out, ()> for F {
type Step = Step<F, ()>;
fn into_step(self, registry: &Registry) -> Self::Step {
Step {
f: self,
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_into_step {
($($P:ident),+) => {
impl<In, Out, F: 'static, $($P: Param + 'static),+>
StepCall<In> for Step<F, ($($P,)+)>
where
for<'a> &'a mut F:
FnMut($($P,)+ In) -> Out +
FnMut($($P::Item<'a>,)+ In) -> Out,
{
type Out = Out;
#[inline(always)]
#[allow(non_snake_case)]
fn call(&mut self, world: &mut World, input: In) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<$($P,)+ Input, Output>(
mut f: impl FnMut($($P,)+ Input) -> Output,
$($P: $P,)+
input: Input,
) -> Output {
f($($P,)+ input)
}
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+ input)
}
}
impl<In, Out, F: 'static, $($P: Param + 'static),+>
IntoStep<In, Out, ($($P,)+)> for F
where
for<'a> &'a mut F:
FnMut($($P,)+ In) -> Out +
FnMut($($P::Item<'a>,)+ In) -> Out,
{
type Step = Step<F, ($($P,)+)>;
fn into_step(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as Param>::init(registry);
{
#[allow(non_snake_case)]
let ($($P,)+) = &state;
registry.check_access(&[
$(
(<$P as Param>::resource_id($P),
std::any::type_name::<$P>()),
)+
]);
}
Step { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
all_tuples!(impl_into_step);
#[doc(hidden)]
pub struct OpaqueStep<F> {
f: F,
#[allow(dead_code)]
name: &'static str,
}
impl<In, Out, F: FnMut(&mut World, In) -> Out + 'static> StepCall<In> for OpaqueStep<F> {
type Out = Out;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Out {
(self.f)(world, input)
}
}
impl<In, Out, F: FnMut(&mut World, In) -> Out + 'static> IntoStep<In, Out, Opaque> for F {
type Step = OpaqueStep<F>;
fn into_step(self, _registry: &Registry) -> Self::Step {
OpaqueStep {
f: self,
name: std::any::type_name::<F>(),
}
}
}
#[doc(hidden)]
pub trait RefStepCall<In> {
type Out;
fn call(&mut self, world: &mut World, input: &In) -> Self::Out;
}
#[diagnostic::on_unimplemented(
message = "this function cannot be used as a reference step for this input type",
note = "reference steps (guard, filter, tap, inspect) take `&In`, not `In`",
note = "if the pipeline output is `Option<T>`, `.filter()` operates on `&T` inside the Option",
note = "closures with resource parameters are not supported — use a named `fn`"
)]
pub trait IntoRefStep<In, Out, Params> {
type Step: RefStepCall<In, Out = Out>;
fn into_ref_step(self, registry: &Registry) -> Self::Step;
}
impl<In, Out, F: FnMut(&In) -> Out + 'static> RefStepCall<In> for Step<F, ()> {
type Out = Out;
#[inline(always)]
fn call(&mut self, _world: &mut World, input: &In) -> Out {
(self.f)(input)
}
}
impl<In, Out, F: FnMut(&In) -> Out + 'static> IntoRefStep<In, Out, ()> for F {
type Step = Step<F, ()>;
fn into_ref_step(self, registry: &Registry) -> Self::Step {
Step {
f: self,
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_into_ref_step {
($($P:ident),+) => {
impl<In, Out, F: 'static, $($P: Param + 'static),+>
RefStepCall<In> for Step<F, ($($P,)+)>
where
for<'a> &'a mut F:
FnMut($($P,)+ &In) -> Out +
FnMut($($P::Item<'a>,)+ &In) -> Out,
{
type Out = Out;
#[inline(always)]
#[allow(non_snake_case)]
fn call(&mut self, world: &mut World, input: &In) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<$($P,)+ Input: ?Sized, Output>(
mut f: impl FnMut($($P,)+ &Input) -> Output,
$($P: $P,)+
input: &Input,
) -> Output {
f($($P,)+ input)
}
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+ input)
}
}
impl<In, Out, F: 'static, $($P: Param + 'static),+>
IntoRefStep<In, Out, ($($P,)+)> for F
where
for<'a> &'a mut F:
FnMut($($P,)+ &In) -> Out +
FnMut($($P::Item<'a>,)+ &In) -> Out,
{
type Step = Step<F, ($($P,)+)>;
fn into_ref_step(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as Param>::init(registry);
{
#[allow(non_snake_case)]
let ($($P,)+) = &state;
registry.check_access(&[
$(
(<$P as Param>::resource_id($P),
std::any::type_name::<$P>()),
)+
]);
}
Step { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
all_tuples!(impl_into_ref_step);
#[doc(hidden)]
pub struct OpaqueRefStep<F> {
f: F,
#[allow(dead_code)]
name: &'static str,
}
impl<In, Out, F: FnMut(&mut World, &In) -> Out + 'static> RefStepCall<In> for OpaqueRefStep<F> {
type Out = Out;
#[inline(always)]
fn call(&mut self, world: &mut World, input: &In) -> Out {
(self.f)(world, input)
}
}
impl<In, Out, F: FnMut(&mut World, &In) -> Out + 'static> IntoRefStep<In, Out, Opaque> for F {
type Step = OpaqueRefStep<F>;
fn into_ref_step(self, _registry: &Registry) -> Self::Step {
OpaqueRefStep {
f: self,
name: std::any::type_name::<F>(),
}
}
}
pub fn resolve_ref_step<In, Out, Params, S: IntoRefStep<In, Out, Params>>(
f: S,
registry: &Registry,
) -> impl FnMut(&mut World, &In) -> Out + use<In, Out, Params, S> {
let mut resolved = f.into_ref_step(registry);
move |world: &mut World, input: &In| resolved.call(world, input)
}
#[doc(hidden)]
pub trait ProducerCall {
type Out;
fn call(&mut self, world: &mut World) -> Self::Out;
}
#[diagnostic::on_unimplemented(
message = "this function cannot be used as a producer for this output type",
note = "producers take no pipeline input — they produce a value (e.g., default, fallback)",
note = "closures with resource parameters are not supported — use a named `fn`"
)]
pub trait IntoProducer<Out, Params> {
type Step: ProducerCall<Out = Out>;
fn into_producer(self, registry: &Registry) -> Self::Step;
}
impl<Out, F: FnMut() -> Out + 'static> ProducerCall for Step<F, ()> {
type Out = Out;
#[inline(always)]
fn call(&mut self, _world: &mut World) -> Out {
(self.f)()
}
}
impl<Out, F: FnMut() -> Out + 'static> IntoProducer<Out, ()> for F {
type Step = Step<F, ()>;
fn into_producer(self, registry: &Registry) -> Self::Step {
Step {
f: self,
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_into_producer {
($($P:ident),+) => {
impl<Out, F: 'static, $($P: Param + 'static),+>
ProducerCall for Step<F, ($($P,)+)>
where
for<'a> &'a mut F:
FnMut($($P,)+) -> Out +
FnMut($($P::Item<'a>,)+) -> Out,
{
type Out = Out;
#[inline(always)]
#[allow(non_snake_case)]
fn call(&mut self, world: &mut World) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<$($P,)+ Output>(
mut f: impl FnMut($($P,)+) -> Output,
$($P: $P,)+
) -> Output {
f($($P,)+)
}
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+)
}
}
impl<Out, F: 'static, $($P: Param + 'static),+>
IntoProducer<Out, ($($P,)+)> for F
where
for<'a> &'a mut F:
FnMut($($P,)+) -> Out +
FnMut($($P::Item<'a>,)+) -> Out,
{
type Step = Step<F, ($($P,)+)>;
fn into_producer(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as Param>::init(registry);
{
#[allow(non_snake_case)]
let ($($P,)+) = &state;
registry.check_access(&[
$(
(<$P as Param>::resource_id($P),
std::any::type_name::<$P>()),
)+
]);
}
Step { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
all_tuples!(impl_into_producer);
#[doc(hidden)]
pub struct OpaqueProducer<F> {
f: F,
#[allow(dead_code)]
name: &'static str,
}
impl<Out, F: FnMut(&mut World) -> Out + 'static> ProducerCall for OpaqueProducer<F> {
type Out = Out;
#[inline(always)]
fn call(&mut self, world: &mut World) -> Out {
(self.f)(world)
}
}
impl<Out, F: FnMut(&mut World) -> Out + 'static> IntoProducer<Out, Opaque> for F {
type Step = OpaqueProducer<F>;
fn into_producer(self, _registry: &Registry) -> Self::Step {
OpaqueProducer {
f: self,
name: std::any::type_name::<F>(),
}
}
}
pub fn resolve_producer<Out, Params, S: IntoProducer<Out, Params>>(
f: S,
registry: &Registry,
) -> impl FnMut(&mut World) -> Out + use<Out, Params, S> {
let mut resolved = f.into_producer(registry);
move |world: &mut World| resolved.call(world)
}
#[doc(hidden)]
pub trait ScanStepCall<Acc, In> {
type Out;
fn call(&mut self, world: &mut World, acc: &mut Acc, input: In) -> Self::Out;
}
#[diagnostic::on_unimplemented(
message = "this function cannot be used as a scan step",
note = "scan steps take `&mut Accumulator` as first param, then resources, then input last",
note = "closures with resource parameters are not supported — use a named `fn`"
)]
pub trait IntoScanStep<Acc, In, Out, Params> {
type Step: ScanStepCall<Acc, In, Out = Out>;
fn into_scan_step(self, registry: &Registry) -> Self::Step;
}
impl<Acc, In, Out, F: FnMut(&mut Acc, In) -> Out + 'static> ScanStepCall<Acc, In> for Step<F, ()> {
type Out = Out;
#[inline(always)]
fn call(&mut self, _world: &mut World, acc: &mut Acc, input: In) -> Out {
(self.f)(acc, input)
}
}
impl<Acc, In, Out, F: FnMut(&mut Acc, In) -> Out + 'static> IntoScanStep<Acc, In, Out, ()> for F {
type Step = Step<F, ()>;
fn into_scan_step(self, registry: &Registry) -> Self::Step {
Step {
f: self,
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_into_scan_step {
($($P:ident),+) => {
impl<Acc, In, Out, F: 'static, $($P: Param + 'static),+>
ScanStepCall<Acc, In> for Step<F, ($($P,)+)>
where
for<'a> &'a mut F:
FnMut($($P,)+ &mut Acc, In) -> Out +
FnMut($($P::Item<'a>,)+ &mut Acc, In) -> Out,
{
type Out = Out;
#[inline(always)]
#[allow(non_snake_case)]
fn call(&mut self, world: &mut World, acc: &mut Acc, input: In) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<$($P,)+ Accumulator, Input, Output>(
mut f: impl FnMut($($P,)+ &mut Accumulator, Input) -> Output,
$($P: $P,)+
acc: &mut Accumulator,
input: Input,
) -> Output {
f($($P,)+ acc, input)
}
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+ acc, input)
}
}
impl<Acc, In, Out, F: 'static, $($P: Param + 'static),+>
IntoScanStep<Acc, In, Out, ($($P,)+)> for F
where
for<'a> &'a mut F:
FnMut($($P,)+ &mut Acc, In) -> Out +
FnMut($($P::Item<'a>,)+ &mut Acc, In) -> Out,
{
type Step = Step<F, ($($P,)+)>;
fn into_scan_step(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as Param>::init(registry);
{
#[allow(non_snake_case)]
let ($($P,)+) = &state;
registry.check_access(&[
$(
(<$P as Param>::resource_id($P),
std::any::type_name::<$P>()),
)+
]);
}
Step { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
all_tuples!(impl_into_scan_step);
#[doc(hidden)]
pub struct OpaqueScanStep<F> {
f: F,
#[allow(dead_code)]
name: &'static str,
}
impl<Acc, In, Out, F: FnMut(&mut World, &mut Acc, In) -> Out + 'static> ScanStepCall<Acc, In>
for OpaqueScanStep<F>
{
type Out = Out;
#[inline(always)]
fn call(&mut self, world: &mut World, acc: &mut Acc, input: In) -> Out {
(self.f)(world, acc, input)
}
}
impl<Acc, In, Out, F: FnMut(&mut World, &mut Acc, In) -> Out + 'static>
IntoScanStep<Acc, In, Out, Opaque> for F
{
type Step = OpaqueScanStep<F>;
fn into_scan_step(self, _registry: &Registry) -> Self::Step {
OpaqueScanStep {
f: self,
name: std::any::type_name::<F>(),
}
}
}
pub fn resolve_scan_step<Acc, In, Out, Params, S: IntoScanStep<Acc, In, Out, Params>>(
f: S,
registry: &Registry,
) -> impl FnMut(&mut World, &mut Acc, In) -> Out + use<Acc, In, Out, Params, S> {
let mut resolved = f.into_scan_step(registry);
move |world: &mut World, acc: &mut Acc, input: In| resolved.call(world, acc, input)
}
#[doc(hidden)]
pub trait RefScanStepCall<Acc, In> {
type Out;
fn call(&mut self, world: &mut World, acc: &mut Acc, input: &In) -> Self::Out;
}
#[diagnostic::on_unimplemented(
message = "this function cannot be used as a reference scan step",
note = "reference scan steps take `&mut Accumulator` as first param, then resources, then `&In` last",
note = "closures with resource parameters are not supported — use a named `fn`"
)]
pub trait IntoRefScanStep<Acc, In, Out, Params> {
type Step: RefScanStepCall<Acc, In, Out = Out>;
fn into_ref_scan_step(self, registry: &Registry) -> Self::Step;
}
impl<Acc, In, Out, F: FnMut(&mut Acc, &In) -> Out + 'static> RefScanStepCall<Acc, In>
for Step<F, ()>
{
type Out = Out;
#[inline(always)]
fn call(&mut self, _world: &mut World, acc: &mut Acc, input: &In) -> Out {
(self.f)(acc, input)
}
}
impl<Acc, In, Out, F: FnMut(&mut Acc, &In) -> Out + 'static> IntoRefScanStep<Acc, In, Out, ()>
for F
{
type Step = Step<F, ()>;
fn into_ref_scan_step(self, registry: &Registry) -> Self::Step {
Step {
f: self,
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_into_ref_scan_step {
($($P:ident),+) => {
impl<Acc, In, Out, F: 'static, $($P: Param + 'static),+>
RefScanStepCall<Acc, In> for Step<F, ($($P,)+)>
where
for<'a> &'a mut F:
FnMut($($P,)+ &mut Acc, &In) -> Out +
FnMut($($P::Item<'a>,)+ &mut Acc, &In) -> Out,
{
type Out = Out;
#[inline(always)]
#[allow(non_snake_case)]
fn call(&mut self, world: &mut World, acc: &mut Acc, input: &In) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<$($P,)+ Accumulator, Input: ?Sized, Output>(
mut f: impl FnMut($($P,)+ &mut Accumulator, &Input) -> Output,
$($P: $P,)+
acc: &mut Accumulator,
input: &Input,
) -> Output {
f($($P,)+ acc, input)
}
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+ acc, input)
}
}
impl<Acc, In, Out, F: 'static, $($P: Param + 'static),+>
IntoRefScanStep<Acc, In, Out, ($($P,)+)> for F
where
for<'a> &'a mut F:
FnMut($($P,)+ &mut Acc, &In) -> Out +
FnMut($($P::Item<'a>,)+ &mut Acc, &In) -> Out,
{
type Step = Step<F, ($($P,)+)>;
fn into_ref_scan_step(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as Param>::init(registry);
{
#[allow(non_snake_case)]
let ($($P,)+) = &state;
registry.check_access(&[
$(
(<$P as Param>::resource_id($P),
std::any::type_name::<$P>()),
)+
]);
}
Step { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
all_tuples!(impl_into_ref_scan_step);
#[doc(hidden)]
pub struct OpaqueRefScanStep<F> {
f: F,
#[allow(dead_code)]
name: &'static str,
}
impl<Acc, In, Out, F: FnMut(&mut World, &mut Acc, &In) -> Out + 'static> RefScanStepCall<Acc, In>
for OpaqueRefScanStep<F>
{
type Out = Out;
#[inline(always)]
fn call(&mut self, world: &mut World, acc: &mut Acc, input: &In) -> Out {
(self.f)(world, acc, input)
}
}
impl<Acc, In, Out, F: FnMut(&mut World, &mut Acc, &In) -> Out + 'static>
IntoRefScanStep<Acc, In, Out, Opaque> for F
{
type Step = OpaqueRefScanStep<F>;
fn into_ref_scan_step(self, _registry: &Registry) -> Self::Step {
OpaqueRefScanStep {
f: self,
name: std::any::type_name::<F>(),
}
}
}
pub fn resolve_ref_scan_step<Acc, In, Out, Params, S: IntoRefScanStep<Acc, In, Out, Params>>(
f: S,
registry: &Registry,
) -> impl FnMut(&mut World, &mut Acc, &In) -> Out + use<Acc, In, Out, Params, S> {
let mut resolved = f.into_ref_scan_step(registry);
move |world: &mut World, acc: &mut Acc, input: &In| resolved.call(world, acc, input)
}
#[doc(hidden)]
pub trait SplatCall2<A, B> {
type Out;
fn call_splat(&mut self, world: &mut World, a: A, b: B) -> Self::Out;
}
#[doc(hidden)]
pub trait IntoSplatStep2<A, B, Out, Params> {
type Step: SplatCall2<A, B, Out = Out>;
fn into_splat_step(self, registry: &Registry) -> Self::Step;
}
impl<A, B, Out, F: FnMut(A, B) -> Out + 'static> SplatCall2<A, B> for Step<F, ()> {
type Out = Out;
#[inline(always)]
fn call_splat(&mut self, _world: &mut World, a: A, b: B) -> Out {
(self.f)(a, b)
}
}
impl<A, B, Out, F: FnMut(A, B) -> Out + 'static> IntoSplatStep2<A, B, Out, ()> for F {
type Step = Step<F, ()>;
fn into_splat_step(self, registry: &Registry) -> Self::Step {
Step {
f: self,
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_splat2_step {
($($P:ident),+) => {
impl<A, B, Out, F: 'static, $($P: Param + 'static),+>
SplatCall2<A, B> for Step<F, ($($P,)+)>
where
for<'a> &'a mut F:
FnMut($($P,)+ A, B) -> Out +
FnMut($($P::Item<'a>,)+ A, B) -> Out,
{
type Out = Out;
#[inline(always)]
#[allow(non_snake_case)]
fn call_splat(&mut self, world: &mut World, a: A, b: 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 Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+ a, b)
}
}
impl<A, B, Out, F: 'static, $($P: Param + 'static),+>
IntoSplatStep2<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 = Step<F, ($($P,)+)>;
fn into_splat_step(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as Param>::init(registry);
{
#[allow(non_snake_case)]
let ($($P,)+) = &state;
registry.check_access(&[
$(
(<$P as Param>::resource_id($P),
std::any::type_name::<$P>()),
)+
]);
}
Step { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
#[doc(hidden)]
pub trait SplatCall3<A, B, C> {
type Out;
fn call_splat(&mut self, world: &mut World, a: A, b: B, c: C) -> Self::Out;
}
#[doc(hidden)]
pub trait IntoSplatStep3<A, B, C, Out, Params> {
type Step: SplatCall3<A, B, C, Out = Out>;
fn into_splat_step(self, registry: &Registry) -> Self::Step;
}
impl<A, B, C, Out, F: FnMut(A, B, C) -> Out + 'static> SplatCall3<A, B, C> for Step<F, ()> {
type Out = Out;
#[inline(always)]
fn call_splat(&mut self, _world: &mut World, a: A, b: B, c: C) -> Out {
(self.f)(a, b, c)
}
}
impl<A, B, C, Out, F: FnMut(A, B, C) -> Out + 'static> IntoSplatStep3<A, B, C, Out, ()> for F {
type Step = Step<F, ()>;
fn into_splat_step(self, registry: &Registry) -> Self::Step {
Step {
f: self,
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_splat3_step {
($($P:ident),+) => {
impl<A, B, C, Out, F: 'static, $($P: Param + 'static),+>
SplatCall3<A, B, C> for Step<F, ($($P,)+)>
where
for<'a> &'a mut F:
FnMut($($P,)+ A, B, C) -> Out +
FnMut($($P::Item<'a>,)+ A, B, C) -> Out,
{
type Out = Out;
#[inline(always)]
#[allow(non_snake_case)]
fn call_splat(&mut self, world: &mut World, a: A, b: B, c: 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 Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+ a, b, c)
}
}
impl<A, B, C, Out, F: 'static, $($P: Param + 'static),+>
IntoSplatStep3<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 = Step<F, ($($P,)+)>;
fn into_splat_step(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as Param>::init(registry);
{
#[allow(non_snake_case)]
let ($($P,)+) = &state;
registry.check_access(&[
$(
(<$P as Param>::resource_id($P),
std::any::type_name::<$P>()),
)+
]);
}
Step { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
#[doc(hidden)]
pub trait SplatCall4<A, B, C, D> {
type Out;
fn call_splat(&mut self, world: &mut World, a: A, b: B, c: C, d: D) -> Self::Out;
}
#[doc(hidden)]
pub trait IntoSplatStep4<A, B, C, D, Out, Params> {
type Step: SplatCall4<A, B, C, D, Out = Out>;
fn into_splat_step(self, registry: &Registry) -> Self::Step;
}
impl<A, B, C, D, Out, F: FnMut(A, B, C, D) -> Out + 'static> SplatCall4<A, B, C, D>
for Step<F, ()>
{
type Out = Out;
#[inline(always)]
fn call_splat(&mut self, _world: &mut World, a: A, b: B, c: C, d: D) -> Out {
(self.f)(a, b, c, d)
}
}
impl<A, B, C, D, Out, F: FnMut(A, B, C, D) -> Out + 'static> IntoSplatStep4<A, B, C, D, Out, ()>
for F
{
type Step = Step<F, ()>;
fn into_splat_step(self, registry: &Registry) -> Self::Step {
Step {
f: self,
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_splat4_step {
($($P:ident),+) => {
impl<A, B, C, D, Out, F: 'static, $($P: Param + 'static),+>
SplatCall4<A, B, C, D> for Step<F, ($($P,)+)>
where for<'a> &'a mut F:
FnMut($($P,)+ A, B, C, D) -> Out +
FnMut($($P::Item<'a>,)+ A, B, C, D) -> Out,
{
type Out = Out;
#[inline(always)]
#[allow(non_snake_case)]
fn call_splat(&mut self, world: &mut World, a: A, b: B, c: C, d: 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 Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+ a, b, c, d)
}
}
impl<A, B, C, D, Out, F: 'static, $($P: Param + 'static),+>
IntoSplatStep4<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 = Step<F, ($($P,)+)>;
fn into_splat_step(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as Param>::init(registry);
{ #[allow(non_snake_case)] let ($($P,)+) = &state;
registry.check_access(&[$((<$P as Param>::resource_id($P), std::any::type_name::<$P>()),)+]); }
Step { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
#[doc(hidden)]
pub trait SplatCall5<A, B, C, D, E> {
type Out;
#[allow(clippy::many_single_char_names)]
fn call_splat(&mut self, world: &mut World, a: A, b: B, c: C, d: D, e: E) -> Self::Out;
}
#[doc(hidden)]
pub trait IntoSplatStep5<A, B, C, D, E, Out, Params> {
type Step: SplatCall5<A, B, C, D, E, Out = Out>;
fn into_splat_step(self, registry: &Registry) -> Self::Step;
}
impl<A, B, C, D, E, Out, F: FnMut(A, B, C, D, E) -> Out + 'static> SplatCall5<A, B, C, D, E>
for Step<F, ()>
{
type Out = Out;
#[inline(always)]
#[allow(clippy::many_single_char_names)]
fn call_splat(&mut self, _world: &mut World, a: A, b: B, c: C, d: D, e: E) -> Out {
(self.f)(a, b, c, d, e)
}
}
impl<A, B, C, D, E, Out, F: FnMut(A, B, C, D, E) -> Out + 'static>
IntoSplatStep5<A, B, C, D, E, Out, ()> for F
{
type Step = Step<F, ()>;
fn into_splat_step(self, registry: &Registry) -> Self::Step {
Step {
f: self,
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_splat5_step {
($($P:ident),+) => {
impl<A, B, C, D, E, Out, F: 'static, $($P: Param + 'static),+>
SplatCall5<A, B, C, D, E> for Step<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,
{
type Out = Out;
#[inline(always)]
#[allow(non_snake_case, clippy::many_single_char_names)]
fn call_splat(&mut self, world: &mut World, a: A, b: B, c: C, d: D, e: 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 Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, $($P,)+ a, b, c, d, e)
}
}
impl<A, B, C, D, E, Out, F: 'static, $($P: Param + 'static),+>
IntoSplatStep5<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 = Step<F, ($($P,)+)>;
fn into_splat_step(self, registry: &Registry) -> Self::Step {
let state = <($($P,)+) as Param>::init(registry);
{ #[allow(non_snake_case)] let ($($P,)+) = &state;
registry.check_access(&[$((<$P as Param>::resource_id($P), std::any::type_name::<$P>()),)+]); }
Step { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
all_tuples!(impl_splat2_step);
all_tuples!(impl_splat3_step);
all_tuples!(impl_splat4_step);
all_tuples!(impl_splat5_step);
#[doc(hidden)]
pub trait ChainCall<In> {
type Out;
fn call(&mut self, world: &mut World, input: In) -> Self::Out;
}
#[doc(hidden)]
pub struct IdentityNode;
impl<In> ChainCall<In> for IdentityNode {
type Out = In;
#[inline(always)]
fn call(&mut self, _world: &mut World, input: In) -> In {
input
}
}
#[doc(hidden)]
pub struct ThenNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, Prev, S> ChainCall<In> for ThenNode<Prev, S>
where
Prev: ChainCall<In>,
S: StepCall<Prev::Out>,
{
type Out = S::Out;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> S::Out {
let mid = self.prev.call(world, input);
self.step.call(world, mid)
}
}
#[doc(hidden)]
pub struct TapNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, Prev, S> ChainCall<In> for TapNode<Prev, S>
where
Prev: ChainCall<In>,
S: RefStepCall<Prev::Out, Out = ()>,
{
type Out = Prev::Out;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Prev::Out {
let val = self.prev.call(world, input);
self.step.call(world, &val);
val
}
}
#[doc(hidden)]
pub struct GuardNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, Prev, S> ChainCall<In> for GuardNode<Prev, S>
where
Prev: ChainCall<In>,
S: RefStepCall<Prev::Out, Out = bool>,
{
type Out = Option<Prev::Out>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<Prev::Out> {
let val = self.prev.call(world, input);
if self.step.call(world, &val) {
Some(val)
} else {
None
}
}
}
#[doc(hidden)]
pub struct DedupNode<Prev, T> {
pub(crate) prev: Prev,
pub(crate) last: Option<T>,
}
impl<In, T: PartialEq + Clone, Prev: ChainCall<In, Out = T>> ChainCall<In> for DedupNode<Prev, T> {
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<T> {
let val = self.prev.call(world, input);
if self.last.as_ref() == Some(&val) {
None
} else {
self.last = Some(val.clone());
Some(val)
}
}
}
#[doc(hidden)]
pub struct ScanNode<Prev, S, Acc> {
pub(crate) prev: Prev,
pub(crate) step: S,
pub(crate) acc: Acc,
}
impl<In, Prev, S, Acc> ChainCall<In> for ScanNode<Prev, S, Acc>
where
Prev: ChainCall<In>,
S: ScanStepCall<Acc, Prev::Out>,
{
type Out = S::Out;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> S::Out {
let val = self.prev.call(world, input);
self.step.call(world, &mut self.acc, val)
}
}
#[doc(hidden)]
pub struct DispatchNode<Prev, H> {
pub(crate) prev: Prev,
pub(crate) handler: H,
}
impl<In, Prev, H> ChainCall<In> for DispatchNode<Prev, H>
where
Prev: ChainCall<In>,
H: Handler<Prev::Out>,
{
type Out = ();
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) {
let out = self.prev.call(world, input);
self.handler.run(world, out);
}
}
#[doc(hidden)]
pub struct TeeNode<Prev, C> {
pub(crate) prev: Prev,
pub(crate) side: C,
}
impl<In, Prev, C> ChainCall<In> for TeeNode<Prev, C>
where
Prev: ChainCall<In>,
Prev::Out: 'static,
C: for<'a> ChainCall<&'a Prev::Out, Out = ()>,
{
type Out = Prev::Out;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Prev::Out {
let val = self.prev.call(world, input);
self.side.call(world, &val);
val
}
}
#[doc(hidden)]
pub struct RouteNode<Prev, P, C0, C1> {
pub(crate) prev: Prev,
pub(crate) pred: P,
pub(crate) on_true: C0,
pub(crate) on_false: C1,
}
impl<In, Prev, P, C0, C1> ChainCall<In> for RouteNode<Prev, P, C0, C1>
where
Prev: ChainCall<In>,
P: RefStepCall<Prev::Out, Out = bool>,
C0: ChainCall<Prev::Out>,
C1: ChainCall<Prev::Out, Out = C0::Out>,
{
type Out = C0::Out;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> C0::Out {
let val = self.prev.call(world, input);
if self.pred.call(world, &val) {
self.on_true.call(world, val)
} else {
self.on_false.call(world, val)
}
}
}
#[doc(hidden)]
pub struct MapOptionNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, Prev, S> ChainCall<In> for MapOptionNode<Prev, S>
where
Prev: ChainCall<In, Out = Option<T>>,
S: StepCall<T>,
{
type Out = Option<S::Out>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<S::Out> {
self.prev
.call(world, input)
.map(|val| self.step.call(world, val))
}
}
#[doc(hidden)]
pub struct FilterNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, Prev, S> ChainCall<In> for FilterNode<Prev, S>
where
Prev: ChainCall<In, Out = Option<T>>,
S: RefStepCall<T, Out = bool>,
{
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<T> {
self.prev
.call(world, input)
.filter(|val| self.step.call(world, val))
}
}
#[doc(hidden)]
pub struct InspectOptionNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, Prev, S> ChainCall<In> for InspectOptionNode<Prev, S>
where
Prev: ChainCall<In, Out = Option<T>>,
S: RefStepCall<T, Out = ()>,
{
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<T> {
self.prev
.call(world, input)
.inspect(|val| self.step.call(world, val))
}
}
#[doc(hidden)]
pub struct AndThenNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, U, Prev, S> ChainCall<In> for AndThenNode<Prev, S>
where
Prev: ChainCall<In, Out = Option<T>>,
S: StepCall<T, Out = Option<U>>,
{
type Out = Option<U>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<U> {
self.prev
.call(world, input)
.and_then(|val| self.step.call(world, val))
}
}
#[doc(hidden)]
pub struct OnNoneNode<Prev, P> {
pub(crate) prev: Prev,
pub(crate) producer: P,
}
impl<In, T, Prev, P> ChainCall<In> for OnNoneNode<Prev, P>
where
Prev: ChainCall<In, Out = Option<T>>,
P: ProducerCall<Out = ()>,
{
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<T> {
let result = self.prev.call(world, input);
if result.is_none() {
self.producer.call(world);
}
result
}
}
#[doc(hidden)]
pub struct OkOrNode<Prev, E> {
pub(crate) prev: Prev,
pub(crate) err: E,
}
impl<In, T, E: Clone, Prev: ChainCall<In, Out = Option<T>>> ChainCall<In> for OkOrNode<Prev, E> {
type Out = Result<T, E>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Result<T, E> {
self.prev.call(world, input).ok_or_else(|| self.err.clone())
}
}
#[doc(hidden)]
pub struct OkOrElseNode<Prev, P> {
pub(crate) prev: Prev,
pub(crate) producer: P,
}
impl<In, T, E, Prev, P> ChainCall<In> for OkOrElseNode<Prev, P>
where
Prev: ChainCall<In, Out = Option<T>>,
P: ProducerCall<Out = E>,
{
type Out = Result<T, E>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Result<T, E> {
self.prev
.call(world, input)
.ok_or_else(|| self.producer.call(world))
}
}
#[doc(hidden)]
pub struct UnwrapOrOptionNode<Prev, T> {
pub(crate) prev: Prev,
pub(crate) default: T,
}
impl<In, T: Clone, Prev: ChainCall<In, Out = Option<T>>> ChainCall<In>
for UnwrapOrOptionNode<Prev, T>
{
type Out = T;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> T {
self.prev
.call(world, input)
.unwrap_or_else(|| self.default.clone())
}
}
#[doc(hidden)]
pub struct UnwrapOrElseOptionNode<Prev, P> {
pub(crate) prev: Prev,
pub(crate) producer: P,
}
impl<In, T, Prev, P> ChainCall<In> for UnwrapOrElseOptionNode<Prev, P>
where
Prev: ChainCall<In, Out = Option<T>>,
P: ProducerCall<Out = T>,
{
type Out = T;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> T {
self.prev
.call(world, input)
.unwrap_or_else(|| self.producer.call(world))
}
}
#[doc(hidden)]
pub struct MapResultNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, E, Prev, S> ChainCall<In> for MapResultNode<Prev, S>
where
Prev: ChainCall<In, Out = Result<T, E>>,
S: StepCall<T>,
{
type Out = Result<S::Out, E>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Result<S::Out, E> {
self.prev
.call(world, input)
.map(|val| self.step.call(world, val))
}
}
#[doc(hidden)]
pub struct AndThenResultNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, U, E, Prev, S> ChainCall<In> for AndThenResultNode<Prev, S>
where
Prev: ChainCall<In, Out = Result<T, E>>,
S: StepCall<T, Out = Result<U, E>>,
{
type Out = Result<U, E>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Result<U, E> {
self.prev
.call(world, input)
.and_then(|val| self.step.call(world, val))
}
}
#[doc(hidden)]
pub struct CatchNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, E, Prev, S> ChainCall<In> for CatchNode<Prev, S>
where
Prev: ChainCall<In, Out = Result<T, E>>,
S: StepCall<E, Out = ()>,
{
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<T> {
match self.prev.call(world, input) {
Ok(val) => Some(val),
Err(err) => {
self.step.call(world, err);
None
}
}
}
}
#[doc(hidden)]
pub struct MapErrNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, E, Prev, S> ChainCall<In> for MapErrNode<Prev, S>
where
Prev: ChainCall<In, Out = Result<T, E>>,
S: StepCall<E>,
{
type Out = Result<T, S::Out>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Result<T, S::Out> {
self.prev
.call(world, input)
.map_err(|err| self.step.call(world, err))
}
}
#[doc(hidden)]
pub struct OrElseNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, E, E2, Prev, S> ChainCall<In> for OrElseNode<Prev, S>
where
Prev: ChainCall<In, Out = Result<T, E>>,
S: StepCall<E, Out = Result<T, E2>>,
{
type Out = Result<T, E2>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Result<T, E2> {
self.prev
.call(world, input)
.or_else(|err| self.step.call(world, err))
}
}
#[doc(hidden)]
pub struct InspectResultNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, E, Prev, S> ChainCall<In> for InspectResultNode<Prev, S>
where
Prev: ChainCall<In, Out = Result<T, E>>,
S: RefStepCall<T, Out = ()>,
{
type Out = Result<T, E>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Result<T, E> {
self.prev
.call(world, input)
.inspect(|val| self.step.call(world, val))
}
}
#[doc(hidden)]
pub struct InspectErrNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, E, Prev, S> ChainCall<In> for InspectErrNode<Prev, S>
where
Prev: ChainCall<In, Out = Result<T, E>>,
S: RefStepCall<E, Out = ()>,
{
type Out = Result<T, E>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Result<T, E> {
self.prev
.call(world, input)
.inspect_err(|err| self.step.call(world, err))
}
}
#[doc(hidden)]
pub struct OkResultNode<Prev> {
pub(crate) prev: Prev,
}
impl<In, T, E, Prev: ChainCall<In, Out = Result<T, E>>> ChainCall<In> for OkResultNode<Prev> {
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<T> {
self.prev.call(world, input).ok()
}
}
#[doc(hidden)]
pub struct UnwrapOrResultNode<Prev, T> {
pub(crate) prev: Prev,
pub(crate) default: T,
}
impl<In, T: Clone, E, Prev: ChainCall<In, Out = Result<T, E>>> ChainCall<In>
for UnwrapOrResultNode<Prev, T>
{
type Out = T;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> T {
self.prev
.call(world, input)
.unwrap_or_else(|_| self.default.clone())
}
}
#[doc(hidden)]
pub struct UnwrapOrElseResultNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, E, Prev, S> ChainCall<In> for UnwrapOrElseResultNode<Prev, S>
where
Prev: ChainCall<In, Out = Result<T, E>>,
S: StepCall<E, Out = T>,
{
type Out = T;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> T {
match self.prev.call(world, input) {
Ok(val) => val,
Err(err) => self.step.call(world, err),
}
}
}
#[doc(hidden)]
pub struct NotNode<Prev> {
pub(crate) prev: Prev,
}
impl<In, Prev: ChainCall<In, Out = bool>> ChainCall<In> for NotNode<Prev> {
type Out = bool;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> bool {
!self.prev.call(world, input)
}
}
#[doc(hidden)]
pub struct AndBoolNode<Prev, P> {
pub(crate) prev: Prev,
pub(crate) producer: P,
}
impl<In, Prev, P> ChainCall<In> for AndBoolNode<Prev, P>
where
Prev: ChainCall<In, Out = bool>,
P: ProducerCall<Out = bool>,
{
type Out = bool;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> bool {
self.prev.call(world, input) && self.producer.call(world)
}
}
#[doc(hidden)]
pub struct OrBoolNode<Prev, P> {
pub(crate) prev: Prev,
pub(crate) producer: P,
}
impl<In, Prev, P> ChainCall<In> for OrBoolNode<Prev, P>
where
Prev: ChainCall<In, Out = bool>,
P: ProducerCall<Out = bool>,
{
type Out = bool;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> bool {
self.prev.call(world, input) || self.producer.call(world)
}
}
#[doc(hidden)]
pub struct XorBoolNode<Prev, P> {
pub(crate) prev: Prev,
pub(crate) producer: P,
}
impl<In, Prev, P> ChainCall<In> for XorBoolNode<Prev, P>
where
Prev: ChainCall<In, Out = bool>,
P: ProducerCall<Out = bool>,
{
type Out = bool;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> bool {
self.prev.call(world, input) ^ self.producer.call(world)
}
}
#[doc(hidden)]
pub struct ClonedNode<Prev> {
pub(crate) prev: Prev,
}
impl<'a, In, T: Clone + 'a, Prev: ChainCall<In, Out = &'a T>> ChainCall<In> for ClonedNode<Prev> {
type Out = T;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> T {
T::clone(self.prev.call(world, input))
}
}
#[doc(hidden)]
pub struct ClonedOptionNode<Prev> {
pub(crate) prev: Prev,
}
impl<'a, In, T: Clone + 'a, Prev: ChainCall<In, Out = Option<&'a T>>> ChainCall<In>
for ClonedOptionNode<Prev>
{
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<T> {
self.prev.call(world, input).cloned()
}
}
#[doc(hidden)]
pub struct ClonedResultNode<Prev> {
pub(crate) prev: Prev,
}
impl<'a, In, T: Clone + 'a, E, Prev: ChainCall<In, Out = Result<&'a T, E>>> ChainCall<In>
for ClonedResultNode<Prev>
{
type Out = Result<T, E>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Result<T, E> {
self.prev.call(world, input).cloned()
}
}
#[doc(hidden)]
pub struct DagThenNode<Prev, S, NewOut> {
pub(crate) prev: Prev,
pub(crate) step: S,
pub(crate) _out: PhantomData<fn() -> NewOut>,
}
impl<In, Prev, S, NewOut: 'static> ChainCall<In> for DagThenNode<Prev, S, NewOut>
where
Prev: ChainCall<In>,
Prev::Out: 'static,
S: for<'a> StepCall<&'a Prev::Out, Out = NewOut>,
{
type Out = NewOut;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> NewOut {
let out = self.prev.call(world, input);
self.step.call(world, &out)
}
}
#[doc(hidden)]
pub struct RefScanNode<Prev, S, Acc> {
pub(crate) prev: Prev,
pub(crate) step: S,
pub(crate) acc: Acc,
}
impl<In, Prev, S, Acc> ChainCall<In> for RefScanNode<Prev, S, Acc>
where
Prev: ChainCall<In>,
S: RefScanStepCall<Acc, Prev::Out>,
{
type Out = S::Out;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> S::Out {
let val = self.prev.call(world, input);
self.step.call(world, &mut self.acc, &val)
}
}
#[doc(hidden)]
pub struct DagRouteNode<Prev, P, C0, C1, NewOut> {
pub(crate) prev: Prev,
pub(crate) pred: P,
pub(crate) on_true: C0,
pub(crate) on_false: C1,
pub(crate) _out: PhantomData<fn() -> NewOut>,
}
impl<In, Prev, P, C0, C1, NewOut> ChainCall<In> for DagRouteNode<Prev, P, C0, C1, NewOut>
where
Prev: ChainCall<In>,
Prev::Out: 'static,
P: RefStepCall<Prev::Out, Out = bool>,
C0: for<'a> ChainCall<&'a Prev::Out, Out = NewOut>,
C1: for<'a> ChainCall<&'a Prev::Out, Out = NewOut>,
{
type Out = NewOut;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> NewOut {
let val = self.prev.call(world, input);
if self.pred.call(world, &val) {
self.on_true.call(world, &val)
} else {
self.on_false.call(world, &val)
}
}
}
#[doc(hidden)]
pub struct DagMapOptionNode<Prev, S, U> {
pub(crate) prev: Prev,
pub(crate) step: S,
pub(crate) _out: PhantomData<fn() -> U>,
}
impl<In, T: 'static, U: 'static, Prev, S> ChainCall<In> for DagMapOptionNode<Prev, S, U>
where
Prev: ChainCall<In, Out = Option<T>>,
S: for<'a> StepCall<&'a T, Out = U>,
{
type Out = Option<U>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<U> {
self.prev
.call(world, input)
.map(|ref val| self.step.call(world, val))
}
}
#[doc(hidden)]
pub struct DagAndThenOptionNode<Prev, S, U> {
pub(crate) prev: Prev,
pub(crate) step: S,
pub(crate) _out: PhantomData<fn() -> U>,
}
impl<In, T: 'static, U: 'static, Prev, S> ChainCall<In> for DagAndThenOptionNode<Prev, S, U>
where
Prev: ChainCall<In, Out = Option<T>>,
S: for<'a> StepCall<&'a T, Out = Option<U>>,
{
type Out = Option<U>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<U> {
self.prev
.call(world, input)
.and_then(|ref val| self.step.call(world, val))
}
}
#[doc(hidden)]
pub struct DagMapResultNode<Prev, S, U> {
pub(crate) prev: Prev,
pub(crate) step: S,
pub(crate) _out: PhantomData<fn() -> U>,
}
impl<In, T: 'static, E, U: 'static, Prev, S> ChainCall<In> for DagMapResultNode<Prev, S, U>
where
Prev: ChainCall<In, Out = Result<T, E>>,
S: for<'a> StepCall<&'a T, Out = U>,
{
type Out = Result<U, E>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Result<U, E> {
self.prev
.call(world, input)
.map(|ref val| self.step.call(world, val))
}
}
#[doc(hidden)]
pub struct DagAndThenResultNode<Prev, S, U> {
pub(crate) prev: Prev,
pub(crate) step: S,
pub(crate) _out: PhantomData<fn() -> U>,
}
impl<In, T: 'static, E, U: 'static, Prev, S> ChainCall<In> for DagAndThenResultNode<Prev, S, U>
where
Prev: ChainCall<In, Out = Result<T, E>>,
S: for<'a> StepCall<&'a T, Out = Result<U, E>>,
{
type Out = Result<U, E>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Result<U, E> {
self.prev
.call(world, input)
.and_then(|ref val| self.step.call(world, val))
}
}
#[doc(hidden)]
pub struct DagCatchNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<In, T, E: 'static, Prev, S> ChainCall<In> for DagCatchNode<Prev, S>
where
Prev: ChainCall<In, Out = Result<T, E>>,
S: for<'a> StepCall<&'a E, Out = ()>,
{
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> Option<T> {
match self.prev.call(world, input) {
Ok(val) => Some(val),
Err(ref err) => {
self.step.call(world, err);
None
}
}
}
}
#[doc(hidden)]
pub struct DiscardOptionNode<Prev> {
pub(crate) prev: Prev,
}
impl<In, Prev: ChainCall<In, Out = Option<()>>> ChainCall<In> for DiscardOptionNode<Prev> {
type Out = ();
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) {
let _ = self.prev.call(world, input);
}
}
#[must_use = "a pipeline builder does nothing unless you chain steps and call .build()"]
pub struct PipelineBuilder<In>(PhantomData<fn(In)>);
impl<In> PipelineBuilder<In> {
pub fn new() -> Self {
Self(PhantomData)
}
pub fn then<Out, Params, S: IntoStep<In, Out, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Out, ThenNode<IdentityNode, S::Step>> {
PipelineChain {
chain: ThenNode {
prev: IdentityNode,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
pub fn scan<Acc, Out, Params, S>(
self,
initial: Acc,
f: S,
registry: &Registry,
) -> PipelineChain<In, Out, ScanNode<IdentityNode, S::Step, Acc>>
where
Acc: 'static,
S: IntoScanStep<Acc, In, Out, Params>,
{
PipelineChain {
chain: ScanNode {
prev: IdentityNode,
step: f.into_scan_step(registry),
acc: initial,
},
_marker: PhantomData,
}
}
}
impl<In> Default for PipelineBuilder<In> {
fn default() -> Self {
Self::new()
}
}
#[must_use = "pipeline chain does nothing until .build() is called"]
pub struct PipelineChain<In, Out, Chain> {
pub(crate) chain: Chain,
pub(crate) _marker: PhantomData<fn(In) -> Out>,
}
impl<In, Out, Chain: ChainCall<In, Out = Out>> PipelineChain<In, Out, Chain> {
pub fn then<NewOut, Params, S: IntoStep<Out, NewOut, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, NewOut, ThenNode<Chain, S::Step>> {
PipelineChain {
chain: ThenNode {
prev: self.chain,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
pub fn run(&mut self, world: &mut World, input: In) -> Out {
self.chain.call(world, input)
}
pub fn dispatch<H: Handler<Out>>(
self,
handler: H,
) -> PipelineChain<In, (), DispatchNode<Chain, H>> {
PipelineChain {
chain: DispatchNode {
prev: self.chain,
handler,
},
_marker: PhantomData,
}
}
pub fn guard<Params, S: IntoRefStep<Out, bool, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Option<Out>, GuardNode<Chain, S::Step>> {
PipelineChain {
chain: GuardNode {
prev: self.chain,
step: f.into_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn tap<Params, S: IntoRefStep<Out, (), Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Out, TapNode<Chain, S::Step>> {
PipelineChain {
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: PipelineChain<Out, NewOut, C0>,
on_false: PipelineChain<Out, NewOut, C1>,
) -> PipelineChain<In, NewOut, RouteNode<Chain, Pred::Step, C0, C1>>
where
C0: ChainCall<Out, Out = NewOut>,
C1: ChainCall<Out, Out = NewOut>,
{
PipelineChain {
chain: RouteNode {
prev: self.chain,
pred: pred.into_ref_step(registry),
on_true: on_true.chain,
on_false: on_false.chain,
},
_marker: PhantomData,
}
}
pub fn tee<C>(self, side: DagArm<Out, (), C>) -> PipelineChain<In, Out, TeeNode<Chain, C>>
where
C: for<'a> ChainCall<&'a Out, Out = ()>,
{
PipelineChain {
chain: TeeNode {
prev: self.chain,
side: side.chain,
},
_marker: PhantomData,
}
}
pub fn scan<Acc, NewOut, Params, S>(
self,
initial: Acc,
f: S,
registry: &Registry,
) -> PipelineChain<In, NewOut, ScanNode<Chain, S::Step, Acc>>
where
Acc: 'static,
S: IntoScanStep<Acc, Out, NewOut, Params>,
{
PipelineChain {
chain: ScanNode {
prev: self.chain,
step: f.into_scan_step(registry),
acc: initial,
},
_marker: PhantomData,
}
}
}
macro_rules! define_splat_builders {
(
$N:literal,
start: $SplatStart:ident,
mid: $SplatBuilder:ident,
node: $SplatThenNode:ident,
into_trait: $IntoSplatStep:ident,
call_trait: $SplatCall:ident,
($($T:ident),+),
($($idx:tt),+)
) => {
#[doc(hidden)]
pub struct $SplatThenNode<Prev, S> {
prev: Prev,
step: S,
}
impl<In, $($T,)+ Prev, S> ChainCall<In> for $SplatThenNode<Prev, S>
where
Prev: ChainCall<In, Out = ($($T,)+)>,
S: $SplatCall<$($T),+>,
{
type Out = S::Out;
#[inline(always)]
fn call(&mut self, world: &mut World, input: In) -> S::Out {
let tuple = self.prev.call(world, input);
self.step.call_splat(world, $(tuple.$idx),+)
}
}
#[doc(hidden)]
pub struct $SplatStart<$($T),+>(PhantomData<fn(($($T,)+))>);
impl<$($T),+> $SplatStart<$($T),+> {
pub fn then<Out, Params, S>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<($($T,)+), Out, $SplatThenNode<IdentityNode, S::Step>>
where
S: $IntoSplatStep<$($T,)+ Out, Params>,
{
PipelineChain {
chain: $SplatThenNode {
prev: IdentityNode,
step: f.into_splat_step(registry),
},
_marker: PhantomData,
}
}
}
impl<$($T),+> PipelineBuilder<($($T,)+)> {
pub fn splat(self) -> $SplatStart<$($T),+> {
$SplatStart(PhantomData)
}
}
#[doc(hidden)]
pub struct $SplatBuilder<In, $($T,)+ Chain> {
chain: Chain,
_marker: PhantomData<fn(In) -> ($($T,)+)>,
}
impl<In, $($T,)+ Chain: ChainCall<In, Out = ($($T,)+)>> $SplatBuilder<In, $($T,)+ Chain> {
pub fn then<Out, Params, S>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Out, $SplatThenNode<Chain, S::Step>>
where
S: $IntoSplatStep<$($T,)+ Out, Params>,
{
PipelineChain {
chain: $SplatThenNode {
prev: self.chain,
step: f.into_splat_step(registry),
},
_marker: PhantomData,
}
}
}
impl<In, $($T,)+ Chain: ChainCall<In, Out = ($($T,)+)>> PipelineChain<In, ($($T,)+), Chain> {
pub fn splat(self) -> $SplatBuilder<In, $($T,)+ Chain> {
$SplatBuilder {
chain: self.chain,
_marker: PhantomData,
}
}
}
};
}
define_splat_builders!(2,
start: SplatStart2,
mid: SplatBuilder2,
node: SplatThenNode2,
into_trait: IntoSplatStep2,
call_trait: SplatCall2,
(A, B),
(0, 1)
);
define_splat_builders!(3,
start: SplatStart3,
mid: SplatBuilder3,
node: SplatThenNode3,
into_trait: IntoSplatStep3,
call_trait: SplatCall3,
(A, B, C),
(0, 1, 2)
);
define_splat_builders!(4,
start: SplatStart4,
mid: SplatBuilder4,
node: SplatThenNode4,
into_trait: IntoSplatStep4,
call_trait: SplatCall4,
(A, B, C, D),
(0, 1, 2, 3)
);
define_splat_builders!(5,
start: SplatStart5,
mid: SplatBuilder5,
node: SplatThenNode5,
into_trait: IntoSplatStep5,
call_trait: SplatCall5,
(A, B, C, D, E),
(0, 1, 2, 3, 4)
);
impl<In, Out: PartialEq + Clone, Chain: ChainCall<In, Out = Out>> PipelineChain<In, Out, Chain> {
pub fn dedup(self) -> PipelineChain<In, Option<Out>, DedupNode<Chain, Out>> {
PipelineChain {
chain: DedupNode {
prev: self.chain,
last: None,
},
_marker: PhantomData,
}
}
}
impl<In, Chain: ChainCall<In, Out = bool>> PipelineChain<In, bool, Chain> {
#[allow(clippy::should_implement_trait)]
pub fn not(self) -> PipelineChain<In, bool, NotNode<Chain>> {
PipelineChain {
chain: NotNode { prev: self.chain },
_marker: PhantomData,
}
}
pub fn and<Params, S: IntoProducer<bool, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, bool, AndBoolNode<Chain, S::Step>> {
PipelineChain {
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,
) -> PipelineChain<In, bool, OrBoolNode<Chain, S::Step>> {
PipelineChain {
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,
) -> PipelineChain<In, bool, XorBoolNode<Chain, S::Step>> {
PipelineChain {
chain: XorBoolNode {
prev: self.chain,
producer: f.into_producer(registry),
},
_marker: PhantomData,
}
}
}
impl<'a, In, T: Clone, Chain: ChainCall<In, Out = &'a T>> PipelineChain<In, &'a T, Chain> {
pub fn cloned(self) -> PipelineChain<In, T, ClonedNode<Chain>> {
PipelineChain {
chain: ClonedNode { prev: self.chain },
_marker: PhantomData,
}
}
}
impl<'a, In, T: Clone, Chain: ChainCall<In, Out = Option<&'a T>>>
PipelineChain<In, Option<&'a T>, Chain>
{
pub fn cloned(self) -> PipelineChain<In, Option<T>, ClonedOptionNode<Chain>> {
PipelineChain {
chain: ClonedOptionNode { prev: self.chain },
_marker: PhantomData,
}
}
}
impl<'a, In, T: Clone, E, Chain: ChainCall<In, Out = Result<&'a T, E>>>
PipelineChain<In, Result<&'a T, E>, Chain>
{
pub fn cloned(self) -> PipelineChain<In, Result<T, E>, ClonedResultNode<Chain>> {
PipelineChain {
chain: ClonedResultNode { prev: self.chain },
_marker: PhantomData,
}
}
}
impl<In, T, Chain: ChainCall<In, Out = Option<T>>> PipelineChain<In, Option<T>, Chain> {
pub fn map<U, Params, S: IntoStep<T, U, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Option<U>, MapOptionNode<Chain, S::Step>> {
PipelineChain {
chain: MapOptionNode {
prev: self.chain,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
pub fn and_then<U, Params, S: IntoStep<T, Option<U>, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Option<U>, AndThenNode<Chain, S::Step>> {
PipelineChain {
chain: AndThenNode {
prev: self.chain,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
pub fn on_none<Params, S: IntoProducer<(), Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Option<T>, OnNoneNode<Chain, S::Step>> {
PipelineChain {
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,
) -> PipelineChain<In, Option<T>, FilterNode<Chain, S::Step>> {
PipelineChain {
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,
) -> PipelineChain<In, Option<T>, InspectOptionNode<Chain, S::Step>> {
PipelineChain {
chain: InspectOptionNode {
prev: self.chain,
step: f.into_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn ok_or<E: Clone>(self, err: E) -> PipelineChain<In, Result<T, E>, OkOrNode<Chain, E>> {
PipelineChain {
chain: OkOrNode {
prev: self.chain,
err,
},
_marker: PhantomData,
}
}
pub fn ok_or_else<E, Params, S: IntoProducer<E, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Result<T, E>, OkOrElseNode<Chain, S::Step>> {
PipelineChain {
chain: OkOrElseNode {
prev: self.chain,
producer: f.into_producer(registry),
},
_marker: PhantomData,
}
}
pub fn unwrap_or(self, default: T) -> PipelineChain<In, T, UnwrapOrOptionNode<Chain, T>>
where
T: Clone,
{
PipelineChain {
chain: UnwrapOrOptionNode {
prev: self.chain,
default,
},
_marker: PhantomData,
}
}
pub fn unwrap_or_else<Params, S: IntoProducer<T, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, T, UnwrapOrElseOptionNode<Chain, S::Step>> {
PipelineChain {
chain: UnwrapOrElseOptionNode {
prev: self.chain,
producer: f.into_producer(registry),
},
_marker: PhantomData,
}
}
}
impl<In, T, E, Chain: ChainCall<In, Out = Result<T, E>>> PipelineChain<In, Result<T, E>, Chain> {
pub fn map<U, Params, S: IntoStep<T, U, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Result<U, E>, MapResultNode<Chain, S::Step>> {
PipelineChain {
chain: MapResultNode {
prev: self.chain,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
pub fn and_then<U, Params, S: IntoStep<T, Result<U, E>, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Result<U, E>, AndThenResultNode<Chain, S::Step>> {
PipelineChain {
chain: AndThenResultNode {
prev: self.chain,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
pub fn catch<Params, S: IntoStep<E, (), Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Option<T>, CatchNode<Chain, S::Step>> {
PipelineChain {
chain: CatchNode {
prev: self.chain,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
pub fn map_err<E2, Params, S: IntoStep<E, E2, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Result<T, E2>, MapErrNode<Chain, S::Step>> {
PipelineChain {
chain: MapErrNode {
prev: self.chain,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
pub fn or_else<E2, Params, S: IntoStep<E, Result<T, E2>, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Result<T, E2>, OrElseNode<Chain, S::Step>> {
PipelineChain {
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,
) -> PipelineChain<In, Result<T, E>, InspectResultNode<Chain, S::Step>> {
PipelineChain {
chain: InspectResultNode {
prev: self.chain,
step: f.into_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn inspect_err<Params, S: IntoRefStep<E, (), Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, Result<T, E>, InspectErrNode<Chain, S::Step>> {
PipelineChain {
chain: InspectErrNode {
prev: self.chain,
step: f.into_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn ok(self) -> PipelineChain<In, Option<T>, OkResultNode<Chain>> {
PipelineChain {
chain: OkResultNode { prev: self.chain },
_marker: PhantomData,
}
}
pub fn unwrap_or(self, default: T) -> PipelineChain<In, T, UnwrapOrResultNode<Chain, T>>
where
T: Clone,
{
PipelineChain {
chain: UnwrapOrResultNode {
prev: self.chain,
default,
},
_marker: PhantomData,
}
}
pub fn unwrap_or_else<Params, S: IntoStep<E, T, Params>>(
self,
f: S,
registry: &Registry,
) -> PipelineChain<In, T, UnwrapOrElseResultNode<Chain, S::Step>> {
PipelineChain {
chain: UnwrapOrElseResultNode {
prev: self.chain,
step: f.into_step(registry),
},
_marker: PhantomData,
}
}
}
mod pipeline_output_seal {
pub trait Sealed {}
impl Sealed for () {}
impl Sealed for Option<()> {}
}
#[diagnostic::on_unimplemented(
message = "`build()` requires the pipeline output to be `()` or `Option<()>`",
label = "this pipeline produces `{Self}`, not `()` or `Option<()>`",
note = "add a final `.then()` or `.dispatch()` that consumes the output"
)]
pub trait PipelineOutput: pipeline_output_seal::Sealed {}
impl PipelineOutput for () {}
impl PipelineOutput for Option<()> {}
impl<In, Chain: ChainCall<In, Out = ()>> PipelineChain<In, (), Chain> {
#[must_use = "building a pipeline without storing it does nothing"]
pub fn build(self) -> Pipeline<Chain> {
Pipeline { chain: self.chain }
}
}
impl<In, Chain: ChainCall<In, Out = Option<()>>> PipelineChain<In, Option<()>, Chain> {
#[must_use = "building a pipeline without storing it does nothing"]
pub fn build(self) -> Pipeline<DiscardOptionNode<Chain>> {
Pipeline {
chain: DiscardOptionNode { prev: self.chain },
}
}
}
impl<In, Out: PipelineOutput, Chain: ChainCall<In, Out = Out>> PipelineChain<In, Out, Chain> {
#[must_use = "building a pipeline without storing it does nothing"]
pub fn build_batch(self, capacity: usize) -> BatchPipeline<In, Chain> {
BatchPipeline {
input: Vec::with_capacity(capacity),
chain: self.chain,
}
}
}
pub struct Pipeline<F> {
chain: F,
}
impl<E, F: ChainCall<E, Out = ()> + Send> crate::Handler<E> for Pipeline<F> {
fn run(&mut self, world: &mut World, event: E) {
self.chain.call(world, event);
}
}
pub struct BatchPipeline<In, F> {
input: Vec<In>,
chain: F,
}
impl<In, Out: PipelineOutput, F: ChainCall<In, Out = Out>> BatchPipeline<In, F> {
pub fn input_mut(&mut self) -> &mut Vec<In> {
&mut self.input
}
pub fn input(&self) -> &[In] {
&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_step<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<In, Out, Params>,
{
let mut resolved = f.into_step(registry);
move |world: &mut World, input: In| resolved.call(world, input)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Handler, IntoHandler, Local, Res, ResMut, WorldBuilder, fan_out};
#[test]
fn step_pure_transform() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new().then(|x: u32| x as u64 * 2, r);
assert_eq!(p.run(&mut world, 5), 10u64);
}
#[test]
fn step_one_res() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(10);
let mut world = wb.build();
fn multiply(factor: Res<u64>, x: u32) -> u64 {
*factor * x as u64
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new().then(multiply, r);
assert_eq!(p.run(&mut world, 5), 50);
}
#[test]
fn step_one_res_mut() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn accumulate(mut total: ResMut<u64>, x: u32) {
*total += x as u64;
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new().then(accumulate, r);
p.run(&mut world, 10);
p.run(&mut world, 5);
assert_eq!(*world.resource::<u64>(), 15);
}
#[test]
fn step_two_params() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(10);
wb.register::<bool>(true);
let mut world = wb.build();
fn conditional(factor: Res<u64>, flag: Res<bool>, x: u32) -> u64 {
if *flag { *factor * x as u64 } else { 0 }
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new().then(conditional, r);
assert_eq!(p.run(&mut world, 5), 50);
}
#[test]
fn step_chain_two() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(2);
let mut world = wb.build();
fn double(factor: Res<u64>, x: u32) -> u64 {
*factor * x as u64
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(double, r)
.then(|val: u64| val + 1, r);
assert_eq!(p.run(&mut world, 5), 11); }
#[test]
fn option_map_on_some() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(10);
let mut world = wb.build();
fn add_factor(factor: Res<u64>, x: u32) -> u64 {
*factor + x as u64
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| -> Option<u32> { Some(x) }, r)
.map(add_factor, r);
assert_eq!(p.run(&mut world, 5), Some(15));
}
#[test]
fn option_map_skips_none() {
let mut wb = WorldBuilder::new();
wb.register::<bool>(false);
let mut world = wb.build();
fn mark(mut flag: ResMut<bool>, _x: u32) -> u32 {
*flag = true;
0
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Option<u32> { None }, r)
.map(mark, r);
assert_eq!(p.run(&mut world, 5), None);
assert!(!*world.resource::<bool>());
}
#[test]
fn option_and_then_chains() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(10);
let mut world = wb.build();
fn check(min: Res<u64>, x: u32) -> Option<u64> {
let val = x as u64;
if val > *min { Some(val) } else { None }
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| Some(x), r)
.and_then(check, r);
assert_eq!(p.run(&mut world, 20), Some(20));
}
#[test]
fn option_and_then_short_circuits() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(10);
let mut world = wb.build();
fn check(min: Res<u64>, x: u32) -> Option<u64> {
let val = x as u64;
if val > *min { Some(val) } else { None }
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| Some(x), r)
.and_then(check, r);
assert_eq!(p.run(&mut world, 5), None);
}
#[test]
fn option_on_none_fires() {
let mut wb = WorldBuilder::new();
wb.register::<bool>(false);
let mut world = wb.build();
let r = world.registry();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Option<u32> { None }, r)
.on_none(
|w: &mut World| {
*w.resource_mut::<bool>() = true;
},
r,
);
p.run(&mut world, 0);
assert!(*world.resource::<bool>());
}
#[test]
fn option_filter_keeps() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| Some(x), r)
.filter(|x: &u32| *x > 3, r);
assert_eq!(p.run(&mut world, 5), Some(5));
}
#[test]
fn option_filter_drops() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| Some(x), r)
.filter(|x: &u32| *x > 10, r);
assert_eq!(p.run(&mut world, 5), None);
}
#[test]
fn result_map_on_ok() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(10);
let mut world = wb.build();
fn add_factor(factor: Res<u64>, x: u32) -> u64 {
*factor + x as u64
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| -> Result<u32, String> { Ok(x) }, r)
.map(add_factor, r);
assert_eq!(p.run(&mut world, 5), Ok(15));
}
#[test]
fn result_map_skips_err() {
let mut wb = WorldBuilder::new();
wb.register::<bool>(false);
let mut world = wb.build();
fn mark(mut flag: ResMut<bool>, _x: u32) -> u32 {
*flag = true;
0
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Result<u32, String> { Err("fail".into()) }, r)
.map(mark, r);
assert!(p.run(&mut world, 5).is_err());
assert!(!*world.resource::<bool>());
}
#[test]
fn result_catch_handles_error() {
let mut wb = WorldBuilder::new();
wb.register::<String>(String::new());
let mut world = wb.build();
fn log_error(mut log: ResMut<String>, err: String) {
*log = err;
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Result<u32, String> { Err("caught".into()) }, r)
.catch(log_error, r);
assert_eq!(p.run(&mut world, 0), None);
assert_eq!(world.resource::<String>().as_str(), "caught");
}
#[test]
fn result_catch_passes_ok() {
let mut wb = WorldBuilder::new();
wb.register::<String>(String::new());
let mut world = wb.build();
fn log_error(mut log: ResMut<String>, err: String) {
*log = err;
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| -> Result<u32, String> { Ok(x) }, r)
.catch(log_error, r);
assert_eq!(p.run(&mut world, 5), Some(5));
assert!(world.resource::<String>().is_empty());
}
#[test]
fn build_produces_handler() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn accumulate(mut total: ResMut<u64>, x: u32) {
*total += x as u64;
}
let r = world.registry_mut();
let mut pipeline = PipelineBuilder::<u32>::new().then(accumulate, r).build();
pipeline.run(&mut world, 10);
pipeline.run(&mut world, 5);
assert_eq!(*world.resource::<u64>(), 15);
}
#[test]
fn run_returns_output() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(3);
let mut world = wb.build();
fn multiply(factor: Res<u64>, x: u32) -> u64 {
*factor * x as u64
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new().then(multiply, r);
let result: u64 = p.run(&mut world, 7);
assert_eq!(result, 21);
}
#[test]
#[should_panic(expected = "not registered")]
fn panics_on_missing_resource() {
let mut world = WorldBuilder::new().build();
fn needs_u64(_val: Res<u64>, _x: u32) -> u32 {
0
}
let r = world.registry_mut();
let _p = PipelineBuilder::<u32>::new().then(needs_u64, r);
}
#[test]
#[should_panic(expected = "conflicting access")]
fn step_duplicate_access_panics() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn bad(a: Res<u64>, b: ResMut<u64>, _x: u32) -> u32 {
let _ = (*a, &*b);
0
}
let r = world.registry_mut();
let _p = PipelineBuilder::<u32>::new().then(bad, r);
}
#[test]
fn local_in_step() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn count(mut count: Local<u64>, mut total: ResMut<u64>, _x: u32) {
*count += 1;
*total = *count;
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new().then(count, r);
p.run(&mut world, 0);
p.run(&mut world, 0);
p.run(&mut world, 0);
assert_eq!(*world.resource::<u64>(), 3);
}
#[test]
fn option_unwrap_or_some() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| -> Option<u32> { Some(x) }, r)
.unwrap_or(99);
assert_eq!(p.run(&mut world, 5), 5);
}
#[test]
fn option_unwrap_or_none() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Option<u32> { None }, r)
.unwrap_or(99);
assert_eq!(p.run(&mut world, 5), 99);
}
#[test]
fn option_unwrap_or_else() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Option<u32> { None }, r)
.unwrap_or_else(|| 42, r);
assert_eq!(p.run(&mut world, 0), 42);
}
#[test]
fn option_ok_or() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Option<u32> { None }, r)
.ok_or("missing");
assert_eq!(p.run(&mut world, 0), Err("missing"));
}
#[test]
fn option_ok_or_some() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| -> Option<u32> { Some(x) }, r)
.ok_or("missing");
assert_eq!(p.run(&mut world, 7), Ok(7));
}
#[test]
fn option_ok_or_else() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Option<u32> { None }, r)
.ok_or_else(|| "computed", r);
assert_eq!(p.run(&mut world, 0), Err("computed"));
}
#[test]
fn option_inspect_passes_through() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| -> Option<u32> { Some(x) }, r)
.inspect(|_val: &u32| {}, r);
assert_eq!(p.run(&mut world, 10), Some(10));
}
#[test]
fn result_map_err() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Result<u32, i32> { Err(-1) }, r)
.map_err(|e: i32| e.to_string(), r);
assert_eq!(p.run(&mut world, 0), Err("-1".to_string()));
}
#[test]
fn result_map_err_ok_passthrough() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| -> Result<u32, i32> { Ok(x) }, r)
.map_err(|e: i32| e.to_string(), r);
assert_eq!(p.run(&mut world, 5), Ok(5));
}
#[test]
fn result_or_else() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Result<u32, &str> { Err("fail") }, r)
.or_else(|_e: &str| Ok::<u32, &str>(42), r);
assert_eq!(p.run(&mut world, 0), Ok(42));
}
#[test]
fn result_inspect_passes_through() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| -> Result<u32, &str> { Ok(x) }, r)
.inspect(|_val: &u32| {}, r);
assert_eq!(p.run(&mut world, 7), Ok(7));
}
#[test]
fn result_inspect_err_passes_through() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Result<u32, &str> { Err("bad") }, r)
.inspect_err(|_e: &&str| {}, r);
assert_eq!(p.run(&mut world, 0), Err("bad"));
}
#[test]
fn result_ok_converts() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| -> Result<u32, &str> { Ok(x) }, r)
.ok();
assert_eq!(p.run(&mut world, 5), Some(5));
}
#[test]
fn result_ok_drops_err() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Result<u32, &str> { Err("gone") }, r)
.ok();
assert_eq!(p.run(&mut world, 0), None);
}
#[test]
fn result_unwrap_or() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Result<u32, &str> { Err("x") }, r)
.unwrap_or(99);
assert_eq!(p.run(&mut world, 0), 99);
}
#[test]
fn result_unwrap_or_else() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| -> Result<u32, i32> { Err(-5) }, r)
.unwrap_or_else(|e: i32| e.unsigned_abs(), r);
assert_eq!(p.run(&mut world, 0), 5);
}
#[test]
fn batch_accumulates() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn accumulate(mut sum: ResMut<u64>, x: u32) {
*sum += x as u64;
}
let r = world.registry_mut();
let mut batch = PipelineBuilder::<u32>::new()
.then(accumulate, r)
.build_batch(16);
batch.input_mut().extend_from_slice(&[1, 2, 3, 4, 5]);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 15);
assert!(batch.input().is_empty());
}
#[test]
fn batch_retains_allocation() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut batch = PipelineBuilder::<u32>::new()
.then(|_x: u32| {}, r)
.build_batch(64);
batch.input_mut().extend_from_slice(&[1, 2, 3]);
batch.run(&mut world);
assert!(batch.input().is_empty());
assert!(batch.input_mut().capacity() >= 64);
}
#[test]
fn batch_empty_is_noop() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn accumulate(mut sum: ResMut<u64>, x: u32) {
*sum += x as u64;
}
let r = world.registry_mut();
let mut batch = PipelineBuilder::<u32>::new()
.then(accumulate, r)
.build_batch(16);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 0);
}
#[test]
fn batch_catch_continues_on_error() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
wb.register::<u32>(0);
let mut world = wb.build();
fn validate(x: u32) -> Result<u32, &'static str> {
if x > 0 { Ok(x) } else { Err("zero") }
}
fn count_errors(mut errs: ResMut<u32>, _err: &'static str) {
*errs += 1;
}
fn accumulate(mut sum: ResMut<u64>, x: u32) {
*sum += x as u64;
}
let r = world.registry_mut();
let mut batch = PipelineBuilder::<u32>::new()
.then(validate, r)
.catch(count_errors, r)
.map(accumulate, r)
.build_batch(16);
batch.input_mut().extend_from_slice(&[1, 0, 2, 0, 3]);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 6); assert_eq!(*world.resource::<u32>(), 2); }
#[test]
fn batch_filter_skips_items() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn accumulate(mut sum: ResMut<u64>, x: u32) {
*sum += x as u64;
}
let r = world.registry_mut();
let mut batch = PipelineBuilder::<u32>::new()
.then(
|x: u32| -> Option<u32> { if x > 2 { Some(x) } else { None } },
r,
)
.map(accumulate, r)
.build_batch(16);
batch.input_mut().extend_from_slice(&[1, 2, 3, 4, 5]);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 12); }
#[test]
fn batch_multiple_runs_accumulate() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn accumulate(mut sum: ResMut<u64>, x: u32) {
*sum += x as u64;
}
let r = world.registry_mut();
let mut batch = PipelineBuilder::<u32>::new()
.then(accumulate, r)
.build_batch(16);
batch.input_mut().extend_from_slice(&[1, 2, 3]);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 6);
batch.input_mut().extend_from_slice(&[4, 5]);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 15);
}
#[test]
fn batch_with_world_access() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(10); wb.register::<Vec<u64>>(Vec::new());
let mut world = wb.build();
fn multiply_and_collect(factor: Res<u64>, mut out: ResMut<Vec<u64>>, x: u32) {
out.push(x as u64 * *factor);
}
let r = world.registry_mut();
let mut batch = PipelineBuilder::<u32>::new()
.then(multiply_and_collect, r)
.build_batch(16);
batch.input_mut().extend_from_slice(&[1, 2, 3]);
batch.run(&mut world);
assert_eq!(world.resource::<Vec<u64>>().as_slice(), &[10, 20, 30]);
}
fn ref_identity(x: &u32) -> &u32 {
x
}
#[allow(clippy::unnecessary_wraps)]
fn ref_wrap_some(x: &u32) -> Option<&u32> {
Some(x)
}
fn ref_wrap_none(_x: &u32) -> Option<&u32> {
None
}
#[allow(clippy::unnecessary_wraps)]
fn ref_wrap_ok(x: &u32) -> Result<&u32, String> {
Ok(x)
}
fn ref_wrap_err(_x: &u32) -> Result<&u32, String> {
Err("fail".into())
}
#[test]
fn cloned_bare() {
let mut world = WorldBuilder::new().build();
let val = 42u32;
let r = world.registry_mut();
let mut p = PipelineBuilder::<&u32>::new()
.then(ref_identity, r)
.cloned();
assert_eq!(p.run(&mut world, &val), 42u32);
}
#[test]
fn cloned_option_some() {
let mut world = WorldBuilder::new().build();
let val = 42u32;
let r = world.registry_mut();
let mut p = PipelineBuilder::<&u32>::new()
.then(ref_wrap_some, r)
.cloned();
assert_eq!(p.run(&mut world, &val), Some(42u32));
}
#[test]
fn cloned_option_none() {
let mut world = WorldBuilder::new().build();
let val = 42u32;
let r = world.registry_mut();
let mut p = PipelineBuilder::<&u32>::new()
.then(ref_wrap_none, r)
.cloned();
assert_eq!(p.run(&mut world, &val), None);
}
#[test]
fn cloned_result_ok() {
let mut world = WorldBuilder::new().build();
let val = 42u32;
let r = world.registry_mut();
let mut p = PipelineBuilder::<&u32>::new().then(ref_wrap_ok, r).cloned();
assert_eq!(p.run(&mut world, &val), Ok(42u32));
}
#[test]
fn cloned_result_err() {
let mut world = WorldBuilder::new().build();
let val = 42u32;
let r = world.registry_mut();
let mut p = PipelineBuilder::<&u32>::new()
.then(ref_wrap_err, r)
.cloned();
assert_eq!(p.run(&mut world, &val), Err("fail".into()));
}
#[test]
fn dispatch_to_handler() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn store(mut out: ResMut<u64>, val: u32) {
*out = val as u64;
}
let r = world.registry_mut();
let handler = PipelineBuilder::<u32>::new().then(store, r).build();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| x * 2, r)
.dispatch(handler)
.build();
p.run(&mut world, 5);
assert_eq!(*world.resource::<u64>(), 10);
}
#[test]
fn dispatch_to_fanout() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
wb.register::<i64>(0);
let mut world = wb.build();
fn write_u64(mut sink: ResMut<u64>, event: &u32) {
*sink += *event as u64;
}
fn write_i64(mut sink: ResMut<i64>, event: &u32) {
*sink += *event as i64;
}
let h1 = write_u64.into_handler(world.registry());
let h2 = write_i64.into_handler(world.registry());
let fan = fan_out!(h1, h2);
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| x * 2, r)
.dispatch(fan)
.build();
p.run(&mut world, 5);
assert_eq!(*world.resource::<u64>(), 10);
assert_eq!(*world.resource::<i64>(), 10);
}
#[test]
fn dispatch_to_broadcast() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn write_u64(mut sink: ResMut<u64>, event: &u32) {
*sink += *event as u64;
}
let mut broadcast = crate::Broadcast::<u32>::new();
broadcast.add(write_u64.into_handler(world.registry()));
broadcast.add(write_u64.into_handler(world.registry()));
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| x + 1, r)
.dispatch(broadcast)
.build();
p.run(&mut world, 4);
assert_eq!(*world.resource::<u64>(), 10); }
#[test]
fn dispatch_build_produces_handler() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn store(mut out: ResMut<u64>, val: u32) {
*out = val as u64;
}
let r = world.registry_mut();
let inner = PipelineBuilder::<u32>::new().then(store, r).build();
let mut pipeline: Box<dyn Handler<u32>> = Box::new(
PipelineBuilder::<u32>::new()
.then(|x: u32| x + 1, r)
.dispatch(inner)
.build(),
);
pipeline.run(&mut world, 9);
assert_eq!(*world.resource::<u64>(), 10);
}
#[test]
fn pipeline_guard_keeps() {
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 p = PipelineBuilder::<u32>::new()
.then(|x: u32| x as u64, reg)
.guard(|v: &u64| *v > 3, reg)
.then(sink, reg);
p.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 5);
}
#[test]
fn pipeline_guard_drops() {
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 p = PipelineBuilder::<u32>::new()
.then(|x: u32| x as u64, reg)
.guard(|v: &u64| *v > 10, reg)
.then(sink, reg);
p.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 999);
}
#[test]
fn pipeline_tap_observes_without_changing() {
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 p = PipelineBuilder::<u32>::new()
.then(|x: u32| x as u64 * 2, reg)
.tap(
|w: &mut World, val: &u64| {
*w.resource_mut::<bool>() = *val == 10;
},
reg,
)
.then(sink, reg);
p.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 10); assert!(*world.resource::<bool>()); }
#[test]
fn pipeline_route_true_arm() {
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 = PipelineBuilder::new().then(|x: u64| x * 2, reg);
let arm_f = PipelineBuilder::new().then(|x: u64| x * 3, reg);
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| x as u64, reg)
.route(|v: &u64| *v > 3, reg, arm_t, arm_f)
.then(sink, reg);
p.run(&mut world, 5u32); assert_eq!(*world.resource::<u64>(), 10);
}
#[test]
fn pipeline_route_false_arm() {
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 = PipelineBuilder::new().then(|x: u64| x * 2, reg);
let arm_f = PipelineBuilder::new().then(|x: u64| x * 3, reg);
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| x as u64, reg)
.route(|v: &u64| *v > 10, reg, arm_t, arm_f)
.then(sink, reg);
p.run(&mut world, 5u32); assert_eq!(*world.resource::<u64>(), 15);
}
#[test]
fn pipeline_route_nested() {
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 = PipelineBuilder::new().then(|x: u64| x + 200, reg);
let inner_f = PipelineBuilder::new().then(|x: u64| x + 300, reg);
let outer_t = PipelineBuilder::new().then(|x: u64| x + 100, reg);
let outer_f = PipelineBuilder::new().then(|x: u64| x, reg).route(
|v: &u64| *v < 10,
reg,
inner_t,
inner_f,
);
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| x as u64, reg)
.route(|v: &u64| *v < 5, reg, outer_t, outer_f)
.then(sink, reg);
p.run(&mut world, 3u32); assert_eq!(*world.resource::<u64>(), 103);
p.run(&mut world, 7u32); assert_eq!(*world.resource::<u64>(), 207);
p.run(&mut world, 15u32); assert_eq!(*world.resource::<u64>(), 315);
}
#[test]
fn pipeline_tee_side_effect_chain() {
use crate::dag::DagArmSeed;
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 p = PipelineBuilder::<u32>::new()
.then(|x: u32| x as u64 * 2, reg)
.tee(side)
.then(sink, reg);
p.run(&mut world, 5u32);
assert_eq!(*world.resource::<u64>(), 10); assert_eq!(*world.resource::<u32>(), 1);
p.run(&mut world, 7u32);
assert_eq!(*world.resource::<u64>(), 14);
assert_eq!(*world.resource::<u32>(), 2);
}
#[test]
fn pipeline_dedup_suppresses_unchanged() {
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 p = PipelineBuilder::<u32>::new()
.then(|x: u32| x as u64 / 2, reg)
.dedup()
.then(sink, reg);
p.run(&mut world, 4u32); assert_eq!(*world.resource::<u32>(), 1);
p.run(&mut world, 5u32); assert_eq!(*world.resource::<u32>(), 1);
p.run(&mut world, 6u32); assert_eq!(*world.resource::<u32>(), 2);
}
#[test]
fn pipeline_not() {
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 p = PipelineBuilder::<u32>::new()
.then(|x: u32| x > 5, reg)
.not()
.then(sink, reg);
p.run(&mut world, 3u32); assert!(*world.resource::<bool>());
p.run(&mut world, 10u32); assert!(!*world.resource::<bool>());
}
#[test]
fn pipeline_and() {
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 p = PipelineBuilder::<u32>::new()
.then(|x: u32| x > 5, reg)
.and(|w: &mut World| *w.resource::<bool>(), reg)
.then(sink, reg);
p.run(&mut world, 10u32); assert!(*world.resource::<bool>());
*world.resource_mut::<bool>() = false;
p.run(&mut world, 10u32); assert!(!*world.resource::<bool>());
}
#[test]
fn pipeline_or() {
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 p = PipelineBuilder::<u32>::new()
.then(|x: u32| x > 5, reg)
.or(|w: &mut World| *w.resource::<bool>(), reg)
.then(sink, reg);
p.run(&mut world, 3u32); assert!(!*world.resource::<bool>());
*world.resource_mut::<bool>() = true;
p.run(&mut world, 3u32); assert!(*world.resource::<bool>());
}
#[test]
fn pipeline_xor() {
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 p = PipelineBuilder::<u32>::new()
.then(|x: u32| x > 5, reg)
.xor(|w: &mut World| *w.resource::<bool>(), reg)
.then(sink, reg);
p.run(&mut world, 10u32); assert!(!*world.resource::<bool>());
}
#[test]
fn splat2_closure_on_start() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<(u32, u64)>::new()
.splat()
.then(|a: u32, b: u64| a as u64 + b, r);
assert_eq!(p.run(&mut world, (3, 7)), 10);
}
#[test]
fn splat2_named_fn_with_param() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(100);
let mut world = wb.build();
fn process(base: Res<u64>, a: u32, b: u32) -> u64 {
*base + a as u64 + b as u64
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<(u32, u32)>::new()
.splat()
.then(process, r);
assert_eq!(p.run(&mut world, (3, 7)), 110);
}
#[test]
fn splat2_mid_chain() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| (x, x * 2), r)
.splat()
.then(|a: u32, b: u32| a as u64 + b as u64, r);
assert_eq!(p.run(&mut world, 5), 15); }
#[test]
fn splat3_closure_on_start() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<(u32, u32, u32)>::new()
.splat()
.then(|a: u32, b: u32, c: u32| a + b + c, r);
assert_eq!(p.run(&mut world, (1, 2, 3)), 6);
}
#[test]
fn splat3_named_fn_with_param() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(10);
let mut world = wb.build();
fn process(factor: Res<u64>, a: u32, b: u32, c: u32) -> u64 {
*factor * (a + b + c) as u64
}
let r = world.registry_mut();
let mut p = PipelineBuilder::<(u32, u32, u32)>::new()
.splat()
.then(process, r);
assert_eq!(p.run(&mut world, (1, 2, 3)), 60);
}
#[test]
fn splat4_mid_chain() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| (x, x + 1, x + 2, x + 3), r)
.splat()
.then(|a: u32, b: u32, c: u32, d: u32| (a + b + c + d) as u64, r);
assert_eq!(p.run(&mut world, 10), 46); }
#[test]
fn splat5_closure_on_start() {
let mut world = WorldBuilder::new().build();
let r = world.registry_mut();
let mut p = PipelineBuilder::<(u8, u8, u8, u8, u8)>::new().splat().then(
|a: u8, b: u8, c: u8, d: u8, e: u8| {
(a as u64) + (b as u64) + (c as u64) + (d as u64) + (e as u64)
},
r,
);
assert_eq!(p.run(&mut world, (1, 2, 3, 4, 5)), 15);
}
#[test]
fn splat_build_into_handler() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn store(mut out: ResMut<u64>, a: u32, b: u32) {
*out = a as u64 + b as u64;
}
let r = world.registry_mut();
let mut pipeline = PipelineBuilder::<(u32, u32)>::new()
.splat()
.then(store, r)
.build();
pipeline.run(&mut world, (3, 7));
assert_eq!(*world.resource::<u64>(), 10);
}
#[test]
fn splat_build_batch() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn accumulate(mut sum: ResMut<u64>, a: u32, b: u32) {
*sum += a as u64 + b as u64;
}
let r = world.registry_mut();
let mut batch = PipelineBuilder::<(u32, u32)>::new()
.splat()
.then(accumulate, r)
.build_batch(8);
batch
.input_mut()
.extend_from_slice(&[(1, 2), (3, 4), (5, 6)]);
batch.run(&mut world);
assert_eq!(*world.resource::<u64>(), 21); }
#[test]
#[should_panic(expected = "conflicting access")]
fn splat_access_conflict_detected() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn bad(a: ResMut<u64>, _b: ResMut<u64>, _x: u32, _y: u32) {
let _ = a;
}
let r = world.registry_mut();
let _ = PipelineBuilder::<(u32, u32)>::new().splat().then(bad, r);
}
#[test]
fn pipeline_then_branching() {
fn double(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);
let mut world = wb.build();
let reg = world.registry();
let mut pipeline = PipelineBuilder::<u32>::new()
.then(double, reg)
.then(|val: u64| if val > 10 { val * 100 } else { val + 1 }, reg)
.then(sink, reg)
.build();
pipeline.run(&mut world, 10u32); assert_eq!(*world.resource::<u64>(), 2000);
pipeline.run(&mut world, 3u32); assert_eq!(*world.resource::<u64>(), 7);
}
#[test]
fn pipeline_then_3_way() {
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 pipeline = PipelineBuilder::<u32>::new()
.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();
pipeline.run(&mut world, 6u32); assert_eq!(*world.resource::<u64>(), 106);
pipeline.run(&mut world, 7u32); assert_eq!(*world.resource::<u64>(), 207);
pipeline.run(&mut world, 8u32); assert_eq!(*world.resource::<u64>(), 308);
}
#[test]
fn pipeline_then_with_resolve_step() {
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_step(add_offset, reg);
let mut arm_double = resolve_step(plain_double, reg);
let mut pipeline = PipelineBuilder::<u32>::new()
.then(
move |world: &mut World, val: u32| {
if val > 10 {
arm_offset(world, val)
} else {
arm_double(world, val)
}
},
reg,
)
.then(sink, reg)
.build();
pipeline.run(&mut world, 20u32); assert_eq!(*world.resource::<u64>(), 120);
pipeline.run(&mut world, 5u32); assert_eq!(*world.resource::<u64>(), 10);
}
#[test]
fn batch_pipeline_then_branching() {
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 = PipelineBuilder::<u32>::new()
.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 guard_named_fn_with_param() {
fn above_threshold(threshold: Res<u64>, val: &u64) -> bool {
*val > *threshold
}
fn sink(mut out: ResMut<i64>, val: Option<u64>) {
*out = val.map_or(-1, |v| v as i64);
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(5); wb.register::<i64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| x as u64, reg)
.guard(above_threshold, reg)
.then(sink, reg);
p.run(&mut world, 10u32); assert_eq!(*world.resource::<i64>(), 10);
p.run(&mut world, 3u32); assert_eq!(*world.resource::<i64>(), -1);
}
#[test]
fn filter_named_fn_with_param() {
fn is_allowed(allowed: Res<u64>, val: &u64) -> bool {
*val != *allowed
}
fn count(mut ctr: ResMut<i64>, _val: u64) {
*ctr += 1;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(42); wb.register::<i64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| -> Option<u64> { Some(x as u64) }, reg)
.filter(is_allowed, reg)
.map(count, reg)
.unwrap_or(());
for v in [1u32, 42, 5, 42, 10] {
p.run(&mut world, v);
}
assert_eq!(*world.resource::<i64>(), 3); }
#[test]
fn inspect_named_fn_with_param() {
fn log_value(mut log: ResMut<Vec<u64>>, val: &u64) {
log.push(*val);
}
let mut wb = WorldBuilder::new();
wb.register::<Vec<u64>>(Vec::new());
let mut world = wb.build();
let reg = world.registry();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| -> Option<u64> { Some(x as u64) }, reg)
.inspect(log_value, reg)
.unwrap_or(0);
for v in [1u32, 2, 3] {
p.run(&mut world, v);
}
assert_eq!(world.resource::<Vec<u64>>().as_slice(), &[1, 2, 3]);
}
#[test]
fn tap_named_fn_with_param() {
fn observe(mut log: ResMut<Vec<u64>>, val: &u64) {
log.push(*val);
}
fn sink(mut out: ResMut<u64>, val: u64) {
*out = val;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
wb.register::<Vec<u64>>(Vec::new());
let mut world = wb.build();
let reg = world.registry();
let mut p = PipelineBuilder::<u32>::new()
.then(|x: u32| x as u64, reg)
.tap(observe, reg)
.then(sink, reg);
p.run(&mut world, 7u32);
assert_eq!(*world.resource::<u64>(), 7);
assert_eq!(world.resource::<Vec<u64>>().as_slice(), &[7]);
}
#[test]
fn and_named_fn_with_param() {
fn check_enabled(flag: Res<bool>) -> bool {
*flag
}
let mut wb = WorldBuilder::new();
wb.register::<bool>(true);
let mut world = wb.build();
let reg = world.registry();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| true, reg)
.and(check_enabled, reg);
assert!(p.run(&mut world, 0u32));
*world.resource_mut::<bool>() = false;
assert!(!p.run(&mut world, 0u32)); }
#[test]
fn or_named_fn_with_param() {
fn check_enabled(flag: Res<bool>) -> bool {
*flag
}
let mut wb = WorldBuilder::new();
wb.register::<bool>(true);
let mut world = wb.build();
let reg = world.registry();
let mut p = PipelineBuilder::<u32>::new()
.then(|_x: u32| false, reg)
.or(check_enabled, reg);
assert!(p.run(&mut world, 0u32));
*world.resource_mut::<bool>() = false;
assert!(!p.run(&mut world, 0u32)); }
#[test]
fn on_none_named_fn_with_param() {
fn log_miss(mut ctr: ResMut<u64>) {
*ctr += 1;
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
let mut p = PipelineBuilder::<u32>::new()
.then(
|x: u32| -> Option<u32> { if x > 5 { Some(x) } else { None } },
reg,
)
.on_none(log_miss, reg)
.unwrap_or(0);
for v in [1u32, 10, 3, 20] {
p.run(&mut world, v);
}
assert_eq!(*world.resource::<u64>(), 2); }
#[test]
fn ok_or_else_named_fn_with_param() {
fn make_error(msg: Res<String>) -> String {
msg.clone()
}
let mut wb = WorldBuilder::new();
wb.register::<String>("not found".into());
let mut world = wb.build();
let reg = world.registry();
let mut p = PipelineBuilder::<u32>::new()
.then(
|x: u32| -> Option<u32> { if x > 0 { Some(x) } else { None } },
reg,
)
.ok_or_else(make_error, reg);
let r: Result<u32, String> = p.run(&mut world, 5u32);
assert_eq!(r, Ok(5));
let r: Result<u32, String> = p.run(&mut world, 0u32);
assert_eq!(r, Err("not found".into()));
}
#[test]
fn unwrap_or_else_option_named_fn_with_param() {
fn fallback(default: Res<u64>) -> u64 {
*default
}
let mut wb = WorldBuilder::new();
wb.register::<u64>(42);
let mut world = wb.build();
let reg = world.registry();
let mut p = PipelineBuilder::<u32>::new()
.then(
|x: u32| -> Option<u64> { if x > 0 { Some(x as u64) } else { None } },
reg,
)
.unwrap_or_else(fallback, reg);
assert_eq!(p.run(&mut world, 5u32), 5);
assert_eq!(p.run(&mut world, 0u32), 42);
}
#[test]
fn map_err_named_fn_with_param() {
fn tag_error(prefix: Res<String>, err: String) -> String {
format!("{}: {err}", &*prefix)
}
fn sink(mut out: ResMut<String>, val: Result<u32, String>) {
match val {
Ok(v) => *out = format!("ok:{v}"),
Err(e) => *out = e,
}
}
let mut wb = WorldBuilder::new();
wb.register::<String>("ERR".into());
let mut world = wb.build();
let reg = world.registry();
let mut p = PipelineBuilder::<u32>::new()
.then(
|x: u32| -> Result<u32, String> { if x > 0 { Ok(x) } else { Err("zero".into()) } },
reg,
)
.map_err(tag_error, reg)
.then(sink, reg);
p.run(&mut world, 0u32);
assert_eq!(world.resource::<String>().as_str(), "ERR: zero");
p.run(&mut world, 5u32);
assert_eq!(world.resource::<String>().as_str(), "ok:5");
}
#[test]
fn scan_arity0_closure_running_sum() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut p = PipelineBuilder::<u64>::new().then(|x: u64| x, reg).scan(
0u64,
|acc: &mut u64, val: u64| {
*acc += val;
Some(*acc)
},
reg,
);
assert_eq!(p.run(&mut world, 10), Some(10));
assert_eq!(p.run(&mut world, 20), Some(30));
assert_eq!(p.run(&mut world, 5), Some(35));
}
#[test]
fn scan_named_fn_with_param() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(100);
let mut world = wb.build();
let reg = world.registry();
fn threshold_scan(limit: Res<u64>, acc: &mut u64, val: u64) -> Option<u64> {
*acc += val;
if *acc > *limit { Some(*acc) } else { None }
}
let mut p =
PipelineBuilder::<u64>::new()
.then(|x: u64| x, reg)
.scan(0u64, threshold_scan, reg);
assert_eq!(p.run(&mut world, 50), None);
assert_eq!(p.run(&mut world, 30), None);
assert_eq!(p.run(&mut world, 25), Some(105));
}
#[test]
fn scan_opaque_closure() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(10);
let mut world = wb.build();
let reg = world.registry();
let mut p = PipelineBuilder::<u64>::new().then(|x: u64| x, reg).scan(
0u64,
|world: &mut World, acc: &mut u64, val: u64| {
let factor = *world.resource::<u64>();
*acc += val * factor;
Some(*acc)
},
reg,
);
assert_eq!(p.run(&mut world, 1), Some(10));
assert_eq!(p.run(&mut world, 2), Some(30));
}
#[test]
fn scan_suppression_returns_none() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut p = PipelineBuilder::<u64>::new().then(|x: u64| x, reg).scan(
0u64,
|acc: &mut u64, val: u64| -> Option<u64> {
*acc += val;
if *acc > 50 { Some(*acc) } else { None }
},
reg,
);
assert_eq!(p.run(&mut world, 20), None);
assert_eq!(p.run(&mut world, 20), None);
assert_eq!(p.run(&mut world, 20), Some(60));
}
#[test]
fn scan_on_pipeline_start() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut p = PipelineBuilder::<u64>::new().scan(
0u64,
|acc: &mut u64, val: u64| {
*acc += val;
*acc
},
reg,
);
assert_eq!(p.run(&mut world, 5), 5);
assert_eq!(p.run(&mut world, 3), 8);
assert_eq!(p.run(&mut world, 2), 10);
}
#[test]
fn scan_persistence_across_batch() {
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 p = PipelineBuilder::<u64>::new()
.then(|x: u64| x, reg)
.scan(
0u64,
|acc: &mut u64, val: u64| {
*acc += val;
*acc
},
reg,
)
.then(store, reg)
.build_batch(4);
p.input_mut().extend([1, 2, 3]);
p.run(&mut world);
assert_eq!(*world.resource::<u64>(), 6);
p.input_mut().push(4);
p.run(&mut world);
assert_eq!(*world.resource::<u64>(), 10);
}
#[test]
fn build_option_unit_terminal() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let r = world.registry_mut();
fn check(x: u32) -> Option<u32> {
if x > 5 { Some(x) } else { None }
}
fn store(mut out: ResMut<u64>, val: u32) {
*out += val as u64;
}
let mut p = PipelineBuilder::<u32>::new()
.then(check, r)
.map(store, r)
.build();
p.run(&mut world, 3); assert_eq!(*world.resource::<u64>(), 0);
p.run(&mut world, 7); assert_eq!(*world.resource::<u64>(), 7);
p.run(&mut world, 10);
assert_eq!(*world.resource::<u64>(), 17);
}
#[test]
fn build_option_unit_boxes_into_handler() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let r = world.registry_mut();
fn double(x: u32) -> Option<u64> {
if x > 0 { Some(x as u64 * 2) } else { None }
}
fn store(mut out: ResMut<u64>, val: u64) {
*out += val;
}
let mut h: Box<dyn Handler<u32>> = Box::new(
PipelineBuilder::<u32>::new()
.then(double, r)
.map(store, r)
.build(),
);
h.run(&mut world, 0); assert_eq!(*world.resource::<u64>(), 0);
h.run(&mut world, 5); assert_eq!(*world.resource::<u64>(), 10);
}
#[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 r = world.registry_mut();
let mut p = PipelineBuilder::<&[u8]>::new()
.then(decode, r)
.then(store, r)
.build();
p.run(&mut world, &msg);
assert_eq!(*world.resource::<u64>(), 3);
}
#[test]
fn build_borrowed_event_option_unit() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
fn decode(msg: &[u8]) -> Option<u64> {
if msg.is_empty() {
None
} else {
Some(msg.len() as u64)
}
}
fn store(mut out: ResMut<u64>, val: u64) {
*out = val;
}
let empty = vec![];
let data = vec![1u8, 2, 3];
let r = world.registry_mut();
let mut p = PipelineBuilder::<&[u8]>::new()
.then(decode, r)
.map(store, r)
.build();
p.run(&mut world, &empty); assert_eq!(*world.resource::<u64>(), 0);
p.run(&mut world, &data); assert_eq!(*world.resource::<u64>(), 3);
}
}