Skip to main content

audio_graph_bsd/
error.rs

1//! Graph construction and compilation errors.
2
3use crate::graph::{NodeId, PortIdx};
4use thiserror::Error;
5
6/// Errors raised while building or compiling an audio [`Graph`](crate::Graph).
7///
8/// Every variant describes a construction- or compile-time failure. None of
9/// these errors can originate on the real-time thread: link validation and
10/// topological sorting both run in [`Graph::compile`](crate::Graph::compile),
11/// and the only RT-path error is [`GraphError::NotCompiled`] (a single branch).
12#[derive(Debug, Clone, PartialEq, Eq, Error)]
13pub enum GraphError {
14    /// The graph contains a cycle, so no valid processing order exists.
15    ///
16    /// The accompanying node ids are those that remain after Kahn's algorithm
17    /// could make no further progress — i.e. the nodes participating in (or
18    /// reachable into) the cycle.
19    #[error("cycle detected in graph; unresolved nodes: {nodes:?}")]
20    CycleDetected {
21        /// Node ids that could not be ordered (part of / feeding a cycle).
22        nodes: Vec<NodeId>,
23    },
24
25    /// A referenced port does not exist on the named node.
26    #[error("port {port} not found on node {node}")]
27    PortNotFound {
28        /// The node that was addressed.
29        node: NodeId,
30        /// The port index that does not exist.
31        port: PortIdx,
32    },
33
34    /// Two ports with incompatible directions were linked.
35    ///
36    /// A valid edge connects an **output** port to an **input** port.
37    #[error("port direction mismatch: {from:?} -> {to:?} (expected Output -> Input)")]
38    PortDirectionMismatch {
39        /// The `(node, port)` that should have been an output.
40        from: (NodeId, PortIdx),
41        /// The `(node, port)` that should have been an input.
42        to: (NodeId, PortIdx),
43    },
44
45    /// Two ports with mismatched channel counts or sample formats were linked.
46    #[error("ports incompatible: {from:?} -> {to:?} (channels or sample format differ)")]
47    PortIncompatible {
48        /// The upstream output `(node, port)`.
49        from: (NodeId, PortIdx),
50        /// The downstream input `(node, port)`.
51        to: (NodeId, PortIdx),
52    },
53
54    /// [`Graph::process_cycle`](crate::Graph::process_cycle) was called before
55    /// the graph was compiled.
56    #[error("graph is not compiled; call Graph::compile first")]
57    NotCompiled,
58
59    /// [`Graph::compile`](crate::Graph::compile) was called on an
60    /// already-compiled graph.
61    #[error("graph is already compiled")]
62    AlreadyCompiled,
63
64    /// A referenced node id does not exist in the graph.
65    #[error("node {0} not found")]
66    NodeNotFound(NodeId),
67}
68
69#[cfg(test)]
70mod tests {
71    use super::GraphError;
72
73    #[test]
74    fn cycle_detected_display_lists_nodes() {
75        let e = GraphError::CycleDetected { nodes: vec![2, 3] };
76        assert_eq!(
77            e.to_string(),
78            "cycle detected in graph; unresolved nodes: [2, 3]"
79        );
80    }
81
82    #[test]
83    fn port_not_found_display() {
84        let e = GraphError::PortNotFound { node: 1, port: 2 };
85        assert_eq!(e.to_string(), "port 2 not found on node 1");
86    }
87
88    #[test]
89    fn port_direction_mismatch_display() {
90        let e = GraphError::PortDirectionMismatch {
91            from: (0, 1),
92            to: (2, 0),
93        };
94        assert!(e.to_string().contains("Output -> Input"));
95    }
96
97    #[test]
98    fn port_incompatible_display() {
99        let e = GraphError::PortIncompatible {
100            from: (0, 0),
101            to: (1, 0),
102        };
103        assert!(e.to_string().contains("incompatible"));
104    }
105
106    #[test]
107    fn not_compiled_display() {
108        let e = GraphError::NotCompiled;
109        assert_eq!(
110            e.to_string(),
111            "graph is not compiled; call Graph::compile first"
112        );
113    }
114
115    #[test]
116    fn already_compiled_display() {
117        let e = GraphError::AlreadyCompiled;
118        assert_eq!(e.to_string(), "graph is already compiled");
119    }
120
121    #[test]
122    fn node_not_found_display() {
123        let e = GraphError::NodeNotFound(5);
124        assert_eq!(e.to_string(), "node 5 not found");
125    }
126
127    #[test]
128    fn clone_and_eq_hold() {
129        let a = GraphError::NodeNotFound(3);
130        assert_eq!(a.clone(), a);
131        assert_ne!(GraphError::NodeNotFound(1), GraphError::NotCompiled);
132    }
133}