1use powerio_core::{DiagnosticInfo, ErrorCategory};
2use thiserror::Error;
3
4use crate::diagnostics::codes;
5
6pub type Result<T> = std::result::Result<T, Error>;
7
8#[derive(Debug, Error)]
9#[non_exhaustive]
10pub enum Error {
11 #[error("io error reading {path}: {source}")]
12 Io {
13 path: String,
14 #[source]
15 source: std::io::Error,
16 },
17
18 #[error("malformed {format} JSON: {message}")]
19 Json {
20 format: &'static str,
21 message: String,
22 },
23
24 #[error("{format} read error: {message}")]
25 FormatRead {
26 format: &'static str,
27 message: String,
28 },
29
30 #[error("unknown distribution format `{0}` (expected dss, bmopf, or pmd)")]
31 UnknownFormat(String),
32
33 #[error("neutral Kron reduction failed: {message}")]
34 KronReduction { message: String },
35
36 #[error("LinDist3Flow preparation failed: {message}")]
37 LinDist3FlowPreparation { message: String },
38}
39
40impl Error {
41 #[must_use]
44 pub fn code(&self) -> &'static DiagnosticInfo {
45 match self {
46 Error::Io { .. } => &codes::READ_DIST_IO_FAILED,
47 Error::Json { .. } => &codes::PARSE_DIST_MALFORMED,
48 Error::FormatRead { .. } => &codes::PARSE_DIST_SOURCE_MALFORMED,
49 Error::UnknownFormat(_) => &codes::REQUEST_DIST_FORMAT_UNKNOWN,
50 Error::KronReduction { .. } => &codes::TRANSFORM_DIST_KRON_REDUCTION_FAILED,
51 Error::LinDist3FlowPreparation { .. } => {
52 &codes::TRANSFORM_DIST_LINDIST3FLOW_PREPARATION_FAILED
53 }
54 }
55 }
56
57 #[must_use]
59 pub fn category(&self) -> ErrorCategory {
60 match self {
61 Error::Io { .. } => ErrorCategory::Io,
62 Error::Json { .. } | Error::FormatRead { .. } => ErrorCategory::Parse,
63 Error::UnknownFormat(_) => ErrorCategory::Request,
64 Error::KronReduction { .. } | Error::LinDist3FlowPreparation { .. } => {
65 ErrorCategory::Data
66 }
67 }
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn every_error_code_publishes_the_category_the_variant_reports() {
77 let every = [
78 Error::Io {
79 path: "case.dss".into(),
80 source: std::io::Error::from(std::io::ErrorKind::NotFound),
81 },
82 Error::Json {
83 format: "BMOPF",
84 message: "top level is not an object".into(),
85 },
86 Error::FormatRead {
87 format: "case text",
88 message: "not valid UTF-8".into(),
89 },
90 Error::UnknownFormat("xyz".into()),
91 Error::KronReduction {
92 message: "ambiguous neutral".into(),
93 },
94 Error::LinDist3FlowPreparation {
95 message: "unsupported transformer".into(),
96 },
97 ];
98 for error in &every {
99 assert_eq!(
100 error.code().category,
101 Some(error.category()),
102 "{}",
103 error.code().code
104 );
105 }
106 }
107}