use std::marker::PhantomData;
use zrx_scheduler::action::context::Binding;
use zrx_scheduler::action::{Action, Context};
use zrx_scheduler::step::{IntoSteps, Scope};
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]
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.key()) {
for (r_scope, r_value) in right.iter() {
let combined = scope.key().concat(r_scope);
output.insert(
combined.clone(),
(l_value.clone(), r_value.clone()),
);
scoped.push(Scope::from(combined).done());
}
} else {
for r_scope in right.keys() {
let combined = scope.key().concat(r_scope);
output.remove(&combined);
scoped.push(Scope::from(combined).done());
}
}
if let Some(r_value) = right.get(scope.key()) {
for (l_scope, l_value) in
left.iter().filter(|(k, _)| *k != scope.key())
{
let combined = scope.key().concat(l_scope);
output.insert(
combined.clone(),
(l_value.clone(), r_value.clone()),
);
scoped.push(Scope::from(combined).done());
}
} else {
for l_scope in left.keys() {
let combined = l_scope.concat(scope.key());
output.remove(&combined);
scoped.push(Scope::from(combined).done());
}
}
scoped
})
}
}