tididi_cnf/
error.rs

1//! The one error type the library returns and the `tididi` binary reports.
2
3/// Everything that stops a run before it has an answer.
4///
5/// The variant is the failure class, which fixes the status the binary exits
6/// with and the name the run record gives it ([`Error::kind`]). The string
7/// says what went wrong, in words an operator can act on;
8/// [`Display`](std::fmt::Display) prints it, and the binary prints it on
9/// stderr after `error: `.
10#[derive(Debug)]
11pub enum Error {
12    /// An option is contradictory, or inert in the mode this run resolved to.
13    Usage(String),
14    /// An environment setting is invalid, or is set where this run would
15    /// ignore it.
16    Environment(String),
17    /// A preprocessing frontend or a compile stage failed.
18    Stage(String),
19    /// The requested vtree spec cannot serve this run.
20    VtreeSpec(String),
21    /// The input could not be opened or read, or does not describe a formula.
22    Input(String),
23    /// An artifact the run was asked to write could not be produced.
24    Artifact(String),
25    /// The run ended without a count: it reached its time limit, or every
26    /// compile and recovery path gave up. The stage that gave up says on
27    /// stderr what stopped it; the message says which of these ended the run.
28    Incomplete(String),
29}
30
31impl Error {
32    /// An allocation was refused and the run has no count to print.
33    pub(crate) fn out_of_memory() -> Self {
34        Error::Incomplete(OUT_OF_MEMORY.to_string())
35    }
36
37    /// The compile, and every path behind it, gave up without a count.
38    pub(crate) fn compile_gave_up() -> Self {
39        Error::Incomplete("no answer: the compile gave up".to_owned())
40    }
41
42    /// Every stage of a projected count declined or gave up.
43    pub(crate) fn every_stage_gave_up() -> Self {
44        Error::Incomplete("no answer: every stage gave up".to_owned())
45    }
46
47    /// `map_err` adapter for a failed preprocessing or expansion stage:
48    /// `{what} failed: {error}`.
49    pub(crate) fn stage_failed<E: std::fmt::Display>(what: &'static str) -> impl FnOnce(E) -> Self {
50        move |error| Error::Stage(format!("{what} failed: {error}"))
51    }
52
53    /// The class's name, as the run record's `exit_reason` gives it: `usage`,
54    /// `environment`, `stage`, `vtree-spec`, `input`, `artifact` or
55    /// `incomplete`.
56    pub fn kind(&self) -> &'static str {
57        match self {
58            Error::Usage(_) => "usage",
59            Error::Environment(_) => "environment",
60            Error::Stage(_) => "stage",
61            Error::VtreeSpec(_) => "vtree-spec",
62            Error::Input(_) => "input",
63            Error::Artifact(_) => "artifact",
64            Error::Incomplete(_) => "incomplete",
65        }
66    }
67
68    /// The status the binary exits with for this class.
69    pub(crate) fn exit_code(&self) -> u8 {
70        match self {
71            Error::Usage(_) | Error::Environment(_) | Error::Stage(_) => 2,
72            Error::VtreeSpec(_)
73            | Error::Input(_)
74            | Error::Artifact(_)
75            | Error::Incomplete(_) => 1,
76        }
77    }
78}
79
80impl std::fmt::Display for Error {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        let message = match self {
83            Error::Usage(message)
84            | Error::Environment(message)
85            | Error::Stage(message)
86            | Error::VtreeSpec(message)
87            | Error::Input(message)
88            | Error::Artifact(message)
89            | Error::Incomplete(message) => message,
90        };
91        f.write_str(message)
92    }
93}
94
95impl std::error::Error for Error {}
96
97/// Stderr diagnostic for a run an allocation refusal ended.
98///
99/// A benchmark harness reads a process as out of memory by finding
100/// `"memory allocation of"` and `"bytes failed"` in its stderr, which is what
101/// the allocator prints when it aborts. A compile that refuses gracefully never
102/// reaches the allocator's own message, so it says the same thing here and the
103/// run is attributed the same way.
104///
105/// The compile raises it when reducing the finished diagram is refused an
106/// allocation (`compile::GiveUp::OutOfMemory`).
107const OUT_OF_MEMORY: &str = "memory allocation of unknown bytes failed while reducing \
108     the compiled diagram: the run reached its memory limit or the system ran out of \
109     memory, and has no answer. A larger memory limit gives it more room.";
110
111#[cfg(test)]
112#[path = "tests/error.rs"]
113mod tests;