use std::any::Any;
use std::collections::{BTreeMap, BTreeSet};
use zrx_graph::{self as graph};
use super::descriptor::Descriptor;
use super::{Action, Graph, Marker, Source};
#[derive(Debug, Default)]
pub struct Builder<I> {
inner: graph::Builder<Descriptor, usize>,
actions: Vec<Box<dyn Action<I>>>,
}
pub struct Connector<'a, I> {
builder: &'a mut Builder<I>,
descriptor: Descriptor,
}
impl<I> Builder<I> {
#[must_use]
pub fn new() -> Self {
Self {
inner: graph::Graph::builder(),
actions: Vec::new(),
}
}
#[allow(clippy::missing_panics_doc)]
#[inline]
pub fn add_source<T>(&mut self) -> usize
where
T: Any,
{
let from = self.inner.add_node(Descriptor::new::<T>());
let node = self.inner.add_node(Descriptor::new::<T>());
self.actions.push(Box::new(Marker));
self.inner.add_edge(from, node, 0).expect("invariant");
node
}
#[inline]
#[must_use]
pub fn add_action<T>(&mut self) -> Connector<'_, I>
where
T: Any,
{
Connector {
builder: self,
descriptor: Descriptor::new::<T>(),
}
}
#[allow(clippy::missing_panics_doc)]
pub fn build(self) -> Graph<I> {
let mut actions = Vec::with_capacity(self.inner.len());
let mut degrees = vec![0; self.inner.len()];
let mut offset = 0;
for edge in self.inner.edges() {
offset += edge.weight;
actions.push(actions.len() - offset);
degrees[edge.target] += 1;
}
let mut sources = BTreeMap::default();
for node in 0..self.inner.len() {
if degrees[node] == 0 {
sources
.entry(self.inner[node].clone())
.or_insert_with(BTreeSet::new)
.insert(actions[node]);
}
}
let mut builder = graph::Graph::builder();
for action in self.actions {
builder.add_node(action);
}
let edge_graph = self.inner.to_edge_graph();
for edge in edge_graph.edges() {
let node = &edge_graph[edge.source];
if node.weight == 0 {
builder
.add_edge(actions[edge.source], actions[edge.target], ())
.expect("invariant");
}
}
Graph {
actions: builder.build(),
sources: sources
.into_iter()
.map(|(descriptor, actions)| Source {
descriptor,
actions: Vec::from_iter(actions),
})
.collect(),
}
}
}
impl<I> Connector<'_, I> {
pub fn with<S, A>(self, sources: S, action: A) -> usize
where
S: IntoIterator<Item = usize>,
A: Action<I> + 'static,
{
self.builder.actions.push(Box::new(action));
let target = self.builder.inner.add_node(self.descriptor);
for (weight, source) in sources.into_iter().enumerate() {
self.builder
.inner
.add_edge(source, target, weight)
.expect("invariant");
}
target
}
}