#![allow(clippy::type_complexity)]
use std::marker::PhantomData;
use crate::handler::{Opaque, Param};
use crate::world::{Registry, World};
#[doc(hidden)]
pub struct CtxStep<F, Params: Param> {
f: F,
state: Params::State,
#[allow(dead_code)]
name: &'static str,
}
#[doc(hidden)]
pub trait CtxStepCall<C, In> {
type Out;
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Self::Out;
}
#[diagnostic::on_unimplemented(
message = "this function cannot be used as a context-aware pipeline step",
note = "ctx step signature: `fn(&mut C, Params..., In) -> Out` — context first, resources, input last",
note = "for raw World access: `fn(&mut C, &mut World, In) -> Out`",
note = "closures with resource parameters are not supported — use a named `fn`"
)]
pub trait IntoCtxStep<C, In, Out, Params> {
type Step: CtxStepCall<C, In, Out = Out>;
fn into_ctx_step(self, registry: &Registry) -> Self::Step;
}
impl<C, In, Out, F: FnMut(&mut C, In) -> Out + 'static> CtxStepCall<C, In> for CtxStep<F, ()> {
type Out = Out;
#[inline(always)]
fn call(&mut self, ctx: &mut C, _world: &mut World, input: In) -> Out {
(self.f)(ctx, input)
}
}
impl<C, In, Out, F: FnMut(&mut C, In) -> Out + 'static> IntoCtxStep<C, In, Out, ()> for F {
type Step = CtxStep<F, ()>;
fn into_ctx_step(self, registry: &Registry) -> Self::Step {
CtxStep {
f: self,
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_into_ctx_step {
($($P:ident),+) => {
impl<C, In, Out, F: 'static, $($P: Param + 'static),+>
CtxStepCall<C, In> for CtxStep<F, ($($P,)+)>
where
for<'a> &'a mut F:
FnMut(&mut C, $($P,)+ In) -> Out +
FnMut(&mut C, $($P::Item<'a>,)+ In) -> Out,
{
type Out = Out;
#[inline(always)]
#[allow(non_snake_case)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<Ctx, $($P,)+ Input, Output>(
mut f: impl FnMut(&mut Ctx, $($P,)+ Input) -> Output,
ctx: &mut Ctx,
$($P: $P,)+
input: Input,
) -> Output {
f(ctx, $($P,)+ input)
}
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, ctx, $($P,)+ input)
}
}
impl<C, In, Out, F: 'static, $($P: Param + 'static),+>
IntoCtxStep<C, In, Out, ($($P,)+)> for F
where
for<'a> &'a mut F:
FnMut(&mut C, $($P,)+ In) -> Out +
FnMut(&mut C, $($P::Item<'a>,)+ In) -> Out,
{
type Step = CtxStep<F, ($($P,)+)>;
fn into_ctx_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>()),
)+
]);
}
CtxStep { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
all_tuples!(impl_into_ctx_step);
#[doc(hidden)]
pub struct CtxOpaqueStep<F> {
f: F,
#[allow(dead_code)]
name: &'static str,
}
impl<C, In, Out, F: FnMut(&mut C, &mut World, In) -> Out + 'static> CtxStepCall<C, In>
for CtxOpaqueStep<F>
{
type Out = Out;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Out {
(self.f)(ctx, world, input)
}
}
impl<C, In, Out, F: FnMut(&mut C, &mut World, In) -> Out + 'static> IntoCtxStep<C, In, Out, Opaque>
for F
{
type Step = CtxOpaqueStep<F>;
fn into_ctx_step(self, _registry: &Registry) -> Self::Step {
CtxOpaqueStep {
f: self,
name: std::any::type_name::<F>(),
}
}
}
#[doc(hidden)]
pub trait CtxRefStepCall<C, In> {
type Out;
fn call(&mut self, ctx: &mut C, world: &mut World, input: &In) -> Self::Out;
}
#[diagnostic::on_unimplemented(
message = "this function cannot be used as a context-aware reference step",
note = "ctx ref step signature: `fn(&mut C, Params..., &In) -> Out`",
note = "for raw World access: `fn(&mut C, &mut World, &In) -> Out`",
note = "closures with resource parameters are not supported — use a named `fn`"
)]
pub trait IntoCtxRefStep<C, In, Out, Params> {
type Step: CtxRefStepCall<C, In, Out = Out>;
fn into_ctx_ref_step(self, registry: &Registry) -> Self::Step;
}
impl<C, In, Out, F: FnMut(&mut C, &In) -> Out + 'static> CtxRefStepCall<C, In> for CtxStep<F, ()> {
type Out = Out;
#[inline(always)]
fn call(&mut self, ctx: &mut C, _world: &mut World, input: &In) -> Out {
(self.f)(ctx, input)
}
}
impl<C, In, Out, F: FnMut(&mut C, &In) -> Out + 'static> IntoCtxRefStep<C, In, Out, ()> for F {
type Step = CtxStep<F, ()>;
fn into_ctx_ref_step(self, registry: &Registry) -> Self::Step {
CtxStep {
f: self,
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_into_ctx_ref_step {
($($P:ident),+) => {
impl<C, In, Out, F: 'static, $($P: Param + 'static),+>
CtxRefStepCall<C, In> for CtxStep<F, ($($P,)+)>
where
for<'a> &'a mut F:
FnMut(&mut C, $($P,)+ &In) -> Out +
FnMut(&mut C, $($P::Item<'a>,)+ &In) -> Out,
{
type Out = Out;
#[inline(always)]
#[allow(non_snake_case)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: &In) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<Ctx, $($P,)+ Input: ?Sized, Output>(
mut f: impl FnMut(&mut Ctx, $($P,)+ &Input) -> Output,
ctx: &mut Ctx,
$($P: $P,)+
input: &Input,
) -> Output {
f(ctx, $($P,)+ input)
}
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, ctx, $($P,)+ input)
}
}
impl<C, In, Out, F: 'static, $($P: Param + 'static),+>
IntoCtxRefStep<C, In, Out, ($($P,)+)> for F
where
for<'a> &'a mut F:
FnMut(&mut C, $($P,)+ &In) -> Out +
FnMut(&mut C, $($P::Item<'a>,)+ &In) -> Out,
{
type Step = CtxStep<F, ($($P,)+)>;
fn into_ctx_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>()),
)+
]);
}
CtxStep { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
all_tuples!(impl_into_ctx_ref_step);
#[doc(hidden)]
pub struct CtxOpaqueRefStep<F> {
f: F,
#[allow(dead_code)]
name: &'static str,
}
impl<C, In, Out, F: FnMut(&mut C, &mut World, &In) -> Out + 'static> CtxRefStepCall<C, In>
for CtxOpaqueRefStep<F>
{
type Out = Out;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: &In) -> Out {
(self.f)(ctx, world, input)
}
}
impl<C, In, Out, F: FnMut(&mut C, &mut World, &In) -> Out + 'static>
IntoCtxRefStep<C, In, Out, Opaque> for F
{
type Step = CtxOpaqueRefStep<F>;
fn into_ctx_ref_step(self, _registry: &Registry) -> Self::Step {
CtxOpaqueRefStep {
f: self,
name: std::any::type_name::<F>(),
}
}
}
#[doc(hidden)]
pub trait CtxProducerCall<C> {
type Out;
fn call(&mut self, ctx: &mut C, world: &mut World) -> Self::Out;
}
#[diagnostic::on_unimplemented(
message = "this function cannot be used as a context-aware producer",
note = "ctx producer signature: `fn(&mut C, Params...) -> Out`",
note = "for raw World access: `fn(&mut C, &mut World) -> Out`",
note = "closures with resource parameters are not supported — use a named `fn`"
)]
pub trait IntoCtxProducer<C, Out, Params> {
type Step: CtxProducerCall<C, Out = Out>;
fn into_ctx_producer(self, registry: &Registry) -> Self::Step;
}
impl<C, Out, F: FnMut(&mut C) -> Out + 'static> CtxProducerCall<C> for CtxStep<F, ()> {
type Out = Out;
#[inline(always)]
fn call(&mut self, ctx: &mut C, _world: &mut World) -> Out {
(self.f)(ctx)
}
}
impl<C, Out, F: FnMut(&mut C) -> Out + 'static> IntoCtxProducer<C, Out, ()> for F {
type Step = CtxStep<F, ()>;
fn into_ctx_producer(self, registry: &Registry) -> Self::Step {
CtxStep {
f: self,
state: <() as Param>::init(registry),
name: std::any::type_name::<F>(),
}
}
}
macro_rules! impl_into_ctx_producer {
($($P:ident),+) => {
impl<C, Out, F: 'static, $($P: Param + 'static),+>
CtxProducerCall<C> for CtxStep<F, ($($P,)+)>
where
for<'a> &'a mut F:
FnMut(&mut C, $($P,)+) -> Out +
FnMut(&mut C, $($P::Item<'a>,)+) -> Out,
{
type Out = Out;
#[inline(always)]
#[allow(non_snake_case)]
fn call(&mut self, ctx: &mut C, world: &mut World) -> Out {
#[allow(clippy::too_many_arguments)]
fn call_inner<Ctx, $($P,)+ Output>(
mut f: impl FnMut(&mut Ctx, $($P,)+) -> Output,
ctx: &mut Ctx,
$($P: $P,)+
) -> Output {
f(ctx, $($P,)+)
}
#[cfg(debug_assertions)]
world.clear_borrows();
let ($($P,)+) = unsafe {
<($($P,)+) as Param>::fetch(world, &mut self.state)
};
call_inner(&mut self.f, ctx, $($P,)+)
}
}
impl<C, Out, F: 'static, $($P: Param + 'static),+>
IntoCtxProducer<C, Out, ($($P,)+)> for F
where
for<'a> &'a mut F:
FnMut(&mut C, $($P,)+) -> Out +
FnMut(&mut C, $($P::Item<'a>,)+) -> Out,
{
type Step = CtxStep<F, ($($P,)+)>;
fn into_ctx_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>()),
)+
]);
}
CtxStep { f: self, state, name: std::any::type_name::<F>() }
}
}
};
}
all_tuples!(impl_into_ctx_producer);
#[doc(hidden)]
pub struct CtxOpaqueProducer<F> {
f: F,
#[allow(dead_code)]
name: &'static str,
}
impl<C, Out, F: FnMut(&mut C, &mut World) -> Out + 'static> CtxProducerCall<C>
for CtxOpaqueProducer<F>
{
type Out = Out;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World) -> Out {
(self.f)(ctx, world)
}
}
impl<C, Out, F: FnMut(&mut C, &mut World) -> Out + 'static> IntoCtxProducer<C, Out, Opaque> for F {
type Step = CtxOpaqueProducer<F>;
fn into_ctx_producer(self, _registry: &Registry) -> Self::Step {
CtxOpaqueProducer {
f: self,
name: std::any::type_name::<F>(),
}
}
}
#[doc(hidden)]
pub trait CtxChainCall<C, In> {
type Out;
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Self::Out;
}
#[doc(hidden)]
pub struct CtxIdentityNode;
impl<C, In> CtxChainCall<C, In> for CtxIdentityNode {
type Out = In;
#[inline(always)]
fn call(&mut self, _ctx: &mut C, _world: &mut World, input: In) -> In {
input
}
}
#[doc(hidden)]
pub struct CtxThenNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, Prev, S> CtxChainCall<C, In> for CtxThenNode<Prev, S>
where
Prev: CtxChainCall<C, In>,
S: CtxStepCall<C, Prev::Out>,
{
type Out = S::Out;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> S::Out {
let mid = self.prev.call(ctx, world, input);
self.step.call(ctx, world, mid)
}
}
#[doc(hidden)]
pub struct CtxTapNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, Prev, S> CtxChainCall<C, In> for CtxTapNode<Prev, S>
where
Prev: CtxChainCall<C, In>,
S: CtxRefStepCall<C, Prev::Out, Out = ()>,
{
type Out = Prev::Out;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Prev::Out {
let val = self.prev.call(ctx, world, input);
self.step.call(ctx, world, &val);
val
}
}
#[doc(hidden)]
pub struct CtxGuardNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, Prev, S> CtxChainCall<C, In> for CtxGuardNode<Prev, S>
where
Prev: CtxChainCall<C, In>,
S: CtxRefStepCall<C, Prev::Out, Out = bool>,
{
type Out = Option<Prev::Out>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Option<Prev::Out> {
let val = self.prev.call(ctx, world, input);
if self.step.call(ctx, world, &val) {
Some(val)
} else {
None
}
}
}
#[doc(hidden)]
pub struct CtxMapOptionNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, T, Prev, S> CtxChainCall<C, In> for CtxMapOptionNode<Prev, S>
where
Prev: CtxChainCall<C, In, Out = Option<T>>,
S: CtxStepCall<C, T>,
{
type Out = Option<S::Out>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Option<S::Out> {
match self.prev.call(ctx, world, input) {
Some(val) => Some(self.step.call(ctx, world, val)),
None => None,
}
}
}
#[doc(hidden)]
pub struct CtxAndThenNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, T, U, Prev, S> CtxChainCall<C, In> for CtxAndThenNode<Prev, S>
where
Prev: CtxChainCall<C, In, Out = Option<T>>,
S: CtxStepCall<C, T, Out = Option<U>>,
{
type Out = Option<U>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Option<U> {
match self.prev.call(ctx, world, input) {
Some(val) => self.step.call(ctx, world, val),
None => None,
}
}
}
#[doc(hidden)]
pub struct CtxFilterNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, T, Prev, S> CtxChainCall<C, In> for CtxFilterNode<Prev, S>
where
Prev: CtxChainCall<C, In, Out = Option<T>>,
S: CtxRefStepCall<C, T, Out = bool>,
{
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Option<T> {
match self.prev.call(ctx, world, input) {
Some(val) if self.step.call(ctx, world, &val) => Some(val),
_ => None,
}
}
}
#[doc(hidden)]
pub struct CtxInspectOptionNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, T, Prev, S> CtxChainCall<C, In> for CtxInspectOptionNode<Prev, S>
where
Prev: CtxChainCall<C, In, Out = Option<T>>,
S: CtxRefStepCall<C, T, Out = ()>,
{
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Option<T> {
let opt = self.prev.call(ctx, world, input);
if let Some(ref val) = opt {
self.step.call(ctx, world, val);
}
opt
}
}
#[doc(hidden)]
pub struct CtxOnNoneNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) producer: S,
}
impl<C, In, T, Prev, S> CtxChainCall<C, In> for CtxOnNoneNode<Prev, S>
where
Prev: CtxChainCall<C, In, Out = Option<T>>,
S: CtxProducerCall<C, Out = ()>,
{
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Option<T> {
let opt = self.prev.call(ctx, world, input);
if opt.is_none() {
self.producer.call(ctx, world);
}
opt
}
}
#[doc(hidden)]
pub struct CtxUnwrapOrElseOptionNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) producer: S,
}
impl<C, In, T, Prev, S> CtxChainCall<C, In> for CtxUnwrapOrElseOptionNode<Prev, S>
where
Prev: CtxChainCall<C, In, Out = Option<T>>,
S: CtxProducerCall<C, Out = T>,
{
type Out = T;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> T {
match self.prev.call(ctx, world, input) {
Some(val) => val,
None => self.producer.call(ctx, world),
}
}
}
#[doc(hidden)]
pub struct CtxMapResultNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, T, E, Prev, S> CtxChainCall<C, In> for CtxMapResultNode<Prev, S>
where
Prev: CtxChainCall<C, In, Out = Result<T, E>>,
S: CtxStepCall<C, T>,
{
type Out = Result<S::Out, E>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Result<S::Out, E> {
match self.prev.call(ctx, world, input) {
Ok(val) => Ok(self.step.call(ctx, world, val)),
Err(e) => Err(e),
}
}
}
#[doc(hidden)]
pub struct CtxAndThenResultNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, T, U, E, Prev, S> CtxChainCall<C, In> for CtxAndThenResultNode<Prev, S>
where
Prev: CtxChainCall<C, In, Out = Result<T, E>>,
S: CtxStepCall<C, T, Out = Result<U, E>>,
{
type Out = Result<U, E>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Result<U, E> {
match self.prev.call(ctx, world, input) {
Ok(val) => self.step.call(ctx, world, val),
Err(e) => Err(e),
}
}
}
#[doc(hidden)]
pub struct CtxCatchNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, T, E, Prev, S> CtxChainCall<C, In> for CtxCatchNode<Prev, S>
where
Prev: CtxChainCall<C, In, Out = Result<T, E>>,
S: CtxStepCall<C, E, Out = ()>,
{
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Option<T> {
match self.prev.call(ctx, world, input) {
Ok(val) => Some(val),
Err(e) => {
self.step.call(ctx, world, e);
None
}
}
}
}
#[doc(hidden)]
pub struct CtxMapErrNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, T, E, E2, Prev, S> CtxChainCall<C, In> for CtxMapErrNode<Prev, S>
where
Prev: CtxChainCall<C, In, Out = Result<T, E>>,
S: CtxStepCall<C, E, Out = E2>,
{
type Out = Result<T, E2>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Result<T, E2> {
match self.prev.call(ctx, world, input) {
Ok(val) => Ok(val),
Err(e) => Err(self.step.call(ctx, world, e)),
}
}
}
#[doc(hidden)]
pub struct CtxInspectErrNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, T, E, Prev, S> CtxChainCall<C, In> for CtxInspectErrNode<Prev, S>
where
Prev: CtxChainCall<C, In, Out = Result<T, E>>,
S: CtxRefStepCall<C, E, Out = ()>,
{
type Out = Result<T, E>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Result<T, E> {
let result = self.prev.call(ctx, world, input);
if let Err(ref e) = result {
self.step.call(ctx, world, e);
}
result
}
}
#[doc(hidden)]
pub struct CtxInspectResultNode<Prev, S> {
pub(crate) prev: Prev,
pub(crate) step: S,
}
impl<C, In, T, E, Prev, S> CtxChainCall<C, In> for CtxInspectResultNode<Prev, S>
where
Prev: CtxChainCall<C, In, Out = Result<T, E>>,
S: CtxRefStepCall<C, T, Out = ()>,
{
type Out = Result<T, E>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Result<T, E> {
let result = self.prev.call(ctx, world, input);
if let Ok(ref val) = result {
self.step.call(ctx, world, val);
}
result
}
}
#[doc(hidden)]
pub struct CtxDiscardOptionNode<Prev> {
pub(crate) prev: Prev,
}
impl<C, In, Prev> CtxChainCall<C, In> for CtxDiscardOptionNode<Prev>
where
Prev: CtxChainCall<C, In, Out = Option<()>>,
{
type Out = ();
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) {
let _ = self.prev.call(ctx, world, input);
}
}
#[must_use = "pipeline builder does nothing until .then() is called"]
pub struct CtxPipelineBuilder<C, In>(PhantomData<fn(&mut C, In)>);
impl<C, In> CtxPipelineBuilder<C, In> {
pub fn new() -> Self {
Self(PhantomData)
}
pub fn then<Out, Params, S: IntoCtxStep<C, In, Out, Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Out, CtxThenNode<CtxIdentityNode, S::Step>> {
CtxPipelineChain {
chain: CtxThenNode {
prev: CtxIdentityNode,
step: f.into_ctx_step(registry),
},
_marker: PhantomData,
}
}
}
impl<C, In> Default for CtxPipelineBuilder<C, In> {
fn default() -> Self {
Self::new()
}
}
#[must_use = "pipeline chain does nothing until .build() is called"]
pub struct CtxPipelineChain<C, In, Out, Chain> {
chain: Chain,
_marker: PhantomData<fn(&mut C, In) -> Out>,
}
impl<C, In, Out, Chain: CtxChainCall<C, In, Out = Out>> CtxPipelineChain<C, In, Out, Chain> {
pub fn then<NewOut, Params, S: IntoCtxStep<C, Out, NewOut, Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, NewOut, CtxThenNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxThenNode {
prev: self.chain,
step: f.into_ctx_step(registry),
},
_marker: PhantomData,
}
}
pub fn run(&mut self, ctx: &mut C, world: &mut World, input: In) -> Out {
self.chain.call(ctx, world, input)
}
pub fn guard<Params, S: IntoCtxRefStep<C, Out, bool, Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Option<Out>, CtxGuardNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxGuardNode {
prev: self.chain,
step: f.into_ctx_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn tap<Params, S: IntoCtxRefStep<C, Out, (), Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Out, CtxTapNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxTapNode {
prev: self.chain,
step: f.into_ctx_ref_step(registry),
},
_marker: PhantomData,
}
}
}
impl<C, In, T, Chain: CtxChainCall<C, In, Out = Option<T>>>
CtxPipelineChain<C, In, Option<T>, Chain>
{
pub fn map<U, Params, S: IntoCtxStep<C, T, U, Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Option<U>, CtxMapOptionNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxMapOptionNode {
prev: self.chain,
step: f.into_ctx_step(registry),
},
_marker: PhantomData,
}
}
pub fn and_then<U, Params, S: IntoCtxStep<C, T, Option<U>, Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Option<U>, CtxAndThenNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxAndThenNode {
prev: self.chain,
step: f.into_ctx_step(registry),
},
_marker: PhantomData,
}
}
pub fn filter<Params, S: IntoCtxRefStep<C, T, bool, Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Option<T>, CtxFilterNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxFilterNode {
prev: self.chain,
step: f.into_ctx_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn inspect<Params, S: IntoCtxRefStep<C, T, (), Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Option<T>, CtxInspectOptionNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxInspectOptionNode {
prev: self.chain,
step: f.into_ctx_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn on_none<Params, S: IntoCtxProducer<C, (), Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Option<T>, CtxOnNoneNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxOnNoneNode {
prev: self.chain,
producer: f.into_ctx_producer(registry),
},
_marker: PhantomData,
}
}
pub fn unwrap_or_else<Params, S: IntoCtxProducer<C, T, Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, T, CtxUnwrapOrElseOptionNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxUnwrapOrElseOptionNode {
prev: self.chain,
producer: f.into_ctx_producer(registry),
},
_marker: PhantomData,
}
}
pub fn ok_or<E: Clone>(
self,
err: E,
) -> CtxPipelineChain<C, In, Result<T, E>, CtxOkOrNode<Chain, E>> {
CtxPipelineChain {
chain: CtxOkOrNode {
prev: self.chain,
err,
},
_marker: PhantomData,
}
}
pub fn unwrap_or(
self,
default: T,
) -> CtxPipelineChain<C, In, T, CtxUnwrapOrOptionNode<Chain, T>>
where
T: Clone,
{
CtxPipelineChain {
chain: CtxUnwrapOrOptionNode {
prev: self.chain,
default,
},
_marker: PhantomData,
}
}
}
#[doc(hidden)]
pub struct CtxOkOrNode<Prev, E> {
pub(crate) prev: Prev,
pub(crate) err: E,
}
impl<C, In, T, E: Clone, Prev> CtxChainCall<C, In> for CtxOkOrNode<Prev, E>
where
Prev: CtxChainCall<C, In, Out = Option<T>>,
{
type Out = Result<T, E>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Result<T, E> {
match self.prev.call(ctx, world, input) {
Some(val) => Ok(val),
None => Err(self.err.clone()),
}
}
}
#[doc(hidden)]
pub struct CtxUnwrapOrOptionNode<Prev, T> {
pub(crate) prev: Prev,
pub(crate) default: T,
}
impl<C, In, T: Clone, Prev> CtxChainCall<C, In> for CtxUnwrapOrOptionNode<Prev, T>
where
Prev: CtxChainCall<C, In, Out = Option<T>>,
{
type Out = T;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> T {
match self.prev.call(ctx, world, input) {
Some(val) => val,
None => self.default.clone(),
}
}
}
impl<C, In, T, E, Chain: CtxChainCall<C, In, Out = Result<T, E>>>
CtxPipelineChain<C, In, Result<T, E>, Chain>
{
pub fn map<U, Params, S: IntoCtxStep<C, T, U, Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Result<U, E>, CtxMapResultNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxMapResultNode {
prev: self.chain,
step: f.into_ctx_step(registry),
},
_marker: PhantomData,
}
}
pub fn and_then<U, Params, S: IntoCtxStep<C, T, Result<U, E>, Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Result<U, E>, CtxAndThenResultNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxAndThenResultNode {
prev: self.chain,
step: f.into_ctx_step(registry),
},
_marker: PhantomData,
}
}
pub fn catch<Params, S: IntoCtxStep<C, E, (), Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Option<T>, CtxCatchNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxCatchNode {
prev: self.chain,
step: f.into_ctx_step(registry),
},
_marker: PhantomData,
}
}
pub fn map_err<E2, Params, S: IntoCtxStep<C, E, E2, Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Result<T, E2>, CtxMapErrNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxMapErrNode {
prev: self.chain,
step: f.into_ctx_step(registry),
},
_marker: PhantomData,
}
}
pub fn inspect<Params, S: IntoCtxRefStep<C, T, (), Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Result<T, E>, CtxInspectResultNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxInspectResultNode {
prev: self.chain,
step: f.into_ctx_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn inspect_err<Params, S: IntoCtxRefStep<C, E, (), Params>>(
self,
f: S,
registry: &Registry,
) -> CtxPipelineChain<C, In, Result<T, E>, CtxInspectErrNode<Chain, S::Step>> {
CtxPipelineChain {
chain: CtxInspectErrNode {
prev: self.chain,
step: f.into_ctx_ref_step(registry),
},
_marker: PhantomData,
}
}
pub fn ok(self) -> CtxPipelineChain<C, In, Option<T>, CtxOkNode<Chain>> {
CtxPipelineChain {
chain: CtxOkNode { prev: self.chain },
_marker: PhantomData,
}
}
pub fn unwrap_or(
self,
default: T,
) -> CtxPipelineChain<C, In, T, CtxUnwrapOrResultNode<Chain, T>>
where
T: Clone,
{
CtxPipelineChain {
chain: CtxUnwrapOrResultNode {
prev: self.chain,
default,
},
_marker: PhantomData,
}
}
}
#[doc(hidden)]
pub struct CtxOkNode<Prev> {
pub(crate) prev: Prev,
}
impl<C, In, T, E, Prev> CtxChainCall<C, In> for CtxOkNode<Prev>
where
Prev: CtxChainCall<C, In, Out = Result<T, E>>,
{
type Out = Option<T>;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> Option<T> {
self.prev.call(ctx, world, input).ok()
}
}
#[doc(hidden)]
pub struct CtxUnwrapOrResultNode<Prev, T> {
pub(crate) prev: Prev,
pub(crate) default: T,
}
impl<C, In, T: Clone, E, Prev> CtxChainCall<C, In> for CtxUnwrapOrResultNode<Prev, T>
where
Prev: CtxChainCall<C, In, Out = Result<T, E>>,
{
type Out = T;
#[inline(always)]
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) -> T {
match self.prev.call(ctx, world, input) {
Ok(val) => val,
Err(_) => self.default.clone(),
}
}
}
impl<C, In, Chain: CtxChainCall<C, In, Out = ()>> CtxPipelineChain<C, In, (), Chain> {
#[must_use = "building a pipeline without storing it does nothing"]
pub fn build(self) -> CtxPipeline<C, In, Chain> {
CtxPipeline {
chain: self.chain,
_marker: PhantomData,
}
}
}
impl<C, In, Chain: CtxChainCall<C, In, Out = Option<()>>>
CtxPipelineChain<C, In, Option<()>, Chain>
{
#[must_use = "building a pipeline without storing it does nothing"]
pub fn build(self) -> CtxPipeline<C, In, CtxDiscardOptionNode<Chain>> {
CtxPipeline {
chain: CtxDiscardOptionNode { prev: self.chain },
_marker: PhantomData,
}
}
}
pub struct CtxPipeline<C, In, Chain> {
chain: Chain,
_marker: PhantomData<fn(&mut C, In)>,
}
impl<C, In, Chain: CtxChainCall<C, In, Out = ()>> CtxStepCall<C, In> for CtxPipeline<C, In, Chain> {
type Out = ();
fn call(&mut self, ctx: &mut C, world: &mut World, input: In) {
self.chain.call(ctx, world, input);
}
}
impl<C, In, Chain: CtxChainCall<C, In, Out = ()>> CtxPipeline<C, In, Chain> {
pub fn run(&mut self, ctx: &mut C, world: &mut World, input: In) {
self.chain.call(ctx, world, input);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Res, ResMut, WorldBuilder};
struct ReconnectCtx {
retries: u32,
last_result: Option<bool>,
}
#[test]
fn ctx_pipeline_three_steps_with_context_mutation() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(10);
let mut world = wb.build();
let reg = world.registry();
fn multiply(ctx: &mut ReconnectCtx, factor: Res<u64>, input: u32) -> u64 {
ctx.retries += 1;
*factor * input as u64
}
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|ctx: &mut ReconnectCtx, x: u32| {
ctx.retries += 1;
x
},
®,
)
.then(multiply, ®)
.then(
|ctx: &mut ReconnectCtx, val: u64| {
ctx.last_result = Some(val > 0);
},
®,
)
.build();
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
pipeline.run(&mut ctx, &mut world, 5);
assert_eq!(ctx.retries, 2);
assert_eq!(ctx.last_result, Some(true));
}
#[test]
fn ctx_pipeline_guard_and_map() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(|_ctx: &mut ReconnectCtx, x: u32| x, ®)
.guard(|_ctx: &mut ReconnectCtx, x: &u32| *x > 10, ®)
.map(
|ctx: &mut ReconnectCtx, x: u32| {
ctx.retries += 1;
x * 2
},
®,
);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
let result = pipeline.run(&mut ctx, &mut world, 5);
assert_eq!(result, None);
assert_eq!(ctx.retries, 0);
let result = pipeline.run(&mut ctx, &mut world, 20);
assert_eq!(result, Some(40));
assert_eq!(ctx.retries, 1);
}
#[test]
fn ctx_pipeline_and_then() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(|_ctx: &mut ReconnectCtx, x: u32| Some(x), ®)
.and_then(
|ctx: &mut ReconnectCtx, x: u32| {
ctx.retries += 1;
if x > 5 { Some(x * 2) } else { None }
},
®,
);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
assert_eq!(pipeline.run(&mut ctx, &mut world, 3), None);
assert_eq!(ctx.retries, 1);
assert_eq!(pipeline.run(&mut ctx, &mut world, 10), Some(20));
assert_eq!(ctx.retries, 2);
}
#[test]
fn ctx_pipeline_catch() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| -> Result<u32, String> {
if x > 0 {
Ok(x)
} else {
Err("zero".to_string())
}
},
®,
)
.catch(
|ctx: &mut ReconnectCtx, _err: String| {
ctx.retries += 1;
},
®,
)
.map(
|ctx: &mut ReconnectCtx, val: u32| {
ctx.last_result = Some(true);
val
},
®,
);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
let result = pipeline.run(&mut ctx, &mut world, 0);
assert_eq!(result, None);
assert_eq!(ctx.retries, 1);
assert_eq!(ctx.last_result, None);
let result = pipeline.run(&mut ctx, &mut world, 42);
assert_eq!(result, Some(42));
assert_eq!(ctx.retries, 1);
assert_eq!(ctx.last_result, Some(true));
}
#[test]
fn ctx_pipeline_with_res_mut() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(0);
let mut world = wb.build();
let reg = world.registry();
fn accumulate(ctx: &mut ReconnectCtx, mut total: ResMut<u64>, val: u32) {
*total += val as u64;
ctx.retries += 1;
}
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(accumulate, ®)
.build();
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
pipeline.run(&mut ctx, &mut world, 10);
pipeline.run(&mut ctx, &mut world, 5);
assert_eq!(*world.resource::<u64>(), 15);
assert_eq!(ctx.retries, 2);
}
#[test]
fn ctx_pipeline_build_with_option_unit() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| -> Option<u32> {
if x > 0 { Some(x) } else { None }
},
®,
)
.map(|_ctx: &mut ReconnectCtx, _x: u32| {}, ®)
.build();
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
pipeline.run(&mut ctx, &mut world, 5);
pipeline.run(&mut ctx, &mut world, 0);
}
#[test]
fn ctx_pipeline_tap() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(|_ctx: &mut ReconnectCtx, x: u32| x * 2, ®)
.tap(
|ctx: &mut ReconnectCtx, val: &u32| {
ctx.retries = *val;
},
®,
)
.then(
|_ctx: &mut ReconnectCtx, x: u32| {
assert_eq!(x, 10);
},
®,
)
.build();
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
pipeline.run(&mut ctx, &mut world, 5);
assert_eq!(ctx.retries, 10);
}
#[test]
fn ctx_pipeline_result_map_and_map_err() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| -> Result<u32, u32> {
if x > 0 { Ok(x) } else { Err(x) }
},
®,
)
.map(|_ctx: &mut ReconnectCtx, x: u32| x * 10, ®)
.map_err(
|ctx: &mut ReconnectCtx, e: u32| {
ctx.retries += 1;
format!("error: {e}")
},
®,
);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
assert_eq!(pipeline.run(&mut ctx, &mut world, 5), Ok(50));
assert_eq!(
pipeline.run(&mut ctx, &mut world, 0),
Err("error: 0".to_string())
);
assert_eq!(ctx.retries, 1);
}
#[test]
fn ctx_pipeline_inspect_err() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| -> Result<u32, String> {
if x > 0 { Ok(x) } else { Err("zero".into()) }
},
®,
)
.inspect_err(
|ctx: &mut ReconnectCtx, _e: &String| {
ctx.retries += 1;
},
®,
);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
let _ = pipeline.run(&mut ctx, &mut world, 0);
assert_eq!(ctx.retries, 1);
let _ = pipeline.run(&mut ctx, &mut world, 5);
assert_eq!(ctx.retries, 1);
}
#[test]
fn ctx_pipeline_filter() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(|_ctx: &mut ReconnectCtx, x: u32| Some(x), ®)
.filter(|_ctx: &mut ReconnectCtx, x: &u32| *x > 10, ®);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
assert_eq!(pipeline.run(&mut ctx, &mut world, 5), None);
assert_eq!(pipeline.run(&mut ctx, &mut world, 20), Some(20));
}
#[test]
fn ctx_pipeline_ok_or() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| {
if x > 0 { Some(x) } else { None }
},
®,
)
.ok_or("was zero");
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
assert_eq!(pipeline.run(&mut ctx, &mut world, 5), Ok(5));
assert_eq!(pipeline.run(&mut ctx, &mut world, 0), Err("was zero"));
}
#[test]
fn ctx_pipeline_unwrap_or_option() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| {
if x > 0 { Some(x) } else { None }
},
®,
)
.unwrap_or(99);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
assert_eq!(pipeline.run(&mut ctx, &mut world, 5), 5);
assert_eq!(pipeline.run(&mut ctx, &mut world, 0), 99);
}
#[test]
fn ctx_pipeline_unwrap_or_else_option() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| {
if x > 0 { Some(x) } else { None }
},
®,
)
.unwrap_or_else(
|ctx: &mut ReconnectCtx| {
ctx.retries += 1;
42
},
®,
);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
assert_eq!(pipeline.run(&mut ctx, &mut world, 5), 5);
assert_eq!(ctx.retries, 0);
assert_eq!(pipeline.run(&mut ctx, &mut world, 0), 42);
assert_eq!(ctx.retries, 1);
}
#[test]
fn ctx_pipeline_inspect_option() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| {
if x > 0 { Some(x) } else { None }
},
®,
)
.inspect(
|ctx: &mut ReconnectCtx, val: &u32| {
ctx.retries = *val;
},
®,
);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
let _ = pipeline.run(&mut ctx, &mut world, 7);
assert_eq!(ctx.retries, 7);
let _ = pipeline.run(&mut ctx, &mut world, 0);
assert_eq!(ctx.retries, 7);
}
#[test]
fn ctx_pipeline_on_none() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| {
if x > 0 { Some(x) } else { None }
},
®,
)
.on_none(
|ctx: &mut ReconnectCtx| {
ctx.retries += 1;
},
®,
);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
let result = pipeline.run(&mut ctx, &mut world, 5);
assert_eq!(result, Some(5));
assert_eq!(ctx.retries, 0);
let result = pipeline.run(&mut ctx, &mut world, 0);
assert_eq!(result, None);
assert_eq!(ctx.retries, 1);
}
#[test]
fn ctx_pipeline_ok_result() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| -> Result<u32, String> {
if x > 0 { Ok(x) } else { Err("zero".into()) }
},
®,
)
.ok();
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
assert_eq!(pipeline.run(&mut ctx, &mut world, 5), Some(5));
assert_eq!(pipeline.run(&mut ctx, &mut world, 0), None);
}
#[test]
fn ctx_pipeline_unwrap_or_result() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| -> Result<u32, String> {
if x > 0 { Ok(x) } else { Err("zero".into()) }
},
®,
)
.unwrap_or(99);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
assert_eq!(pipeline.run(&mut ctx, &mut world, 5), 5);
assert_eq!(pipeline.run(&mut ctx, &mut world, 0), 99);
}
#[test]
fn ctx_pipeline_inspect_result() {
let mut world = WorldBuilder::new().build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| -> Result<u32, String> {
if x > 0 { Ok(x) } else { Err("zero".into()) }
},
®,
)
.inspect(
|ctx: &mut ReconnectCtx, val: &u32| {
ctx.retries = *val;
},
®,
);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
let _ = pipeline.run(&mut ctx, &mut world, 7);
assert_eq!(ctx.retries, 7);
let _ = pipeline.run(&mut ctx, &mut world, 0);
assert_eq!(ctx.retries, 7);
}
#[test]
fn ctx_pipeline_opaque_step() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(100);
let mut world = wb.build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|ctx: &mut ReconnectCtx, w: &mut World, x: u32| {
ctx.retries += 1;
let scale = *w.resource::<u64>();
u64::from(x) * scale
},
®,
)
.then(
|ctx: &mut ReconnectCtx, val: u64| {
ctx.last_result = Some(val > 0);
},
®,
)
.build();
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
pipeline.run(&mut ctx, &mut world, 5);
assert_eq!(ctx.retries, 1);
assert_eq!(ctx.last_result, Some(true));
}
#[test]
fn ctx_pipeline_opaque_guard() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(10);
let mut world = wb.build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(|_ctx: &mut ReconnectCtx, x: u32| x, ®)
.guard(
|_ctx: &mut ReconnectCtx, w: &mut World, x: &u32| {
let threshold = *w.resource::<u64>();
u64::from(*x) > threshold
},
®,
);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
assert_eq!(pipeline.run(&mut ctx, &mut world, 5), None);
assert_eq!(pipeline.run(&mut ctx, &mut world, 20), Some(20));
}
#[test]
fn ctx_pipeline_opaque_producer() {
let mut wb = WorldBuilder::new();
wb.register::<u64>(42);
let mut world = wb.build();
let reg = world.registry();
let mut pipeline = CtxPipelineBuilder::<ReconnectCtx, u32>::new()
.then(
|_ctx: &mut ReconnectCtx, x: u32| {
if x > 0 { Some(x) } else { None }
},
®,
)
.unwrap_or_else(
|ctx: &mut ReconnectCtx, w: &mut World| {
ctx.retries += 1;
*w.resource::<u64>() as u32
},
®,
);
let mut ctx = ReconnectCtx {
retries: 0,
last_result: None,
};
assert_eq!(pipeline.run(&mut ctx, &mut world, 5), 5);
assert_eq!(ctx.retries, 0);
assert_eq!(pipeline.run(&mut ctx, &mut world, 0), 42);
assert_eq!(ctx.retries, 1);
}
}