use std::marker::PhantomData;
use zrx_scheduler::action::context::Binding;
use zrx_scheduler::action::{Action, Context};
use zrx_scheduler::step::{IntoSteps, Scoped};
use zrx_scheduler::{Id, Value};
use crate::stream::combinator::convert::IntoStreamTupleCons;
use crate::stream::Stream;
use super::Operator;
pub struct Product<T, U> {
marker: PhantomData<(T, U)>,
}
impl<I, T> Stream<I, T>
where
I: Id,
T: Value,
{
#[inline]
#[must_use]
pub fn product<U>(&self, stream: &Stream<I, U>) -> Stream<I, (T, U)>
where
U: Value,
{
stream
.into_stream_tuple_cons(self.clone())
.subscribe(Product { marker: PhantomData })
}
}
impl<I, T, U> Action<I> for Product<T, U>
where
I: Id,
T: Value,
U: Value,
{
type Inputs = (T, U);
type Output<'a> = (T, U);
fn execute(&mut self, ctx: Context<I, Self>) -> impl IntoSteps<I, Self> {
let Binding { scopes, inputs, mut output, .. } = ctx.bind();
scopes.into_iter().flat_map(move |scope| {
let (left, right) = *inputs;
let mut scoped = vec![];
if let Some(l_value) = left.get(&scope) {
for (r_scope, r_value) in right.iter() {
let combined = scope.concat(r_scope);
output.insert(
combined.clone(),
(l_value.clone(), r_value.clone()),
);
scoped.push(Scoped::from(combined).done());
}
} else {
for r_scope in right.keys() {
let combined = scope.concat(r_scope);
output.remove(&combined);
scoped.push(Scoped::from(combined).done());
}
}
if let Some(r_value) = right.get(&scope) {
for (l_scope, l_value) in
left.iter().filter(|(k, _)| *k != &*scope)
{
let combined = scope.concat(l_scope);
output.insert(
combined.clone(),
(l_value.clone(), r_value.clone()),
);
scoped.push(Scoped::from(combined).done());
}
} else {
for l_scope in left.keys() {
let combined = l_scope.concat(&scope);
output.remove(&combined);
scoped.push(Scoped::from(combined).done());
}
}
scoped
})
}
}