Skip to main content

infotheory/
error.rs

1//! Shared public error types for fallible infotheory APIs.
2
3use std::error::Error;
4use std::fmt;
5
6/// Result type used by fallible infotheory APIs.
7pub type InfotheoryResult<T> = Result<T, InfotheoryError>;
8
9/// Public error type for spec validation, backend construction, and runtime failures.
10#[derive(Debug)]
11pub enum InfotheoryError {
12    /// Invalid or unsupported backend/spec configuration supplied by the caller.
13    InvalidBackendConfig(String),
14    /// Runtime execution failure while scoring, generating, or compressing.
15    Runtime(String),
16    /// Requested operation is not supported for the chosen backend.
17    Unsupported(String),
18    /// I/O failure surfaced through infotheory APIs.
19    Io(std::io::Error),
20    /// Shared spec/config parsing error.
21    Spec(crate::spec::SpecError),
22}
23
24impl InfotheoryError {
25    /// Build an invalid-backend/spec configuration error.
26    pub fn invalid_backend_config(message: impl Into<String>) -> Self {
27        Self::InvalidBackendConfig(message.into())
28    }
29
30    /// Build a runtime execution error.
31    pub fn runtime(message: impl Into<String>) -> Self {
32        Self::Runtime(message.into())
33    }
34
35    /// Build an unsupported-operation error.
36    pub fn unsupported(message: impl Into<String>) -> Self {
37        Self::Unsupported(message.into())
38    }
39}
40
41impl fmt::Display for InfotheoryError {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        match self {
44            Self::InvalidBackendConfig(message) => {
45                write!(f, "invalid backend configuration: {message}")
46            }
47            Self::Runtime(message) => write!(f, "runtime failure: {message}"),
48            Self::Unsupported(message) => write!(f, "unsupported operation: {message}"),
49            Self::Io(err) => write!(f, "i/o failure: {err}"),
50            Self::Spec(err) => write!(f, "spec error: {err}"),
51        }
52    }
53}
54
55impl Error for InfotheoryError {
56    fn source(&self) -> Option<&(dyn Error + 'static)> {
57        match self {
58            Self::Io(err) => Some(err),
59            Self::Spec(err) => Some(err),
60            _ => None,
61        }
62    }
63}
64
65impl From<std::io::Error> for InfotheoryError {
66    fn from(value: std::io::Error) -> Self {
67        Self::Io(value)
68    }
69}
70
71impl From<crate::spec::SpecError> for InfotheoryError {
72    fn from(value: crate::spec::SpecError) -> Self {
73        Self::Spec(value)
74    }
75}
76
77impl From<String> for InfotheoryError {
78    fn from(value: String) -> Self {
79        Self::Runtime(value)
80    }
81}
82
83impl From<&str> for InfotheoryError {
84    fn from(value: &str) -> Self {
85        Self::Runtime(value.to_string())
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn constructors_and_display_messages_are_stable() {
95        assert_eq!(
96            InfotheoryError::invalid_backend_config("bad depth").to_string(),
97            "invalid backend configuration: bad depth"
98        );
99        assert_eq!(
100            InfotheoryError::runtime("decoder stalled").to_string(),
101            "runtime failure: decoder stalled"
102        );
103        assert_eq!(
104            InfotheoryError::unsupported("requires vm").to_string(),
105            "unsupported operation: requires vm"
106        );
107    }
108
109    #[test]
110    fn source_and_from_conversions_preserve_error_context() {
111        let io = std::io::Error::other("disk broke");
112        let io_error = InfotheoryError::from(io);
113        assert!(io_error.to_string().contains("i/o failure: disk broke"));
114        assert!(io_error.source().is_some());
115
116        let spec = crate::spec::SpecError::new("bad spec");
117        let spec_error = InfotheoryError::from(spec.clone());
118        assert_eq!(spec_error.to_string(), "spec error: bad spec");
119        assert_eq!(
120            spec_error
121                .source()
122                .expect("spec error should retain source")
123                .to_string(),
124            spec.to_string()
125        );
126
127        let runtime_from_string = InfotheoryError::from("runtime text");
128        assert_eq!(
129            runtime_from_string.to_string(),
130            "runtime failure: runtime text"
131        );
132
133        let runtime_from_owned = InfotheoryError::from(String::from("owned runtime"));
134        assert_eq!(
135            runtime_from_owned.to_string(),
136            "runtime failure: owned runtime"
137        );
138    }
139}