use crate::{
Edge,
node::{self, Node},
visit,
};
use gantz_ca::{self as ca, CaHash};
use petgraph::visit::{Data, IntoEdgesDirected, IntoNodeReferences, NodeIndexable, Visitable};
use std::{collections::BTreeSet, fmt};
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, CaHash)]
#[cahash("gantz.eval-kind")]
pub enum EvalKind {
Push,
Pull,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, CaHash)]
#[cahash("gantz.eval-source")]
pub struct EvalSource {
pub path: Vec<node::Id>,
pub kind: EvalKind,
pub conns: node::Conns,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, CaHash)]
#[cahash("gantz.entrypoint")]
pub struct Entrypoint(pub BTreeSet<EvalSource>);
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct EntrypointId(pub ca::ContentAddr);
impl fmt::Display for EntrypointId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl Entrypoint {
pub fn id(&self) -> EntrypointId {
EntrypointId(ca::content_addr(self))
}
}
pub fn source(path: Vec<node::Id>, kind: EvalKind, n_conns: u8) -> EvalSource {
debug_assert!(!path.is_empty(), "EvalSource path must be non-empty");
EvalSource {
path,
kind,
conns: node::Conns::connected(n_conns as usize).unwrap(),
}
}
pub fn push_source(path: Vec<node::Id>, n_outputs: u8) -> EvalSource {
source(path, EvalKind::Push, n_outputs)
}
pub fn pull_source(path: Vec<node::Id>, n_inputs: u8) -> EvalSource {
source(path, EvalKind::Pull, n_inputs)
}
pub fn from_source(source: EvalSource) -> Entrypoint {
Entrypoint(BTreeSet::from([source]))
}
pub fn from_sources(sources: impl IntoIterator<Item = EvalSource>) -> Entrypoint {
Entrypoint(sources.into_iter().collect())
}
pub fn push(path: Vec<node::Id>, n_outputs: u8) -> Entrypoint {
from_source(push_source(path, n_outputs))
}
pub fn pull(path: Vec<node::Id>, n_inputs: u8) -> Entrypoint {
from_source(pull_source(path, n_inputs))
}
struct EntrypointCollector<'a> {
get_node: node::GetNode<'a>,
eps: Vec<Entrypoint>,
}
impl visit::Visitor for EntrypointCollector<'_> {
fn visit_pre(&mut self, ctx: visit::Ctx<'_, '_>, node: &dyn Node) {
let meta_ctx = node::MetaCtx::new(self.get_node);
let path = ctx.path().to_vec();
let n_outputs = node.n_outputs(meta_ctx);
for conf in node.push_eval(meta_ctx) {
let conns = super::meta::conns_from_eval_conf(&conf, n_outputs)
.expect("push_eval conf exceeds output count");
self.eps.push(from_source(EvalSource {
path: path.clone(),
kind: EvalKind::Push,
conns,
}));
}
let n_inputs = node.n_inputs(meta_ctx);
for conf in node.pull_eval(meta_ctx) {
let conns = super::meta::conns_from_eval_conf(&conf, n_inputs)
.expect("pull_eval conf exceeds input count");
self.eps.push(from_source(EvalSource {
path: path.clone(),
kind: EvalKind::Pull,
conns,
}));
}
}
}
pub fn push_pull_entrypoints<G>(get_node: node::GetNode<'_>, g: G) -> Vec<Entrypoint>
where
G: Data<EdgeWeight = Edge> + IntoEdgesDirected + IntoNodeReferences + NodeIndexable + Visitable,
G::NodeWeight: Node,
{
let mut collector = EntrypointCollector {
get_node,
eps: vec![],
};
crate::graph::visit(get_node, g, &[], &mut collector);
collector.eps
}