Skip to main content

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