use std::marker::PhantomData;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use super::{
condition::{Condition, ConditionMap},
merge::MergerMap,
node::BspNode,
value::BspValue,
};
pub type NodeId = usize;
pub type PortIndex = usize;
pub type EdgeId = usize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct OutputPin {
pub node_id: NodeId,
pub port: PortIndex,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct InputPin {
pub node_id: NodeId,
pub port: PortIndex,
}
pub const ERROR_PORT: PortIndex = usize::MAX;
#[derive(Clone)]
pub struct Edge<S> {
pub source: NodeId,
pub target: NodeId,
pub condition: Condition<S>,
pub max_iterations: Option<u64>,
pub source_port: Option<PortIndex>,
pub target_port: Option<PortIndex>,
}
impl<S> Edge<S> {
fn control(source: NodeId, target: NodeId) -> Self {
Self {
source,
target,
condition: Condition::Always,
max_iterations: None,
source_port: None,
target_port: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct NodeHandle {
pub id: NodeId,
}
impl NodeHandle {
#[must_use]
pub const fn new(id: NodeId) -> Self {
Self { id }
}
#[must_use]
pub const fn output(self, port: PortIndex) -> OutputPin {
OutputPin {
node_id: self.id,
port,
}
}
#[must_use]
pub const fn input(self, port: PortIndex) -> InputPin {
InputPin {
node_id: self.id,
port,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TypedNodeHandle<N> {
pub id: NodeId,
_marker: PhantomData<fn() -> N>,
}
impl<N> TypedNodeHandle<N> {
#[must_use]
pub const fn new(id: NodeId) -> Self {
Self {
id,
_marker: PhantomData,
}
}
#[must_use]
pub const fn get_output_pin(&self, port: PortIndex) -> OutputPin {
OutputPin {
node_id: self.id,
port,
}
}
#[must_use]
pub const fn get_input_pin(&self, port: PortIndex) -> InputPin {
InputPin {
node_id: self.id,
port,
}
}
#[must_use]
pub const fn as_untyped(&self) -> NodeHandle {
NodeHandle::new(self.id)
}
}
impl<N> From<TypedNodeHandle<N>> for NodeHandle {
fn from(typed: TypedNodeHandle<N>) -> Self {
Self::new(typed.id)
}
}
impl<N> serde::Serialize for TypedNodeHandle<N> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.id.serialize(serializer)
}
}
impl<'de, N> serde::Deserialize<'de> for TypedNodeHandle<N> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let id = NodeId::deserialize(deserializer)?;
Ok(Self::new(id))
}
}
pub struct EdgeBuilder<'a, S> {
graph: &'a mut BspGraph<S>,
id: EdgeId,
}
impl<S> EdgeBuilder<'_, S> {
pub fn when(self, condition: Condition<S>) -> Self {
self.graph.edges[self.id].condition = condition;
self
}
pub fn with_max_iterations(self, max: u64) -> Self {
self.graph.edges[self.id].max_iterations = Some(max);
self
}
pub fn with_ports(self, source_port: PortIndex, target_port: PortIndex) -> Self {
let edge = &mut self.graph.edges[self.id];
edge.source_port = Some(source_port);
edge.target_port = Some(target_port);
self.graph.reindex_edge_ports(self.id);
self
}
#[must_use]
pub const fn id(&self) -> EdgeId {
self.id
}
}
pub struct BspGraph<S = ()> {
pub(crate) nodes: IndexMap<NodeId, Box<dyn BspNode<S>>>,
pub(crate) edges: Vec<Edge<S>>,
pub(crate) static_inputs: IndexMap<InputPin, BspValue>,
pub(crate) conditions: ConditionMap<S>,
pub(crate) mergers: MergerMap<S>,
pub(crate) labels: IndexMap<NodeId, String>,
pub(crate) entry: Vec<NodeId>,
pub(crate) node_defs: IndexMap<NodeId, crate::def::NodeDef>,
pub(crate) incoming: IndexMap<NodeId, Vec<EdgeId>>,
pub(crate) incoming_by_pin: IndexMap<InputPin, Vec<EdgeId>>,
pub(crate) outgoing: IndexMap<NodeId, Vec<EdgeId>>,
next_node_id: NodeId,
}
impl<S> Default for BspGraph<S> {
fn default() -> Self {
Self::new()
}
}
impl<S> BspGraph<S> {
#[must_use]
pub fn new() -> Self {
Self {
nodes: IndexMap::new(),
edges: Vec::new(),
static_inputs: IndexMap::new(),
conditions: ConditionMap::default(),
mergers: MergerMap::default(),
labels: IndexMap::new(),
entry: Vec::new(),
node_defs: IndexMap::new(),
incoming: IndexMap::new(),
incoming_by_pin: IndexMap::new(),
outgoing: IndexMap::new(),
next_node_id: 1,
}
}
pub fn add_node<N: BspNode<S> + 'static>(&mut self, node: N) -> TypedNodeHandle<N> {
let id = self.next_node_id;
self.next_node_id += 1;
self.nodes.insert(id, Box::new(node));
self.labels.insert(id, id.to_string());
TypedNodeHandle::new(id)
}
pub fn add_node_with_id(&mut self, id: impl Into<NodeId>, node: Box<dyn BspNode<S>>) {
let id = id.into();
self.nodes.insert(id, node);
self.labels.entry(id).or_insert_with(|| id.to_string());
if id >= self.next_node_id {
self.next_node_id = id + 1;
}
}
pub fn add_link(&mut self, source: OutputPin, target: InputPin) {
let id = self.edges.len();
let mut edge = Edge::control(source.node_id, target.node_id);
edge.source_port = Some(source.port);
edge.target_port = Some(target.port);
self.push_edge(edge);
debug_assert_eq!(id, self.edges.len() - 1);
}
pub fn add_link_indices(
&mut self,
source_node: NodeId,
source_port: PortIndex,
target_node: NodeId,
target_port: PortIndex,
) {
self.add_link(
OutputPin {
node_id: source_node,
port: source_port,
},
InputPin {
node_id: target_node,
port: target_port,
},
);
}
pub fn add_edge(
&mut self,
source: impl Into<NodeId>,
target: impl Into<NodeId>,
) -> EdgeBuilder<'_, S> {
let id = self.edges.len();
self.push_edge(Edge::control(source.into(), target.into()));
EdgeBuilder { graph: self, id }
}
pub fn add_edge_with(
&mut self,
source: impl Into<NodeId>,
target: impl Into<NodeId>,
condition: Condition<S>,
) -> EdgeId {
let id = self.edges.len();
let mut edge = Edge::control(source.into(), target.into());
edge.condition = condition;
self.push_edge(edge);
id
}
pub fn set_static_input<T: std::any::Any + Send + Sync>(&mut self, target: InputPin, value: T) {
self.static_inputs.insert(target, BspValue::new(value));
}
pub fn register_condition(
&mut self,
name: impl Into<String>,
f: impl Fn(&S, &crate::run::ValueStore) -> bool + Send + Sync + 'static,
) {
self.conditions.insert(name.into(), std::sync::Arc::new(f));
}
pub fn register_merger<M: crate::merge::Merger<S> + 'static>(
&mut self,
name: impl Into<String>,
merger: M,
) {
self.mergers
.insert(name.into(), std::sync::Arc::new(merger));
}
pub fn add_entry(&mut self, id: impl Into<NodeId>) {
self.entry.push(id.into());
}
#[must_use]
pub fn node_label(&self, id: NodeId) -> Option<&str> {
self.labels.get(&id).map(String::as_str)
}
pub fn node_ids(&self) -> impl Iterator<Item = NodeId> + '_ {
self.nodes.keys().copied()
}
#[must_use]
pub fn edges(&self) -> &[Edge<S>] {
&self.edges
}
pub(crate) fn entries(&self) -> indexmap::IndexSet<NodeId> {
let mut entries: indexmap::IndexSet<NodeId> = indexmap::IndexSet::new();
for &id in &self.entry {
entries.insert(id);
}
for &id in self.nodes.keys() {
if self.incoming.get(&id).is_none_or(Vec::is_empty) {
entries.insert(id);
}
}
entries
}
pub(crate) fn push_edge(&mut self, edge: Edge<S>) {
let id = self.edges.len();
if let Some(target_port) = edge.target_port {
self.incoming_by_pin
.entry(InputPin {
node_id: edge.target,
port: target_port,
})
.or_default()
.push(id);
}
self.incoming.entry(edge.target).or_default().push(id);
self.outgoing.entry(edge.source).or_default().push(id);
self.edges.push(edge);
}
fn reindex_edge_ports(&mut self, id: EdgeId) {
let edge = &self.edges[id];
if let Some(target_port) = edge.target_port {
let pin = InputPin {
node_id: edge.target,
port: target_port,
};
let list = self.incoming_by_pin.entry(pin).or_default();
if !list.contains(&id) {
list.push(id);
}
}
}
#[must_use]
pub fn with_node_id(mut self, id: impl Into<NodeId>, node: Box<dyn BspNode<S>>) -> Self {
self.add_node_with_id(id, node);
self
}
#[must_use]
pub fn with_link(mut self, source: OutputPin, target: InputPin) -> Self {
self.add_link(source, target);
self
}
#[must_use]
pub fn with_static_input<T: std::any::Any + Send + Sync>(
mut self,
target: InputPin,
value: T,
) -> Self {
self.set_static_input(target, value);
self
}
#[must_use]
pub fn with_condition(
mut self,
name: impl Into<String>,
f: impl Fn(&S, &crate::run::ValueStore) -> bool + Send + Sync + 'static,
) -> Self {
self.register_condition(name, f);
self
}
#[must_use]
pub fn with_merger<M: crate::merge::Merger<S> + 'static>(
mut self,
name: impl Into<String>,
merger: M,
) -> Self {
self.register_merger(name, merger);
self
}
}