| @@ -24,14 +24,20 @@ pub trait Consumer<I>: Send + Sized { | |||
| /// The type of result that this consumer will ultimately produce. | |||
| type Result: Send; | |||
| /// Splits off a "left" consumer and returns it. The `self` | |||
| /// consumer should then be used to consume the "right" portion of | |||
| /// the data. (The ordering matters for methods like find_first -- | |||
| /// values produced by the returned value are given precedence | |||
| /// over values produced by `self`.) Once the left and right | |||
| /// halves have been fully consumed, you should reduce the results | |||
| /// with the result of `to_reducer`. | |||
| fn split_off_left(&self) -> (Self, Self::Reducer); | |||
| /// Divide the consumer into two consumers, one processing the left items | |||
| /// and one processing the right items from. Also produces a reducer that | |||
| /// can be used to reduce the results at the end. | |||
| fn split(self) -> (Self, Self, Self::Reducer) { | |||
| panic!("Consumer could not be used in unindexed mode!"); | |||
| } | |||
| /// Divide the consumer into two consumers, one processing items | |||
| /// `0..index` and one processing items from `index..`. Also | |||
| /// produces a reducer that can be used to reduce the results at | |||
| /// the end. | |||
| fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) { | |||
| panic!("Consumer could not be used in indexed mode!"); | |||
| } | |||
| /// Convert the consumer into a folder that can consume items | |||
| /// sequentially, eventually producing a final result. | |||
| @@ -43,14 +49,3 @@ pub trait Consumer<I>: Send + Sized { | |||
| false | |||
| } | |||
| } | |||
| /// A stateless consumer can be freely copied. These consumers can be | |||
| /// used like regular consumers, but they also support a | |||
| /// `split_at` method that does take an index to split. | |||
| pub trait IndexedConsumer<I>: Consumer<I> { | |||
| /// Divide the consumer into two consumers, one processing items | |||
| /// `0..index` and one processing items from `index..`. Also | |||
| /// produces a reducer that can be used to reduce the results at | |||
| /// the end. | |||
| fn split_at(self, index: usize) -> (Self, Self, Self::Reducer); | |||
| } | |||
| @@ -1,6 +1,5 @@ | |||
| use super::{ | |||
| Consumer, IndexedConsumer, IndexedProducer, IndexedProducerCallback, Producer, | |||
| ProducerCallback, Reducer, | |||
| Consumer, IndexedProducer, IndexedProducerCallback, Producer, ProducerCallback, Reducer, | |||
| }; | |||
| pub trait Executor<'a, D> | |||
| @@ -18,7 +17,7 @@ where | |||
| fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | |||
| where | |||
| P: IndexedProducer + 'a, | |||
| C: IndexedConsumer<P::Item, Result = D, Reducer = R> + 'a, | |||
| C: Consumer<P::Item, Result = D, Reducer = R> + 'a, | |||
| R: Reducer<D> + Send; | |||
| } | |||
| @@ -54,7 +53,7 @@ impl<'a, E, D, C, I, R> IndexedProducerCallback<'a, I> for ExecutorCallback<E, C | |||
| where | |||
| E: Executor<'a, D>, | |||
| D: Send, | |||
| C: IndexedConsumer<I, Result = D, Reducer = R> + 'a, | |||
| C: Consumer<I, Result = D, Reducer = R> + 'a, | |||
| R: Reducer<D> + Send, | |||
| { | |||
| type Output = E::Result; | |||
| @@ -1,6 +1,5 @@ | |||
| use super::{ | |||
| Consumer, Executor, FromParallelIterator, IndexedConsumer, IndexedProducerCallback, | |||
| ProducerCallback, Reducer, | |||
| Consumer, Executor, FromParallelIterator, IndexedProducerCallback, ProducerCallback, Reducer, | |||
| }; | |||
| use crate::{ | |||
| @@ -509,7 +508,7 @@ pub trait IndexedParallelIterator<'a>: ParallelIterator<'a> { | |||
| fn drive_indexed<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | |||
| where | |||
| E: Executor<'a, D>, | |||
| C: IndexedConsumer<Self::Item, Result = D, Reducer = R> + 'a, | |||
| C: Consumer<Self::Item, Result = D, Reducer = R> + 'a, | |||
| D: Send, | |||
| R: Reducer<D> + Send; | |||
| @@ -8,7 +8,7 @@ mod iterator; | |||
| mod producer; | |||
| mod reducer; | |||
| pub use consumer::{Consumer, IndexedConsumer}; | |||
| pub use consumer::Consumer; | |||
| pub use driver::Driver; | |||
| pub use executor::{Executor, ExecutorCallback}; | |||
| pub use folder::Folder; | |||
| @@ -1,6 +1,4 @@ | |||
| use crate::core::{ | |||
| Consumer, Executor, Folder, IndexedConsumer, IndexedProducer, Producer, Reducer, | |||
| }; | |||
| use crate::core::{Consumer, Executor, Folder, IndexedProducer, Producer, Reducer}; | |||
| #[derive(Default)] | |||
| pub struct Sequential; | |||
| @@ -27,7 +25,7 @@ where | |||
| fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | |||
| where | |||
| P: IndexedProducer, | |||
| C: IndexedConsumer<P::Item, Result = D, Reducer = R>, | |||
| C: Consumer<P::Item, Result = D, Reducer = R>, | |||
| R: Reducer<D>, | |||
| { | |||
| if consumer.is_full() { | |||
| @@ -7,9 +7,7 @@ use futures::{ | |||
| }; | |||
| use tokio::task::spawn; | |||
| use crate::core::{ | |||
| Consumer, Executor, Folder, IndexedConsumer, IndexedProducer, Producer, Reducer, | |||
| }; | |||
| use crate::core::{Consumer, Executor, Folder, IndexedProducer, Producer, Reducer}; | |||
| pub struct Tokio { | |||
| splits: usize, | |||
| @@ -50,7 +48,7 @@ where | |||
| fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | |||
| where | |||
| P: IndexedProducer + 'a, | |||
| C: IndexedConsumer<P::Item, Result = D, Reducer = R> + 'a, | |||
| C: Consumer<P::Item, Result = D, Reducer = R> + 'a, | |||
| R: Reducer<D> + Send, | |||
| { | |||
| let splits = producer.splits().unwrap_or(self.splits); | |||
| @@ -77,8 +75,7 @@ where | |||
| } else if splitter.try_split() { | |||
| match producer.split() { | |||
| (left_producer, Some(right_producer)) => { | |||
| let ((left_consumer, reducer), right_consumer) = | |||
| (consumer.split_off_left(), consumer); | |||
| let (left_consumer, right_consumer, reducer) = consumer.split(); | |||
| let left = run_as_task(exec(splitter, left_producer, left_consumer)); | |||
| let right = run_as_task(exec(splitter, right_producer, right_consumer)); | |||
| @@ -103,7 +100,7 @@ fn exec_indexed<'a, P, C>( | |||
| ) -> BoxFuture<'a, C::Result> | |||
| where | |||
| P: IndexedProducer + 'a, | |||
| C: IndexedConsumer<P::Item> + 'a, | |||
| C: Consumer<P::Item> + 'a, | |||
| C::Reducer: Send, | |||
| { | |||
| async move { | |||
| @@ -46,13 +46,22 @@ where | |||
| type Reducer = NoOpReducer; | |||
| type Result = (); | |||
| fn split_off_left(&self) -> (Self, NoOpReducer) { | |||
| ( | |||
| ForEachConsumer { | |||
| operation: self.operation.clone(), | |||
| }, | |||
| NoOpReducer, | |||
| ) | |||
| fn split(self) -> (Self, Self, NoOpReducer) { | |||
| let left = self; | |||
| let right = ForEachConsumer { | |||
| operation: left.operation.clone(), | |||
| }; | |||
| (left, right, NoOpReducer) | |||
| } | |||
| fn split_at(self, _index: usize) -> (Self, Self, NoOpReducer) { | |||
| let left = self; | |||
| let right = ForEachConsumer { | |||
| operation: left.operation.clone(), | |||
| }; | |||
| (left, right, NoOpReducer) | |||
| } | |||
| fn into_folder(self) -> Self { | |||
| @@ -1,6 +1,6 @@ | |||
| use crate::{ | |||
| Consumer, Executor, Folder, IndexedConsumer, IndexedParallelIterator, IndexedProducer, | |||
| IndexedProducerCallback, ParallelIterator, Producer, ProducerCallback, Reducer, | |||
| Consumer, Executor, Folder, IndexedParallelIterator, IndexedProducer, IndexedProducerCallback, | |||
| ParallelIterator, Producer, ProducerCallback, Reducer, | |||
| }; | |||
| /* Map */ | |||
| @@ -60,7 +60,7 @@ where | |||
| fn drive_indexed<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | |||
| where | |||
| E: Executor<'a, D>, | |||
| C: IndexedConsumer<Self::Item, Result = D, Reducer = R> + 'a, | |||
| C: Consumer<Self::Item, Result = D, Reducer = R> + 'a, | |||
| D: Send, | |||
| R: Reducer<D> + Send, | |||
| { | |||
| @@ -262,10 +262,22 @@ where | |||
| type Reducer = C::Reducer; | |||
| type Result = C::Result; | |||
| fn split_off_left(&self) -> (Self, Self::Reducer) { | |||
| let (left, reducer) = self.base.split_off_left(); | |||
| fn split(self) -> (Self, Self, Self::Reducer) { | |||
| let (left, right, reducer) = self.base.split(); | |||
| let left = MapConsumer::new(left, self.operation.clone()); | |||
| let right = MapConsumer::new(right, self.operation); | |||
| (left, right, reducer) | |||
| } | |||
| fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) { | |||
| let (left, right, reducer) = self.base.split_at(index); | |||
| let left = MapConsumer::new(left, self.operation.clone()); | |||
| let right = MapConsumer::new(right, self.operation); | |||
| (MapConsumer::new(left, self.operation.clone()), reducer) | |||
| (left, right, reducer) | |||
| } | |||
| fn into_folder(self) -> Self::Folder { | |||
| @@ -280,23 +292,6 @@ where | |||
| } | |||
| } | |||
| impl<I, T, C, O> IndexedConsumer<I> for MapConsumer<C, O> | |||
| where | |||
| C: IndexedConsumer<O::Output>, | |||
| O: Fn(I) -> T + Clone + Send + Sync, | |||
| T: Send, | |||
| { | |||
| fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) { | |||
| let (left, right, reducer) = self.base.split_at(index); | |||
| ( | |||
| MapConsumer::new(left, self.operation.clone()), | |||
| MapConsumer::new(right, self.operation), | |||
| reducer, | |||
| ) | |||
| } | |||
| } | |||
| /* MapFolder */ | |||
| struct MapFolder<F, O> { | |||
| @@ -1,6 +1,6 @@ | |||
| use crate::{ | |||
| Consumer, Executor, Folder, IndexedConsumer, IndexedParallelIterator, IndexedProducer, | |||
| IndexedProducerCallback, ParallelIterator, Producer, ProducerCallback, Reducer, | |||
| Consumer, Executor, Folder, IndexedParallelIterator, IndexedProducer, IndexedProducerCallback, | |||
| ParallelIterator, Producer, ProducerCallback, Reducer, | |||
| }; | |||
| use super::map_with::{MapWithFolder, MapWithIter}; | |||
| @@ -70,7 +70,7 @@ where | |||
| fn drive_indexed<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | |||
| where | |||
| E: Executor<'a, D>, | |||
| C: IndexedConsumer<Self::Item, Result = D, Reducer = R> + 'a, | |||
| C: Consumer<Self::Item, Result = D, Reducer = R> + 'a, | |||
| D: Send, | |||
| R: Reducer<D> + Send, | |||
| { | |||
| @@ -302,13 +302,22 @@ where | |||
| type Reducer = C::Reducer; | |||
| type Result = C::Result; | |||
| fn split_off_left(&self) -> (Self, Self::Reducer) { | |||
| let (left, reducer) = self.base.split_off_left(); | |||
| fn split(self) -> (Self, Self, Self::Reducer) { | |||
| let (left, right, reducer) = self.base.split(); | |||
| ( | |||
| MapInitConsumer::new(left, self.init.clone(), self.operation.clone()), | |||
| reducer, | |||
| ) | |||
| let left = MapInitConsumer::new(left, self.init.clone(), self.operation.clone()); | |||
| let right = MapInitConsumer::new(right, self.init, self.operation); | |||
| (left, right, reducer) | |||
| } | |||
| fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) { | |||
| let (left, right, reducer) = self.base.split_at(index); | |||
| let left = MapInitConsumer::new(left, self.init.clone(), self.operation.clone()); | |||
| let right = MapInitConsumer::new(right, self.init, self.operation); | |||
| (left, right, reducer) | |||
| } | |||
| fn into_folder(self) -> Self::Folder { | |||
| @@ -323,21 +332,3 @@ where | |||
| self.base.is_full() | |||
| } | |||
| } | |||
| impl<I, T, C, S, U, O> IndexedConsumer<I> for MapInitConsumer<C, S, O> | |||
| where | |||
| C: IndexedConsumer<T>, | |||
| O: Fn(&mut U, I) -> T + Clone + Send + Sync, | |||
| T: Send, | |||
| S: Fn() -> U + Clone + Send, | |||
| { | |||
| fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) { | |||
| let (left, right, reducer) = self.base.split_at(index); | |||
| ( | |||
| MapInitConsumer::new(left, self.init.clone(), self.operation.clone()), | |||
| MapInitConsumer::new(right, self.init, self.operation), | |||
| reducer, | |||
| ) | |||
| } | |||
| } | |||
| @@ -1,6 +1,6 @@ | |||
| use crate::{ | |||
| Consumer, Executor, Folder, IndexedConsumer, IndexedParallelIterator, IndexedProducer, | |||
| IndexedProducerCallback, ParallelIterator, Producer, ProducerCallback, Reducer, | |||
| Consumer, Executor, Folder, IndexedParallelIterator, IndexedProducer, IndexedProducerCallback, | |||
| ParallelIterator, Producer, ProducerCallback, Reducer, | |||
| }; | |||
| /* MapWith */ | |||
| @@ -68,7 +68,7 @@ where | |||
| fn drive_indexed<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | |||
| where | |||
| E: Executor<'a, D>, | |||
| C: IndexedConsumer<Self::Item, Result = D, Reducer = R> + 'a, | |||
| C: Consumer<Self::Item, Result = D, Reducer = R> + 'a, | |||
| D: Send, | |||
| R: Reducer<D> + Send, | |||
| { | |||
| @@ -345,13 +345,22 @@ where | |||
| type Reducer = C::Reducer; | |||
| type Result = C::Result; | |||
| fn split_off_left(&self) -> (Self, Self::Reducer) { | |||
| let (left, reducer) = self.base.split_off_left(); | |||
| fn split(self) -> (Self, Self, Self::Reducer) { | |||
| let (left, right, reducer) = self.base.split(); | |||
| ( | |||
| MapWithConsumer::new(left, self.item.clone(), self.operation.clone()), | |||
| reducer, | |||
| ) | |||
| let left = MapWithConsumer::new(left, self.item.clone(), self.operation.clone()); | |||
| let right = MapWithConsumer::new(right, self.item, self.operation); | |||
| (left, right, reducer) | |||
| } | |||
| fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) { | |||
| let (left, right, reducer) = self.base.split_at(index); | |||
| let left = MapWithConsumer::new(left, self.item.clone(), self.operation.clone()); | |||
| let right = MapWithConsumer::new(right, self.item, self.operation); | |||
| (left, right, reducer) | |||
| } | |||
| fn into_folder(self) -> Self::Folder { | |||
| @@ -367,24 +376,6 @@ where | |||
| } | |||
| } | |||
| impl<I, T, C, S, O> IndexedConsumer<I> for MapWithConsumer<C, S, O> | |||
| where | |||
| C: IndexedConsumer<T>, | |||
| O: Fn(&mut S, I) -> T + Clone + Send + Sync, | |||
| T: Send, | |||
| S: Clone + Send, | |||
| { | |||
| fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) { | |||
| let (left, right, reducer) = self.base.split_at(index); | |||
| ( | |||
| MapWithConsumer::new(left, self.item.clone(), self.operation.clone()), | |||
| MapWithConsumer::new(right, self.item, self.operation), | |||
| reducer, | |||
| ) | |||
| } | |||
| } | |||
| /* MapWithFolder */ | |||
| pub struct MapWithFolder<F, S, O> { | |||
| @@ -1,4 +1,4 @@ | |||
| use crate::{Consumer, Folder, IndexedConsumer, Reducer}; | |||
| use crate::{Consumer, Folder, Reducer}; | |||
| pub struct NoOpConsumer; | |||
| @@ -7,8 +7,12 @@ impl<T> Consumer<T> for NoOpConsumer { | |||
| type Reducer = NoOpReducer; | |||
| type Result = (); | |||
| fn split_off_left(&self) -> (Self, Self::Reducer) { | |||
| (NoOpConsumer, NoOpReducer) | |||
| fn split(self) -> (Self, Self, Self::Reducer) { | |||
| (NoOpConsumer, NoOpConsumer, NoOpReducer) | |||
| } | |||
| fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) { | |||
| (NoOpConsumer, NoOpConsumer, NoOpReducer) | |||
| } | |||
| fn into_folder(self) -> Self { | |||
| @@ -38,12 +42,6 @@ impl<T> Folder<T> for NoOpConsumer { | |||
| fn complete(self) {} | |||
| } | |||
| impl<T> IndexedConsumer<T> for NoOpConsumer { | |||
| fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) { | |||
| (NoOpConsumer, NoOpConsumer, NoOpReducer) | |||
| } | |||
| } | |||
| pub struct NoOpReducer; | |||
| impl Reducer<()> for NoOpReducer { | |||
| @@ -1,4 +1,4 @@ | |||
| use crate::{core::Driver, Consumer, Executor, Folder, IndexedConsumer, ParallelIterator, Reducer}; | |||
| use crate::{core::Driver, Consumer, Executor, Folder, ParallelIterator, Reducer}; | |||
| pub struct Reduce<X, S, O> { | |||
| iterator: X, | |||
| @@ -69,8 +69,12 @@ where | |||
| type Reducer = Self; | |||
| type Result = T; | |||
| fn split_off_left(&self) -> (Self, Self::Reducer) { | |||
| (self.clone(), self.clone()) | |||
| fn split(self) -> (Self, Self, Self::Reducer) { | |||
| (self.clone(), self.clone(), self) | |||
| } | |||
| fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) { | |||
| (self.clone(), self.clone(), self) | |||
| } | |||
| fn into_folder(self) -> Self::Folder { | |||
| @@ -81,17 +85,6 @@ where | |||
| } | |||
| } | |||
| impl<S, O, T> IndexedConsumer<T> for ReduceConsumer<S, O> | |||
| where | |||
| S: Fn() -> T + Clone + Send, | |||
| O: Fn(T, T) -> T + Clone + Send, | |||
| T: Send, | |||
| { | |||
| fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) { | |||
| (self.clone(), self.clone(), self) | |||
| } | |||
| } | |||
| impl<S, O, T> Reducer<T> for ReduceConsumer<S, O> | |||
| where | |||
| O: Fn(T, T) -> T, | |||
| @@ -3,9 +3,7 @@ use std::sync::{ | |||
| Arc, | |||
| }; | |||
| use crate::{ | |||
| core::Driver, misc::Try, Consumer, Executor, Folder, IndexedConsumer, ParallelIterator, Reducer, | |||
| }; | |||
| use crate::{core::Driver, misc::Try, Consumer, Executor, Folder, ParallelIterator, Reducer}; | |||
| pub struct TryReduce<X, S, O> { | |||
| iterator: X, | |||
| @@ -80,8 +78,12 @@ where | |||
| type Reducer = Self; | |||
| type Result = T; | |||
| fn split_off_left(&self) -> (Self, Self::Reducer) { | |||
| (self.clone(), self.clone()) | |||
| fn split(self) -> (Self, Self, Self::Reducer) { | |||
| (self.clone(), self.clone(), self) | |||
| } | |||
| fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) { | |||
| (self.clone(), self.clone(), self) | |||
| } | |||
| fn into_folder(self) -> Self::Folder { | |||
| @@ -93,17 +95,6 @@ where | |||
| } | |||
| } | |||
| impl<S, O, T> IndexedConsumer<T> for TryReduceConsumer<S, O> | |||
| where | |||
| S: Fn() -> T::Ok + Clone + Send, | |||
| O: Fn(T::Ok, T::Ok) -> T + Clone + Send, | |||
| T: Try + Send, | |||
| { | |||
| fn split_at(self, _index: usize) -> (Self, Self, Self::Reducer) { | |||
| (self.clone(), self.clone(), self) | |||
| } | |||
| } | |||
| impl<S, O, T> Reducer<T> for TryReduceConsumer<S, O> | |||
| where | |||
| O: Fn(T::Ok, T::Ok) -> T, | |||
| @@ -5,8 +5,8 @@ mod misc; | |||
| mod std; | |||
| pub use self::core::{ | |||
| Consumer, Driver, Executor, ExecutorCallback, Folder, IndexedConsumer, IndexedParallelIterator, | |||
| IndexedProducer, IndexedProducerCallback, IntoParallelIterator, IntoParallelRefIterator, | |||
| Consumer, Driver, Executor, ExecutorCallback, Folder, IndexedParallelIterator, IndexedProducer, | |||
| IndexedProducerCallback, IntoParallelIterator, IntoParallelRefIterator, | |||
| IntoParallelRefMutIterator, ParallelIterator, Producer, ProducerCallback, Reducer, | |||
| }; | |||
| pub use self::executor::{DefaultExecutor, SequentialExecutor}; | |||
| @@ -1,9 +1,9 @@ | |||
| use std::ops::Range; | |||
| use crate::{ | |||
| Consumer, Executor, ExecutorCallback, IndexedConsumer, IndexedParallelIterator, | |||
| IndexedProducer, IndexedProducerCallback, IntoParallelIterator, ParallelIterator, Producer, | |||
| ProducerCallback, Reducer, | |||
| Consumer, Executor, ExecutorCallback, IndexedParallelIterator, IndexedProducer, | |||
| IndexedProducerCallback, IntoParallelIterator, ParallelIterator, Producer, ProducerCallback, | |||
| Reducer, | |||
| }; | |||
| /// Parallel iterator over a range, implemented for all integer types. | |||
| @@ -143,7 +143,7 @@ macro_rules! indexed_parallel_iterator_impl { | |||
| fn drive_indexed<E, C, D, R>(self, executor: E, consumer: C) -> E::Result | |||
| where | |||
| E: Executor<'a, D>, | |||
| C: IndexedConsumer<Self::Item, Result = D, Reducer = R> + 'a, | |||
| C: Consumer<Self::Item, Result = D, Reducer = R> + 'a, | |||
| D: Send, | |||
| R: Reducer<D> + Send, | |||
| { | |||
| @@ -7,7 +7,7 @@ use futures::{ | |||
| }; | |||
| use tokio::task::{block_in_place, spawn}; | |||
| use asparit::{Consumer, Executor, Folder, IndexedConsumer, IndexedProducer, Producer, Reducer}; | |||
| use asparit::{Consumer, Executor, Folder, IndexedProducer, Producer, Reducer}; | |||
| pub struct TokioExecutor; | |||
| @@ -23,7 +23,7 @@ impl Executor for TokioExecutor { | |||
| fn exec_indexed<P, C>(self, producer: P, consumer: C) -> C::Result | |||
| where | |||
| P: IndexedProducer, | |||
| C: IndexedConsumer<P::Item>, | |||
| C: Consumer<P::Item>, | |||
| { | |||
| let _producer = producer; | |||
| let _consumer = consumer; | |||