1use serde::{Deserialize, Serialize};
10use std::collections::BTreeMap;
11
12use crate::types::*;
13
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20pub struct Node {
21 pub id: StableId,
22 pub kind: NodeKind,
23 pub ports: Vec<TypedPort>,
24 pub config: ConfigSnapshot,
25 pub metadata: MetadataRef,
26 pub confidence: ConfidenceScore,
27}
28
29impl Node {
30 pub fn new_processor(name: &str) -> Self {
32 Self {
33 id: StableId::new(),
34 kind: NodeKind::Processor,
35 ports: vec![
36 TypedPort {
37 name: "in".into(),
38 direction: PortDirection::Input,
39 domain: ExecutionDomain::Sample,
40 data_type: DataType::Audio { channels: 2 },
41 semantic: PortSemantic::Signal,
42 polarity: PortPolarity::Bipolar,
43 },
44 TypedPort {
45 name: "out".into(),
46 direction: PortDirection::Output,
47 domain: ExecutionDomain::Sample,
48 data_type: DataType::Audio { channels: 2 },
49 semantic: PortSemantic::Signal,
50 polarity: PortPolarity::Bipolar,
51 },
52 ],
53 config: {
54 let mut c = BTreeMap::new();
55 c.insert("name".into(), ConfigValue::String(name.into()));
56 c
57 },
58 metadata: MetadataRef(None),
59 confidence: ConfidenceScore::Verified,
60 }
61 }
62
63 pub fn new_source(name: &str) -> Self {
65 Self {
66 id: StableId::new(),
67 kind: NodeKind::Source,
68 ports: vec![TypedPort {
69 name: "out".into(),
70 direction: PortDirection::Output,
71 domain: ExecutionDomain::Sample,
72 data_type: DataType::Audio { channels: 2 },
73 semantic: PortSemantic::Signal,
74 polarity: PortPolarity::Bipolar,
75 }],
76 config: {
77 let mut c = BTreeMap::new();
78 c.insert("name".into(), ConfigValue::String(name.into()));
79 c
80 },
81 metadata: MetadataRef(None),
82 confidence: ConfidenceScore::Verified,
83 }
84 }
85
86 pub fn new_sink(name: &str) -> Self {
88 Self {
89 id: StableId::new(),
90 kind: NodeKind::Sink,
91 ports: vec![TypedPort {
92 name: "in".into(),
93 direction: PortDirection::Input,
94 domain: ExecutionDomain::Sample,
95 data_type: DataType::Audio { channels: 2 },
96 semantic: PortSemantic::Signal,
97 polarity: PortPolarity::Bipolar,
98 }],
99 config: {
100 let mut c = BTreeMap::new();
101 c.insert("name".into(), ConfigValue::String(name.into()));
102 c
103 },
104 metadata: MetadataRef(None),
105 confidence: ConfidenceScore::Verified,
106 }
107 }
108
109 pub fn new_subgraph(name: &str) -> Self {
110 Self {
111 id: StableId::new(),
112 kind: NodeKind::SubGraph,
113 ports: vec![
114 TypedPort { name: "in".into(), direction: PortDirection::Input, domain: ExecutionDomain::Sample, data_type: DataType::Audio { channels: 2 }, semantic: PortSemantic::Signal, polarity: PortPolarity::Bipolar },
115 TypedPort { name: "out".into(), direction: PortDirection::Output, domain: ExecutionDomain::Sample, data_type: DataType::Audio { channels: 2 }, semantic: PortSemantic::Signal, polarity: PortPolarity::Bipolar },
116 ],
117 config: {
118 let mut c = BTreeMap::new();
119 c.insert("name".into(), ConfigValue::String(name.into()));
120 c.insert("graph_json".into(), ConfigValue::String("{}".into()));
121 c
122 },
123 metadata: MetadataRef(None),
124 confidence: ConfidenceScore::Verified,
125 }
126 }
127
128 pub fn new_input_proxy(name: &str) -> Self {
129 Self {
130 id: StableId::new(),
131 kind: NodeKind::InputProxy,
132 ports: vec![TypedPort { name: "out".into(), direction: PortDirection::Output, domain: ExecutionDomain::Sample, data_type: DataType::Audio { channels: 2 }, semantic: PortSemantic::Signal, polarity: PortPolarity::Bipolar }],
133 config: {
134 let mut c = BTreeMap::new();
135 c.insert("name".into(), ConfigValue::String(name.into()));
136 c
137 },
138 metadata: MetadataRef(None),
139 confidence: ConfidenceScore::Verified,
140 }
141 }
142
143 pub fn new_output_proxy(name: &str) -> Self {
144 Self {
145 id: StableId::new(),
146 kind: NodeKind::OutputProxy,
147 ports: vec![TypedPort { name: "in".into(), direction: PortDirection::Input, domain: ExecutionDomain::Sample, data_type: DataType::Audio { channels: 2 }, semantic: PortSemantic::Signal, polarity: PortPolarity::Bipolar }],
148 config: {
149 let mut c = BTreeMap::new();
150 c.insert("name".into(), ConfigValue::String(name.into()));
151 c
152 },
153 metadata: MetadataRef(None),
154 confidence: ConfidenceScore::Verified,
155 }
156 }
157}
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
159pub enum EdgeKind {
160 #[default]
162 Normal,
163 Feedback,
165}
166
167#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
169pub struct Modulation {
170 pub id: StableId,
171 pub source: PortRef,
172 pub target_node: StableId,
173 pub target_param: String,
174 pub amount: f32,
175}
176
177impl Modulation {
178 pub fn new(source: PortRef, target_node: StableId, target_param: String, amount: f32) -> Self {
179 Self {
180 id: StableId::new(),
181 source,
182 target_node,
183 target_param,
184 amount,
185 }
186 }
187}
188
189#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
191pub struct Edge {
192 pub id: StableId,
193 pub source: PortRef,
194 pub target: PortRef,
195 pub kind: EdgeKind,
196 #[serde(default)]
197 pub modulations: BTreeMap<StableId, Modulation>,
198}
199
200impl Edge {
201 pub fn new(source: PortRef, target: PortRef) -> Self {
202 Self {
203 id: StableId::new(),
204 source,
205 target,
206 kind: EdgeKind::Normal,
207 modulations: BTreeMap::new(),
208 }
209 }
210}
211
212#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
214pub struct Topology {
215 pub nodes: BTreeMap<StableId, Node>,
216 pub edges: BTreeMap<StableId, Edge>,
217 pub modulations: BTreeMap<StableId, Modulation>,
218}
219
220#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
222pub struct Lineage {
223 pub applied_patches: Vec<PatchId>,
224 pub history: BTreeMap<PatchId, crate::patch::Patch>,
225 pub snapshots: BTreeMap<String, PatchId>,
226}
227
228#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
230pub struct CircuitRegistry {
231 pub definitions: BTreeMap<StableId, crate::types::CircuitDefinition>,
232}
233
234
235#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
237pub struct Graph {
238 pub spec_version: String,
239 pub topology: Topology,
240 pub lineage: Lineage,
241 pub registry: CircuitRegistry,
242 pub verification: Verification,
243 pub revision: Revision,
244
245 #[serde(skip)]
247 pub nodes: BTreeMap<StableId, Node>,
248 #[serde(skip)]
249 pub edges: BTreeMap<StableId, Edge>,
250 #[serde(skip)]
251 pub modulations: BTreeMap<StableId, Modulation>,
252}
253
254impl Graph {
255 pub fn new() -> Self {
257 Self {
258 spec_version: "1.0.0".into(),
259 topology: Topology::default(),
260 lineage: Lineage::default(),
261 registry: CircuitRegistry::default(),
262 verification: Verification {
263 null_test: true,
264 hash: String::new(),
265 trust_state: "verified".into(),
266 },
267 revision: Revision::zero(),
268 nodes: BTreeMap::new(),
269 edges: BTreeMap::new(),
270 modulations: BTreeMap::new(),
271 }
272 }
273
274 pub fn sync(&mut self) {
277 self.nodes = self.topology.nodes.clone();
278 self.edges = self.topology.edges.clone();
279 self.modulations = self.topology.modulations.clone();
280 }
281
282 pub fn squash_history(&mut self) {
283 let empty = Graph::new();
284 let patch_set = empty.diff(self);
285
286 if let Some(baseline) = patch_set.patches.first() {
287 self.lineage.applied_patches = vec![baseline.identity];
288 self.lineage.history.clear();
289 self.lineage.history.insert(baseline.identity, baseline.clone());
290 }
291 }
292
293 pub fn create_snapshot(&mut self, name: &str) {
294 if let Some(&last_patch_id) = self.lineage.applied_patches.last() {
295 self.lineage.snapshots.insert(name.to_string(), last_patch_id);
296 }
297 }
298
299 pub fn node(&self, id: &StableId) -> Option<&Node> {
300 self.topology.nodes.get(id)
301 }
302
303 pub fn edge(&self, id: &StableId) -> Option<&Edge> {
304 self.topology.edges.get(id)
305 }
306
307 pub fn validate_port_ref(&self, port_ref: &PortRef) -> bool {
308 self.topology.nodes
309 .get(&port_ref.node_id)
310 .map(|n| n.ports.iter().any(|p| p.name == port_ref.port_name))
311 .unwrap_or(false)
312 }
313
314 pub fn add_node(&mut self, node: Node) {
317 self.topology.nodes.insert(node.id, node);
318 self.sync();
319 }
320
321 pub fn remove_node(&mut self, id: StableId) {
322 self.topology.nodes.remove(&id);
323 let edge_ids: Vec<StableId> = self.topology.edges.iter()
325 .filter(|(_, e)| e.source.node_id == id || e.target.node_id == id)
326 .map(|(&eid, _)| eid)
327 .collect();
328 for eid in edge_ids {
329 self.topology.edges.remove(&eid);
330 }
331 self.sync();
332 }
333
334 pub fn connect(&mut self, source: PortRef, target: PortRef) -> Result<StableId, String> {
335 if !self.validate_port_ref(&source) { return Err(format!("Invalid source port: {:?}", source)); }
336 if !self.validate_port_ref(&target) { return Err(format!("Invalid target port: {:?}", target)); }
337
338 let edge = Edge::new(source, target);
339 let id = edge.id;
340 self.topology.edges.insert(id, edge);
341 self.sync();
342 Ok(id)
343 }
344
345 pub fn disconnect(&mut self, edge_id: StableId) -> Result<(), String> {
346 if self.topology.edges.remove(&edge_id).is_some() {
347 self.sync();
348 Ok(())
349 } else {
350 Err("Edge not found".into())
351 }
352 }
353
354 pub fn set_config(&mut self, node_id: StableId, key: &str, value: ConfigValue) -> Result<(), String> {
355 if let Some(node) = self.topology.nodes.get_mut(&node_id) {
356 node.config.insert(key.to_string(), value);
357 self.sync();
358 Ok(())
359 } else {
360 Err("Node not found".into())
361 }
362 }
363}
364
365impl Default for Graph {
366 fn default() -> Self {
367 Self::new()
368 }
369}
370
371impl std::fmt::Display for Graph {
372 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
373 write!(f, "Graph(rev={}, nodes={}, edges={})", self.revision.0, self.nodes.len(), self.edges.len())
374 }
375}