1use sim_kernel::{Cx, Error, Expr, Result, Symbol};
4
5use crate::{
6 Budget, Cell, Edge, EdgeId, Graph, Node, NodeId, PortRef, Scheduler, TopologyConnection,
7 capability::topology_write_capability, compile_graph, site::connection_from_graph,
8};
9
10mod data;
11
12#[derive(Clone, Debug)]
14pub struct TopologyPatch {
15 pub ops: Vec<PatchOp>,
17}
18
19impl TopologyPatch {
20 pub fn from_expr(expr: &Expr) -> Result<Self> {
22 let ops = data::parse_patch_ops(expr)?;
23 if ops.is_empty() {
24 return Err(patch_error("patch requires at least one operation"));
25 }
26 Ok(Self { ops })
27 }
28
29 pub fn to_expr(&self) -> Expr {
31 data::patch_ops_to_expr(&self.ops)
32 }
33}
34
35#[derive(Clone, Debug)]
37pub enum PatchOp {
38 AddNode(Node),
40 RemoveNode(NodeId),
42 ReplaceNode {
44 id: NodeId,
46 node: Node,
48 },
49 AddEdge {
51 edge: Edge,
53 explicit_id: bool,
55 },
56 RemoveEdge {
58 from: PortRef,
60 to: PortRef,
62 },
63 ReplaceEdge {
65 from: PortRef,
67 to: PortRef,
69 edge: Edge,
71 explicit_id: bool,
73 },
74 AddCell(Cell),
76 SetBudget(Budget),
78 SetScheduler(Scheduler),
80 SetMetadata {
82 key: Symbol,
84 value: Expr,
86 },
87}
88
89pub fn apply_topology_patch(cx: &mut Cx, source: &Graph, patch: &TopologyPatch) -> Result<Graph> {
91 cx.require(&topology_write_capability())?;
92 let graph = apply_topology_patch_ops(source, patch)?;
93 compile_graph(cx, &graph)?;
94 Ok(graph)
95}
96
97pub fn apply_topology_patch_ops(source: &Graph, patch: &TopologyPatch) -> Result<Graph> {
130 let mut graph = source.clone();
131 for op in &patch.ops {
132 apply_op(&mut graph, op)?;
133 }
134 Ok(graph)
135}
136
137pub fn patched_connection(
139 cx: &mut Cx,
140 source: &Graph,
141 patch: &TopologyPatch,
142) -> Result<TopologyConnection> {
143 let graph = apply_topology_patch(cx, source, patch)?;
144 connection_from_graph(cx, &graph)
145}
146
147fn apply_op(graph: &mut Graph, op: &PatchOp) -> Result<()> {
148 match op {
149 PatchOp::AddNode(node) => graph.nodes.push(node.clone()),
150 PatchOp::RemoveNode(id) => remove_node(graph, id)?,
151 PatchOp::ReplaceNode { id, node } => replace_node(graph, id, node)?,
152 PatchOp::AddEdge { edge, explicit_id } => add_edge(graph, edge, *explicit_id),
153 PatchOp::RemoveEdge { from, to } => remove_edge(graph, from, to)?,
154 PatchOp::ReplaceEdge {
155 from,
156 to,
157 edge,
158 explicit_id,
159 } => replace_edge(graph, from, to, edge, *explicit_id)?,
160 PatchOp::AddCell(cell) => graph.cells.push(cell.clone()),
161 PatchOp::SetBudget(budget) => graph.budget = budget.clone(),
162 PatchOp::SetScheduler(scheduler) => graph.scheduler = scheduler.clone(),
163 PatchOp::SetMetadata { key, value } => set_metadata(graph, key, value.clone()),
164 }
165 Ok(())
166}
167
168fn remove_node(graph: &mut Graph, id: &NodeId) -> Result<()> {
169 let before = graph.nodes.len();
170 graph.nodes.retain(|node| &node.id != id);
171 if graph.nodes.len() == before {
172 return Err(patch_error(format!(
173 "remove-node target {} does not exist",
174 id.as_symbol()
175 )));
176 }
177 Ok(())
178}
179
180fn replace_node(graph: &mut Graph, id: &NodeId, node: &Node) -> Result<()> {
181 if &node.id != id {
182 return Err(patch_error(format!(
183 "replace-node replacement id {} does not match target {}",
184 node.id.as_symbol(),
185 id.as_symbol()
186 )));
187 }
188 let Some(slot) = graph.nodes.iter_mut().find(|existing| &existing.id == id) else {
189 return Err(patch_error(format!(
190 "replace-node target {} does not exist",
191 id.as_symbol()
192 )));
193 };
194 *slot = node.clone();
195 Ok(())
196}
197
198fn add_edge(graph: &mut Graph, edge: &Edge, explicit_id: bool) {
199 let mut edge = edge.clone();
200 if !explicit_id {
201 edge.id = next_edge_id(graph);
202 }
203 graph.edges.push(edge);
204}
205
206fn remove_edge(graph: &mut Graph, from: &PortRef, to: &PortRef) -> Result<()> {
207 let before = graph.edges.len();
208 graph
209 .edges
210 .retain(|edge| &edge.from != from || &edge.to != to);
211 if graph.edges.len() == before {
212 return Err(patch_error("remove-edge target does not exist"));
213 }
214 Ok(())
215}
216
217fn replace_edge(
218 graph: &mut Graph,
219 from: &PortRef,
220 to: &PortRef,
221 edge: &Edge,
222 explicit_id: bool,
223) -> Result<()> {
224 let Some(slot) = graph
225 .edges
226 .iter_mut()
227 .find(|existing| &existing.from == from && &existing.to == to)
228 else {
229 return Err(patch_error("replace-edge target does not exist"));
230 };
231 let mut replacement = edge.clone();
232 if !explicit_id {
233 replacement.id = slot.id;
234 }
235 *slot = replacement;
236 Ok(())
237}
238
239fn set_metadata(graph: &mut Graph, key: &Symbol, value: Expr) {
240 if let Some((_, existing)) = graph.metadata.iter_mut().find(|(name, _)| name == key) {
241 *existing = value;
242 } else {
243 graph.metadata.push((key.clone(), value));
244 }
245}
246
247fn next_edge_id(graph: &Graph) -> EdgeId {
248 EdgeId::new(
249 graph
250 .edges
251 .iter()
252 .map(|edge| edge.id.0)
253 .max()
254 .unwrap_or(0)
255 .saturating_add(1),
256 )
257}
258
259fn patch_error(message: impl Into<String>) -> Error {
260 Error::Eval(format!("topology patch error: {}", message.into()))
261}