Skip to main content

isothrum_compiler/
error.rs

1//! What can be wrong with a drawing, said so the user knows what to fix: one enum for the crate.
2
3use isoloom_spec::syntax::Problems;
4
5use crate::cables::LINK_BLOCK;
6
7/// The crate's result.
8pub type Result<T, E = Error> = std::result::Result<T, E>;
9
10/// Why a drawing doesn't compile.
11#[derive(Debug, thiserror::Error)]
12#[non_exhaustive]
13pub enum Error {
14    /// A cable names a node the project doesn't have.
15    #[error("link {link} refers to node `{node}`, which the project doesn't have")]
16    UnknownNode {
17        /// The cable's index.
18        link: usize,
19        /// The node it names.
20        node: String,
21    },
22    /// A cable runs from a node to itself.
23    #[error("link {link} cables `{node}` to itself")]
24    SelfLink {
25        /// The cable's index.
26        link: usize,
27        /// The node.
28        node: String,
29    },
30    /// Two cables share a port.
31    #[error("port `{port}` of `{node}` already has a cable")]
32    PortInUse {
33        /// The node.
34        node: String,
35        /// Its port.
36        port: String,
37    },
38    /// More cables than the cables' block holds.
39    #[error("{0} cables don't fit in {LINK_BLOCK}")]
40    TooManyLinks(usize),
41    /// A node is on a LAN the project doesn't have.
42    #[error("`{node}` is on LAN `{lan}`, which the project doesn't have")]
43    UnknownLan {
44        /// The node.
45        node: String,
46        /// The LAN it names.
47        lan: String,
48    },
49    /// A LAN's name is one the cables use (`link-<n>`).
50    #[error("`{0}` is a name the cables use; call the LAN something else")]
51    ReservedLanName(String),
52    /// A LAN's CIDR isn't `a.b.c.d/n`.
53    #[error("LAN `{lan}`: `{cidr}` isn't a block like 10.10.1.0/24")]
54    BadCidr {
55        /// The LAN.
56        lan: String,
57        /// Its block as drawn.
58        cidr: String,
59    },
60    /// Two nodes ask for the same address on a LAN.
61    #[error("on LAN `{lan}`, `{first}` and `{second}` both ask for .{octet}")]
62    AddressTaken {
63        /// The LAN.
64        lan: String,
65        /// The address's last octet.
66        octet: u8,
67        /// The node that asked first.
68        first: String,
69        /// The other one.
70        second: String,
71    },
72    /// A LAN has more machines than free addresses.
73    #[error("LAN `{lan}` ({cidr}) has no free address left; use a larger block")]
74    LanFull {
75        /// The LAN.
76        lan: String,
77        /// Its block.
78        cidr: String,
79    },
80    /// A switch has the name of a LAN: its VLANs nest under a LAN of that name in the spec.
81    #[error("switch `{switch}` has the name of a LAN; rename one of them")]
82    SwitchNameTaken {
83        /// The switch.
84        switch: String,
85    },
86    /// A port name the compiler made breaks Isoloom's rule (a bug: the names are generated).
87    #[error("the name of port {port} of `{node}`")]
88    PortName {
89        /// The node.
90        node: String,
91        /// The port.
92        port: u16,
93        /// Why the name was refused.
94        #[source]
95        source: isoloom_spec::Error,
96    },
97    /// The spec couldn't be written as YAML.
98    #[error("writing the spec")]
99    Yaml(#[source] serde_yaml_ng::Error),
100    /// Isoloom doesn't read the spec the compiler wrote (a bug: the compiler writes only what
101    /// Isoloom's format holds).
102    #[error("Isoloom doesn't read the compiled spec: {}", lines(.0))]
103    Unreadable(Problems),
104}
105
106/// The problems, on one line, separated by semicolons.
107fn lines(problems: &Problems) -> String {
108    problems.iter().map(isoloom_spec::syntax::Problem::line).collect::<Vec<_>>().join("; ")
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn a_spec_isoloom_does_not_read_says_each_problem() {
117        let problems = isoloom_spec::syntax::parse("version: 1\nname: x\nmachines:\n  a:\n    networks: { lan: x }\n  b:\n    color: red\n").unwrap_err();
118        assert!(problems.len() >= 2, "{problems:?}");
119        let said = Error::Unreadable(problems.clone()).to_string();
120        let expected: Vec<String> = problems.iter().map(isoloom_spec::syntax::Problem::line).collect();
121        assert_eq!(said, format!("Isoloom doesn't read the compiled spec: {}", expected.join("; ")));
122    }
123}