juncture_core/edge/compiled.rs
1use crate::{State, edge::Router};
2use std::{collections::HashMap, sync::Arc};
3
4/// Trigger table for compiled graph execution
5///
6/// Maps nodes to their outgoing edges (which nodes they trigger) and
7/// incoming edges (what triggers them).
8///
9/// # Examples
10///
11/// ```ignore
12/// use juncture_core::edge::{TriggerTable, CompiledEdge, TriggerSource};
13/// use std::collections::HashMap;
14///
15/// let mut trigger_table = TriggerTable::<MyState>::new();
16/// trigger_table.outgoing.insert(
17/// "node_a".to_string(),
18/// vec![CompiledEdge::Fixed { target: "node_b".to_string() }],
19/// );
20/// ```
21///
22/// [`MyState`]: crate::State
23#[derive(Clone, Debug)]
24pub struct TriggerTable<S: State> {
25 /// Map of node name to its outgoing edges
26 pub outgoing: HashMap<String, Vec<CompiledEdge<S>>>,
27
28 /// Map of node name to sources that trigger it
29 pub incoming: HashMap<String, Vec<TriggerSource>>,
30}
31
32impl<S: State> Default for TriggerTable<S> {
33 fn default() -> Self {
34 Self {
35 outgoing: HashMap::new(),
36 incoming: HashMap::new(),
37 }
38 }
39}
40
41impl<S: State> TriggerTable<S> {
42 /// Create a new empty trigger table
43 #[must_use]
44 pub fn new() -> Self {
45 Self::default()
46 }
47
48 /// Add an outgoing edge for a node
49 pub fn add_outgoing(&mut self, from: String, edge: CompiledEdge<S>) {
50 self.outgoing.entry(from).or_default().push(edge);
51 }
52
53 /// Add an incoming trigger for a node
54 pub fn add_incoming(&mut self, to: String, source: TriggerSource) {
55 self.incoming.entry(to).or_default().push(source);
56 }
57
58 /// Get all nodes that have outgoing edges
59 #[must_use = "iterators are lazy and do nothing unless consumed"]
60 pub fn sources(&self) -> impl Iterator<Item = &String> {
61 self.outgoing.keys()
62 }
63
64 /// Get all nodes that have incoming edges
65 pub fn targets(&self) -> impl Iterator<Item = &String> {
66 self.incoming.keys()
67 }
68}
69
70/// Compiled edge for efficient execution
71///
72/// Represents a fixed or conditional edge after graph compilation.
73#[derive(Clone)]
74pub enum CompiledEdge<S: State> {
75 /// Fixed edge to a single target
76 Fixed {
77 /// Target node name
78 target: String,
79 },
80
81 /// Conditional edge with router
82 Conditional {
83 /// Router function
84 router: Arc<dyn Router<S>>,
85 /// Path mapping for validation
86 path_map: super::PathMap,
87 },
88}
89
90impl<S: State> std::fmt::Debug for CompiledEdge<S> {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 match self {
93 Self::Fixed { target } => f.debug_tuple("Fixed").field(target).finish(),
94 Self::Conditional { path_map, .. } => {
95 f.debug_tuple("Conditional").field(path_map).finish()
96 }
97 }
98 }
99}
100
101/// Source of a node trigger
102///
103/// Indicates what causes a node to be scheduled for execution.
104#[derive(Clone, Debug, PartialEq, Eq)]
105pub enum TriggerSource {
106 /// Triggered by an edge from another node
107 Edge {
108 /// Source node name
109 from: String,
110 },
111
112 /// Triggered by a Send operation
113 Send {
114 /// Source node name
115 from: String,
116 },
117}
118
119// Rust guideline compliant 2025-01-18