1use std::error::Error;
4use std::fmt;
5
6pub type InfotheoryResult<T> = Result<T, InfotheoryError>;
8
9#[derive(Debug)]
11pub enum InfotheoryError {
12 InvalidBackendConfig(String),
14 Runtime(String),
16 Unsupported(String),
18 Io(std::io::Error),
20 Spec(crate::spec::SpecError),
22}
23
24impl InfotheoryError {
25 pub fn invalid_backend_config(message: impl Into<String>) -> Self {
27 Self::InvalidBackendConfig(message.into())
28 }
29
30 pub fn runtime(message: impl Into<String>) -> Self {
32 Self::Runtime(message.into())
33 }
34
35 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}