use std::collections::BTreeMap;
use crate::dispatcher::{DispatcherError, SealedDispatcher};
use crate::merge::MergeStrategy;
use gen_types::TypedDispatcher;
type ComposedHelper<C, O> =
Box<dyn Fn(&serde_json::Value, &mut C) -> O + Send + Sync + 'static>;
pub struct ComposedDispatcher<C, O> {
helpers: BTreeMap<String, ComposedHelper<C, O>>,
strategy: MergeStrategy,
sources: Vec<ComposedSource>,
}
#[derive(Debug, Clone)]
pub struct ComposedSource {
pub label: String,
pub kinds: Vec<String>,
}
impl<C, O> Default for ComposedDispatcher<C, O> {
fn default() -> Self {
Self {
helpers: BTreeMap::new(),
strategy: MergeStrategy::default(),
sources: Vec::new(),
}
}
}
impl<C, O> ComposedDispatcher<C, O>
where
O: Default,
{
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_strategy(mut self, strategy: MergeStrategy) -> Self {
self.strategy = strategy;
self
}
#[must_use]
pub fn strategy(&self) -> MergeStrategy {
self.strategy
}
pub fn add<V>(
mut self,
label: &str,
source: SealedDispatcher<V, C, O>,
) -> Result<Self, DispatcherError>
where
V: TypedDispatcher
+ serde::Serialize
+ serde::de::DeserializeOwned
+ 'static,
C: 'static,
O: 'static,
{
let kinds = source.saturation_witness().into_iter().map(String::from).collect::<Vec<_>>();
for k in &kinds {
if self.helpers.contains_key(k) {
return Err(DispatcherError::DuplicateHelper { kind: k.clone() });
}
}
let arc = std::sync::Arc::new(source);
for k in &kinds {
let arc2 = arc.clone();
let kind_owned = k.clone();
self.helpers.insert(
kind_owned,
Box::new(move |value: &serde_json::Value, ctx: &mut C| {
let typed: V = match serde_json::from_value(value.clone()) {
Ok(t) => t,
Err(_) => return O::default(),
};
let r = arc2.apply_each(&[typed], ctx);
r.into_iter().next().unwrap_or_default()
}),
);
}
self.sources.push(ComposedSource {
label: label.to_string(),
kinds,
});
Ok(self)
}
pub fn apply_each(&self, variants: &[serde_json::Value], ctx: &mut C) -> Vec<O>
where
O: Default,
{
let mut out = Vec::with_capacity(variants.len());
for v in variants {
let Some(kind) = v.get("kind").and_then(|k| k.as_str()) else {
continue;
};
if let Some(h) = self.helpers.get(kind) {
out.push(h(v, ctx));
}
}
out
}
pub fn try_apply_each(
&self,
variants: &[serde_json::Value],
ctx: &mut C,
) -> Result<Vec<O>, DispatcherError>
where
O: Default,
{
let mut out = Vec::with_capacity(variants.len());
for v in variants {
let kind = v
.get("kind")
.and_then(|k| k.as_str())
.ok_or_else(|| DispatcherError::UnknownKind {
kind: "<no kind field>".to_string(),
})?;
let Some(h) = self.helpers.get(kind) else {
return Err(DispatcherError::UnknownKind {
kind: kind.to_string(),
});
};
out.push(h(v, ctx));
}
Ok(out)
}
#[must_use]
pub fn helper_count(&self) -> usize {
self.helpers.len()
}
#[must_use]
pub fn covered_kinds(&self) -> Vec<&str> {
self.helpers.keys().map(String::as_str).collect()
}
#[must_use]
pub fn sources(&self) -> &[ComposedSource] {
&self.sources
}
}