use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use crate::types::*;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Node {
pub id: StableId,
pub kind: NodeKind,
pub ports: Vec<TypedPort>,
pub config: ConfigSnapshot,
pub metadata: MetadataRef,
pub confidence: ConfidenceScore,
}
impl Node {
pub fn new_processor(name: &str) -> Self {
Self {
id: StableId::new(),
kind: NodeKind::Processor,
ports: vec![
TypedPort {
name: "in".into(),
direction: PortDirection::Input,
domain: ExecutionDomain::Sample,
data_type: DataType::Audio { channels: 2 },
semantic: PortSemantic::Signal,
polarity: PortPolarity::Bipolar,
},
TypedPort {
name: "out".into(),
direction: PortDirection::Output,
domain: ExecutionDomain::Sample,
data_type: DataType::Audio { channels: 2 },
semantic: PortSemantic::Signal,
polarity: PortPolarity::Bipolar,
},
],
config: {
let mut c = BTreeMap::new();
c.insert("name".into(), ConfigValue::String(name.into()));
c
},
metadata: MetadataRef(None),
confidence: ConfidenceScore::Verified,
}
}
pub fn new_source(name: &str) -> Self {
Self {
id: StableId::new(),
kind: NodeKind::Source,
ports: vec![TypedPort {
name: "out".into(),
direction: PortDirection::Output,
domain: ExecutionDomain::Sample,
data_type: DataType::Audio { channels: 2 },
semantic: PortSemantic::Signal,
polarity: PortPolarity::Bipolar,
}],
config: {
let mut c = BTreeMap::new();
c.insert("name".into(), ConfigValue::String(name.into()));
c
},
metadata: MetadataRef(None),
confidence: ConfidenceScore::Verified,
}
}
pub fn new_sink(name: &str) -> Self {
Self {
id: StableId::new(),
kind: NodeKind::Sink,
ports: vec![TypedPort {
name: "in".into(),
direction: PortDirection::Input,
domain: ExecutionDomain::Sample,
data_type: DataType::Audio { channels: 2 },
semantic: PortSemantic::Signal,
polarity: PortPolarity::Bipolar,
}],
config: {
let mut c = BTreeMap::new();
c.insert("name".into(), ConfigValue::String(name.into()));
c
},
metadata: MetadataRef(None),
confidence: ConfidenceScore::Verified,
}
}
pub fn new_subgraph(name: &str) -> Self {
Self {
id: StableId::new(),
kind: NodeKind::SubGraph,
ports: vec![
TypedPort { name: "in".into(), direction: PortDirection::Input, domain: ExecutionDomain::Sample, data_type: DataType::Audio { channels: 2 }, semantic: PortSemantic::Signal, polarity: PortPolarity::Bipolar },
TypedPort { name: "out".into(), direction: PortDirection::Output, domain: ExecutionDomain::Sample, data_type: DataType::Audio { channels: 2 }, semantic: PortSemantic::Signal, polarity: PortPolarity::Bipolar },
],
config: {
let mut c = BTreeMap::new();
c.insert("name".into(), ConfigValue::String(name.into()));
c.insert("graph_json".into(), ConfigValue::String("{}".into()));
c
},
metadata: MetadataRef(None),
confidence: ConfidenceScore::Verified,
}
}
pub fn new_input_proxy(name: &str) -> Self {
Self {
id: StableId::new(),
kind: NodeKind::InputProxy,
ports: vec![TypedPort { name: "out".into(), direction: PortDirection::Output, domain: ExecutionDomain::Sample, data_type: DataType::Audio { channels: 2 }, semantic: PortSemantic::Signal, polarity: PortPolarity::Bipolar }],
config: {
let mut c = BTreeMap::new();
c.insert("name".into(), ConfigValue::String(name.into()));
c
},
metadata: MetadataRef(None),
confidence: ConfidenceScore::Verified,
}
}
pub fn new_output_proxy(name: &str) -> Self {
Self {
id: StableId::new(),
kind: NodeKind::OutputProxy,
ports: vec![TypedPort { name: "in".into(), direction: PortDirection::Input, domain: ExecutionDomain::Sample, data_type: DataType::Audio { channels: 2 }, semantic: PortSemantic::Signal, polarity: PortPolarity::Bipolar }],
config: {
let mut c = BTreeMap::new();
c.insert("name".into(), ConfigValue::String(name.into()));
c
},
metadata: MetadataRef(None),
confidence: ConfidenceScore::Verified,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum EdgeKind {
#[default]
Normal,
Feedback,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Modulation {
pub id: StableId,
pub source: PortRef,
pub target_node: StableId,
pub target_param: String,
pub amount: f32,
}
impl Modulation {
pub fn new(source: PortRef, target_node: StableId, target_param: String, amount: f32) -> Self {
Self {
id: StableId::new(),
source,
target_node,
target_param,
amount,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Edge {
pub id: StableId,
pub source: PortRef,
pub target: PortRef,
pub kind: EdgeKind,
#[serde(default)]
pub modulations: BTreeMap<StableId, Modulation>,
}
impl Edge {
pub fn new(source: PortRef, target: PortRef) -> Self {
Self {
id: StableId::new(),
source,
target,
kind: EdgeKind::Normal,
modulations: BTreeMap::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct Topology {
pub nodes: BTreeMap<StableId, Node>,
pub edges: BTreeMap<StableId, Edge>,
pub modulations: BTreeMap<StableId, Modulation>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct Lineage {
pub applied_patches: Vec<PatchId>,
pub history: BTreeMap<PatchId, crate::patch::Patch>,
pub snapshots: BTreeMap<String, PatchId>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct CircuitRegistry {
pub definitions: BTreeMap<StableId, crate::types::CircuitDefinition>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Graph {
pub spec_version: String,
pub topology: Topology,
pub lineage: Lineage,
pub registry: CircuitRegistry,
pub verification: Verification,
pub revision: Revision,
#[serde(skip)]
pub nodes: BTreeMap<StableId, Node>,
#[serde(skip)]
pub edges: BTreeMap<StableId, Edge>,
#[serde(skip)]
pub modulations: BTreeMap<StableId, Modulation>,
}
impl Graph {
pub fn new() -> Self {
Self {
spec_version: "1.0.0".into(),
topology: Topology::default(),
lineage: Lineage::default(),
registry: CircuitRegistry::default(),
verification: Verification {
null_test: true,
hash: String::new(),
trust_state: "verified".into(),
},
revision: Revision::zero(),
nodes: BTreeMap::new(),
edges: BTreeMap::new(),
modulations: BTreeMap::new(),
}
}
pub fn sync(&mut self) {
self.nodes = self.topology.nodes.clone();
self.edges = self.topology.edges.clone();
self.modulations = self.topology.modulations.clone();
}
pub fn squash_history(&mut self) {
let empty = Graph::new();
let patch_set = empty.diff(self);
if let Some(baseline) = patch_set.patches.first() {
self.lineage.applied_patches = vec![baseline.identity];
self.lineage.history.clear();
self.lineage.history.insert(baseline.identity, baseline.clone());
}
}
pub fn create_snapshot(&mut self, name: &str) {
if let Some(&last_patch_id) = self.lineage.applied_patches.last() {
self.lineage.snapshots.insert(name.to_string(), last_patch_id);
}
}
pub fn node(&self, id: &StableId) -> Option<&Node> {
self.topology.nodes.get(id)
}
pub fn edge(&self, id: &StableId) -> Option<&Edge> {
self.topology.edges.get(id)
}
pub fn validate_port_ref(&self, port_ref: &PortRef) -> bool {
self.topology.nodes
.get(&port_ref.node_id)
.map(|n| n.ports.iter().any(|p| p.name == port_ref.port_name))
.unwrap_or(false)
}
pub fn add_node(&mut self, node: Node) {
self.topology.nodes.insert(node.id, node);
self.sync();
}
pub fn remove_node(&mut self, id: StableId) {
self.topology.nodes.remove(&id);
let edge_ids: Vec<StableId> = self.topology.edges.iter()
.filter(|(_, e)| e.source.node_id == id || e.target.node_id == id)
.map(|(&eid, _)| eid)
.collect();
for eid in edge_ids {
self.topology.edges.remove(&eid);
}
self.sync();
}
pub fn connect(&mut self, source: PortRef, target: PortRef) -> Result<StableId, String> {
if !self.validate_port_ref(&source) { return Err(format!("Invalid source port: {:?}", source)); }
if !self.validate_port_ref(&target) { return Err(format!("Invalid target port: {:?}", target)); }
let edge = Edge::new(source, target);
let id = edge.id;
self.topology.edges.insert(id, edge);
self.sync();
Ok(id)
}
pub fn disconnect(&mut self, edge_id: StableId) -> Result<(), String> {
if self.topology.edges.remove(&edge_id).is_some() {
self.sync();
Ok(())
} else {
Err("Edge not found".into())
}
}
pub fn set_config(&mut self, node_id: StableId, key: &str, value: ConfigValue) -> Result<(), String> {
if let Some(node) = self.topology.nodes.get_mut(&node_id) {
node.config.insert(key.to_string(), value);
self.sync();
Ok(())
} else {
Err("Node not found".into())
}
}
}
impl Default for Graph {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Display for Graph {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Graph(rev={}, nodes={}, edges={})", self.revision.0, self.nodes.len(), self.edges.len())
}
}