oxigdal_temporal/
error.rs1use thiserror::Error;
7
8pub type Result<T> = std::result::Result<T, TemporalError>;
10
11#[derive(Debug, Error)]
13pub enum TemporalError {
14 #[error("Invalid temporal input: {0}")]
16 InvalidInput(String),
17
18 #[error("Insufficient temporal data: {0}")]
20 InsufficientData(String),
21
22 #[error("Time index out of bounds: {index} (valid range: {min}..{max})")]
24 TimeIndexOutOfBounds {
25 index: usize,
27 min: usize,
29 max: usize,
31 },
32
33 #[error("Invalid time range: start={start}, end={end}")]
35 InvalidTimeRange {
36 start: String,
38 end: String,
40 },
41
42 #[error("Gap detected in time series at position {position}")]
44 GapDetected {
45 position: usize,
47 },
48
49 #[error("Temporal dimension mismatch: expected {expected}, got {actual}")]
51 DimensionMismatch {
52 expected: String,
54 actual: String,
56 },
57
58 #[error("Invalid temporal parameter '{param}': {reason}")]
60 InvalidParameter {
61 param: String,
63 reason: String,
65 },
66
67 #[error("Compositing failed: {0}")]
69 CompositingError(String),
70
71 #[error("Temporal interpolation failed: {0}")]
73 InterpolationError(String),
74
75 #[error("Temporal aggregation failed: {0}")]
77 AggregationError(String),
78
79 #[error("Change detection failed: {0}")]
81 ChangeDetectionError(String),
82
83 #[error("Trend analysis failed: {0}")]
85 TrendAnalysisError(String),
86
87 #[error("Phenology analysis failed: {0}")]
89 PhenologyError(String),
90
91 #[error("Data cube operation failed: {0}")]
93 DataCubeError(String),
94
95 #[error("Temporal metadata error: {0}")]
97 MetadataError(String),
98
99 #[error("Storage error: {0}")]
101 StorageError(String),
102
103 #[cfg(feature = "zarr")]
105 #[error("Zarr error: {0}")]
106 ZarrError(String),
107
108 #[error("Analytics error: {0}")]
110 AnalyticsError(#[from] oxigdal_analytics::error::AnalyticsError),
111
112 #[error("Core error: {0}")]
114 CoreError(#[from] oxigdal_core::error::OxiGdalError),
115
116 #[error("DateTime parse error: {0}")]
118 DateTimeParseError(String),
119
120 #[error("I/O error: {0}")]
122 IoError(#[from] std::io::Error),
123
124 #[error("Serialization error: {0}")]
126 SerializationError(#[from] serde_json::Error),
127}
128
129impl TemporalError {
130 pub fn invalid_input(msg: impl Into<String>) -> Self {
132 Self::InvalidInput(msg.into())
133 }
134
135 pub fn insufficient_data(msg: impl Into<String>) -> Self {
137 Self::InsufficientData(msg.into())
138 }
139
140 pub fn time_index_out_of_bounds(index: usize, min: usize, max: usize) -> Self {
142 Self::TimeIndexOutOfBounds { index, min, max }
143 }
144
145 pub fn invalid_time_range(start: impl Into<String>, end: impl Into<String>) -> Self {
147 Self::InvalidTimeRange {
148 start: start.into(),
149 end: end.into(),
150 }
151 }
152
153 pub fn gap_detected(position: usize) -> Self {
155 Self::GapDetected { position }
156 }
157
158 pub fn dimension_mismatch(expected: impl Into<String>, actual: impl Into<String>) -> Self {
160 Self::DimensionMismatch {
161 expected: expected.into(),
162 actual: actual.into(),
163 }
164 }
165
166 pub fn invalid_parameter(param: impl Into<String>, reason: impl Into<String>) -> Self {
168 Self::InvalidParameter {
169 param: param.into(),
170 reason: reason.into(),
171 }
172 }
173
174 pub fn compositing_error(msg: impl Into<String>) -> Self {
176 Self::CompositingError(msg.into())
177 }
178
179 pub fn interpolation_error(msg: impl Into<String>) -> Self {
181 Self::InterpolationError(msg.into())
182 }
183
184 pub fn aggregation_error(msg: impl Into<String>) -> Self {
186 Self::AggregationError(msg.into())
187 }
188
189 pub fn change_detection_error(msg: impl Into<String>) -> Self {
191 Self::ChangeDetectionError(msg.into())
192 }
193
194 pub fn trend_analysis_error(msg: impl Into<String>) -> Self {
196 Self::TrendAnalysisError(msg.into())
197 }
198
199 pub fn phenology_error(msg: impl Into<String>) -> Self {
201 Self::PhenologyError(msg.into())
202 }
203
204 pub fn datacube_error(msg: impl Into<String>) -> Self {
206 Self::DataCubeError(msg.into())
207 }
208
209 pub fn metadata_error(msg: impl Into<String>) -> Self {
211 Self::MetadataError(msg.into())
212 }
213
214 pub fn storage_error(msg: impl Into<String>) -> Self {
216 Self::StorageError(msg.into())
217 }
218
219 #[cfg(feature = "zarr")]
221 pub fn zarr_error(msg: impl Into<String>) -> Self {
222 Self::ZarrError(msg.into())
223 }
224
225 pub fn datetime_parse_error(msg: impl Into<String>) -> Self {
227 Self::DateTimeParseError(msg.into())
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn test_error_creation() {
237 let err = TemporalError::invalid_input("test");
238 assert!(matches!(err, TemporalError::InvalidInput(_)));
239
240 let err = TemporalError::dimension_mismatch("100x100x10", "100x100x5");
241 assert!(matches!(err, TemporalError::DimensionMismatch { .. }));
242
243 let err = TemporalError::time_index_out_of_bounds(15, 0, 10);
244 assert!(matches!(err, TemporalError::TimeIndexOutOfBounds { .. }));
245 }
246
247 #[test]
248 fn test_error_display() {
249 let err = TemporalError::invalid_input("invalid timestamp format");
250 assert_eq!(
251 format!("{}", err),
252 "Invalid temporal input: invalid timestamp format"
253 );
254
255 let err = TemporalError::dimension_mismatch("100x100x10", "100x100x5");
256 assert_eq!(
257 format!("{}", err),
258 "Temporal dimension mismatch: expected 100x100x10, got 100x100x5"
259 );
260
261 let err = TemporalError::time_index_out_of_bounds(15, 0, 10);
262 assert_eq!(
263 format!("{}", err),
264 "Time index out of bounds: 15 (valid range: 0..10)"
265 );
266 }
267
268 #[test]
269 fn test_invalid_time_range() {
270 let err = TemporalError::invalid_time_range("2023-12-31", "2023-01-01");
271 assert!(matches!(err, TemporalError::InvalidTimeRange { .. }));
272 assert!(format!("{}", err).contains("2023-12-31"));
273 assert!(format!("{}", err).contains("2023-01-01"));
274 }
275
276 #[test]
277 fn test_gap_detected() {
278 let err = TemporalError::gap_detected(5);
279 assert!(matches!(err, TemporalError::GapDetected { position: 5 }));
280 }
281}