tatara_engine/domain/
substrate_manager.rs1use std::collections::BTreeMap;
8
9use tatara_core::domain::convergence_graph::*;
10use tatara_core::domain::convergence_state::*;
11use tatara_core::domain::multi_distance::*;
12use tatara_core::domain::point_id::PointId;
13
14pub struct SubstrateManager {
17 substrates: BTreeMap<SubstrateType, SubstrateDAG>,
18}
19
20impl SubstrateManager {
21 pub fn new() -> Self {
22 Self {
23 substrates: BTreeMap::new(),
24 }
25 }
26
27 pub fn add_substrate(&mut self, dag: SubstrateDAG) {
29 self.substrates.insert(dag.substrate, dag);
30 }
31
32 pub fn get_substrate(&self, substrate: &SubstrateType) -> Option<&SubstrateDAG> {
34 self.substrates.get(substrate)
35 }
36
37 pub fn substrate_count(&self) -> usize {
39 self.substrates.len()
40 }
41
42 pub fn compose_graph(&self) -> ConvergenceGraph {
46 let mut graph = ConvergenceGraph::new();
47
48 for dag in self.substrates.values() {
49 for (id, point) in &dag.points {
51 graph.add_point(*id, point.clone());
52 }
53 for edge in &dag.internal_edges {
55 graph.add_edge(edge.clone());
56 }
57 for edge in &dag.cross_edges {
59 graph.add_edge(edge.clone());
60 }
61 }
62
63 graph
64 }
65
66 pub fn convergence_per_substrate(&self) -> MultiDimensionalDistance {
68 let mut distance = MultiDimensionalDistance::new();
69
70 for (substrate_type, dag) in &self.substrates {
71 if dag.points.is_empty() {
72 distance.set(*substrate_type, 0.0);
73 continue;
74 }
75
76 let max_distance: f64 = dag
77 .points
78 .values()
79 .map(|p| p.state.distance.numeric())
80 .fold(0.0_f64, f64::max);
81 distance.set(*substrate_type, max_distance);
82 }
83
84 distance
85 }
86
87 pub fn cross_substrate_edges(&self) -> Vec<(SubstrateType, SubstrateType, TypedEdge)> {
89 let mut result = Vec::new();
90
91 for (substrate_type, dag) in &self.substrates {
92 for edge in &dag.cross_edges {
93 for (other_type, other_dag) in &self.substrates {
95 if other_type != substrate_type && other_dag.points.contains_key(&edge.to) {
96 result.push((*substrate_type, *other_type, edge.clone()));
97 }
98 }
99 }
100 }
101
102 result
103 }
104}
105
106impl Default for SubstrateManager {
107 fn default() -> Self {
108 Self::new()
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 fn make_substrate_dag(substrate: SubstrateType, point_names: &[&str]) -> SubstrateDAG {
117 let mut points = BTreeMap::new();
118 let mut ids = Vec::new();
119
120 for name in point_names {
121 let id = PointId::compute(name.as_bytes(), &[], b"desired");
122 let point = ConvergencePoint {
123 name: (*name).into(),
124 description: format!("{name} point"),
125 monotone: true,
126 mechanism: ConvergenceMechanism::Local,
127 state: ConvergenceState::new(*name),
128 boundary: ConvergenceBoundary::default(),
129 point_type: ConvergencePointType::Transform,
130 horizon: ConvergenceHorizon::Bounded,
131 substrate,
132 computation_mode: ComputationMode::Mechanical,
133 };
134 points.insert(id, point);
135 ids.push(id);
136 }
137
138 let mut internal_edges = Vec::new();
139 for i in 1..ids.len() {
140 internal_edges.push(TypedEdge {
141 from: ids[i - 1],
142 to: ids[i],
143 edge_type: EdgeType::Attestation,
144 });
145 }
146
147 SubstrateDAG {
148 substrate,
149 points,
150 internal_edges,
151 cross_edges: Vec::new(),
152 bandwidth: ConvergenceBandwidth::Seconds(30),
153 }
154 }
155
156 #[test]
157 fn test_single_substrate() {
158 let mut mgr = SubstrateManager::new();
159 mgr.add_substrate(make_substrate_dag(
160 SubstrateType::Compute,
161 &["cpu_alloc", "mem_alloc", "driver_start"],
162 ));
163
164 assert_eq!(mgr.substrate_count(), 1);
165 let graph = mgr.compose_graph();
166 assert_eq!(graph.point_count(), 3);
167 assert_eq!(graph.edge_count(), 2);
168 }
169
170 #[test]
171 fn test_multi_substrate_composition() {
172 let mut mgr = SubstrateManager::new();
173 mgr.add_substrate(make_substrate_dag(SubstrateType::Compute, &["cpu", "mem"]));
174 mgr.add_substrate(make_substrate_dag(
175 SubstrateType::Network,
176 &["dns", "route"],
177 ));
178 mgr.add_substrate(make_substrate_dag(SubstrateType::Security, &["secret"]));
179
180 assert_eq!(mgr.substrate_count(), 3);
181 let graph = mgr.compose_graph();
182 assert_eq!(graph.point_count(), 5);
183 }
184
185 #[test]
186 fn test_convergence_per_substrate() {
187 let mut mgr = SubstrateManager::new();
188 mgr.add_substrate(make_substrate_dag(SubstrateType::Compute, &["a"]));
189 mgr.add_substrate(make_substrate_dag(SubstrateType::Network, &["b"]));
190
191 let dist = mgr.convergence_per_substrate();
192 assert_eq!(dist.get(&SubstrateType::Compute), 1.0);
194 assert_eq!(dist.get(&SubstrateType::Network), 1.0);
195 assert!(!dist.is_converged());
196 }
197
198 #[test]
199 fn test_empty_manager() {
200 let mgr = SubstrateManager::new();
201 assert_eq!(mgr.substrate_count(), 0);
202 let graph = mgr.compose_graph();
203 assert_eq!(graph.point_count(), 0);
204 }
205}