1#[cfg(feature = "alloc")]
9use alloc::string::String;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17#[non_exhaustive]
18pub enum FormatError {
19 BadMagic,
21 UnsupportedVersion,
23 Truncated,
25 Unaligned,
27 InvalidNodeIndex,
29 InvalidFeatureIndex,
31 MisalignedTreeOffset,
33}
34
35impl core::fmt::Display for FormatError {
36 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37 match self {
38 FormatError::BadMagic => write!(f, "bad magic: expected \"IRIT\""),
39 FormatError::UnsupportedVersion => write!(f, "unsupported format version"),
40 FormatError::Truncated => write!(f, "buffer truncated"),
41 FormatError::Unaligned => write!(f, "buffer not 4-byte aligned"),
42 FormatError::InvalidNodeIndex => write!(f, "node child index out of bounds"),
43 FormatError::InvalidFeatureIndex => write!(f, "feature index exceeds n_features"),
44 FormatError::MisalignedTreeOffset => {
45 write!(f, "tree offset not aligned to node size")
46 }
47 }
48 }
49}
50
51#[cfg(feature = "alloc")]
62#[derive(Debug, Clone)]
63#[non_exhaustive]
64pub enum ConfigError {
65 OutOfRange {
74 param: &'static str,
76 constraint: &'static str,
78 value: String,
80 },
81
82 Invalid {
92 param: &'static str,
94 reason: String,
96 },
97}
98
99#[cfg(feature = "alloc")]
100impl ConfigError {
101 pub fn out_of_range(
103 param: &'static str,
104 constraint: &'static str,
105 value: impl core::fmt::Display,
106 ) -> Self {
107 use alloc::format;
108 ConfigError::OutOfRange {
109 param,
110 constraint,
111 value: format!("{}", value),
112 }
113 }
114
115 pub fn invalid(param: &'static str, reason: impl Into<String>) -> Self {
117 ConfigError::Invalid {
118 param,
119 reason: reason.into(),
120 }
121 }
122}
123
124#[cfg(feature = "alloc")]
125impl core::fmt::Display for ConfigError {
126 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
127 match self {
128 ConfigError::OutOfRange {
129 param,
130 constraint,
131 value,
132 } => write!(f, "{} {} (got {})", param, constraint, value),
133 ConfigError::Invalid { param, reason } => write!(f, "{} {}", param, reason),
134 }
135 }
136}
137
138#[cfg(feature = "std")]
140impl std::error::Error for ConfigError {}
141
142#[cfg(feature = "alloc")]
150#[derive(Debug)]
151#[non_exhaustive]
152pub enum IrithyllError {
153 InvalidConfig(ConfigError),
155 InsufficientData(String),
157 DimensionMismatch {
159 expected: usize,
161 got: usize,
163 },
164 NotTrained,
166}
167
168#[cfg(feature = "alloc")]
169impl core::fmt::Display for IrithyllError {
170 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
171 match self {
172 IrithyllError::InvalidConfig(e) => write!(f, "invalid configuration: {}", e),
173 IrithyllError::InsufficientData(msg) => write!(f, "insufficient data: {}", msg),
174 IrithyllError::DimensionMismatch { expected, got } => {
175 write!(f, "dimension mismatch: expected {}, got {}", expected, got)
176 }
177 IrithyllError::NotTrained => write!(f, "model not trained"),
178 }
179 }
180}
181
182#[cfg(feature = "alloc")]
183impl From<ConfigError> for IrithyllError {
184 fn from(e: ConfigError) -> Self {
185 IrithyllError::InvalidConfig(e)
186 }
187}
188
189#[cfg(feature = "alloc")]
191pub type Result<T> = core::result::Result<T, IrithyllError>;
192
193#[cfg(test)]
198mod tests {
199 use super::*;
200 use alloc::string::ToString;
201
202 #[test]
203 fn format_error_display() {
204 assert_eq!(
205 FormatError::BadMagic.to_string(),
206 "bad magic: expected \"IRIT\""
207 );
208 assert_eq!(FormatError::Truncated.to_string(), "buffer truncated");
209 }
210
211 #[cfg(feature = "alloc")]
212 #[test]
213 fn config_error_out_of_range_display() {
214 let e = ConfigError::out_of_range("n_steps", "must be > 0", 0);
215 assert_eq!(e.to_string(), "n_steps must be > 0 (got 0)");
216 }
217
218 #[cfg(feature = "alloc")]
219 #[test]
220 fn config_error_invalid_display() {
221 let e = ConfigError::invalid(
222 "split_reeval_interval",
223 "must be >= grace_period (200), got 50",
224 );
225 assert!(e.to_string().contains("split_reeval_interval"));
226 assert!(e.to_string().contains("must be >= grace_period"));
227 }
228
229 #[cfg(feature = "alloc")]
230 #[test]
231 fn irithyll_error_from_config_error() {
232 let ce = ConfigError::out_of_range("learning_rate", "must be in (0, 1]", 1.5);
233 let ie: IrithyllError = ce.into();
234 let msg = ie.to_string();
235 assert!(msg.contains("invalid configuration"));
236 assert!(msg.contains("learning_rate"));
237 }
238
239 #[cfg(feature = "alloc")]
240 #[test]
241 fn irithyll_error_dimension_mismatch() {
242 let e = IrithyllError::DimensionMismatch {
243 expected: 10,
244 got: 5,
245 };
246 assert_eq!(e.to_string(), "dimension mismatch: expected 10, got 5");
247 }
248
249 #[cfg(feature = "alloc")]
250 #[test]
251 fn irithyll_error_not_trained() {
252 assert_eq!(IrithyllError::NotTrained.to_string(), "model not trained");
253 }
254}