Skip to main content

oxigdal_grib/
error.rs

1//! Error types for GRIB format parsing and processing.
2//!
3//! This module provides comprehensive error handling for GRIB1 and GRIB2 format operations,
4//! including parsing errors, validation errors, and I/O errors.
5
6use std::io;
7
8/// Result type for GRIB operations.
9pub type Result<T> = std::result::Result<T, GribError>;
10
11/// Comprehensive error type for GRIB operations.
12#[derive(Debug, thiserror::Error)]
13pub enum GribError {
14    /// I/O error occurred during file operations
15    #[error("I/O error: {0}")]
16    Io(#[from] io::Error),
17
18    /// Invalid GRIB magic number or header
19    #[error("Invalid GRIB header: expected 'GRIB' magic bytes, found {0:?}")]
20    InvalidHeader(Vec<u8>),
21
22    /// Unsupported GRIB edition
23    #[error("Unsupported GRIB edition: {0} (only GRIB1 and GRIB2 are supported)")]
24    UnsupportedEdition(u8),
25
26    /// Invalid section number
27    #[error("Invalid section number: {0}")]
28    InvalidSection(u8),
29
30    /// Missing required section
31    #[error("Missing required section: {0}")]
32    MissingSection(String),
33
34    /// Invalid section length
35    #[error("Invalid section length: expected at least {expected}, got {actual}")]
36    InvalidSectionLength {
37        /// Expected minimum length
38        expected: usize,
39        /// Actual length found
40        actual: usize,
41    },
42
43    /// Unsupported grid definition template
44    #[error("Unsupported grid definition template: {0}")]
45    UnsupportedGridTemplate(u16),
46
47    /// Unsupported product definition template
48    #[error("Unsupported product definition template: {0}")]
49    UnsupportedProductTemplate(u16),
50
51    /// Unsupported data representation template
52    #[error("Unsupported data representation template: {0}")]
53    UnsupportedDataTemplate(u16),
54
55    /// Invalid parameter code
56    #[error("Invalid parameter: discipline={discipline}, category={category}, number={number}")]
57    InvalidParameter {
58        /// WMO discipline code
59        discipline: u8,
60        /// Parameter category
61        category: u8,
62        /// Parameter number
63        number: u8,
64    },
65
66    /// Invalid grid definition
67    #[error("Invalid grid definition: {0}")]
68    InvalidGrid(String),
69
70    /// Data decoding error
71    #[error("Data decoding error: {0}")]
72    DecodingError(String),
73
74    /// Invalid data representation
75    #[error("Invalid data representation: {0}")]
76    InvalidDataRepresentation(String),
77
78    /// Invalid bitmap
79    #[error("Invalid bitmap: {0}")]
80    InvalidBitmap(String),
81
82    /// Invalid level/layer specification
83    #[error("Invalid level: type={level_type}, value={value}")]
84    InvalidLevel {
85        /// Level type code
86        level_type: u8,
87        /// Level value
88        value: f64,
89    },
90
91    /// Invalid time specification
92    #[error("Invalid time specification: {0}")]
93    InvalidTime(String),
94
95    /// Message truncated or incomplete
96    #[error("Truncated message: expected {expected} bytes, got {actual} bytes")]
97    TruncatedMessage {
98        /// Expected message size
99        expected: usize,
100        /// Actual size found
101        actual: usize,
102    },
103
104    /// Invalid end marker
105    #[error("Invalid end marker: expected '7777', found {0:?}")]
106    InvalidEndMarker(Vec<u8>),
107
108    /// Feature not implemented
109    #[error("Feature not implemented: {0}")]
110    NotImplemented(String),
111
112    /// Unsupported compression or packing method
113    #[error("Unsupported packing method: {0}")]
114    UnsupportedPacking(String),
115
116    /// Invalid bit offset or bit length
117    #[error("Invalid bit operation: {0}")]
118    InvalidBitOperation(String),
119
120    /// Coordinate conversion error
121    #[error("Coordinate conversion error: {0}")]
122    CoordinateError(String),
123
124    /// Generic parsing error
125    #[error("Parse error: {0}")]
126    ParseError(String),
127
128    /// Value out of valid range
129    #[error("Value out of range: {0}")]
130    OutOfRange(String),
131
132    /// UTF-8 decoding error
133    #[error("UTF-8 error: {0}")]
134    Utf8Error(#[from] std::str::Utf8Error),
135
136    /// Serialization/deserialization error
137    #[error("Serialization error: {0}")]
138    SerializationError(String),
139
140    /// Integration error with oxigdal-core
141    #[error("OxiGDAL integration error: {0}")]
142    IntegrationError(String),
143
144    /// Other errors
145    #[error("Other error: {0}")]
146    Other(String),
147}
148
149impl GribError {
150    /// Create a new parsing error
151    pub fn parse<S: Into<String>>(msg: S) -> Self {
152        Self::ParseError(msg.into())
153    }
154
155    /// Create a new decoding error
156    pub fn decode<S: Into<String>>(msg: S) -> Self {
157        Self::DecodingError(msg.into())
158    }
159
160    /// Create a new grid error
161    pub fn grid<S: Into<String>>(msg: S) -> Self {
162        Self::InvalidGrid(msg.into())
163    }
164
165    /// Create a new data representation error
166    pub fn data_repr<S: Into<String>>(msg: S) -> Self {
167        Self::InvalidDataRepresentation(msg.into())
168    }
169
170    /// Create a new "not implemented" error
171    pub fn not_impl<S: Into<String>>(feature: S) -> Self {
172        Self::NotImplemented(feature.into())
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn test_error_display() {
182        let err = GribError::InvalidHeader(vec![0x47, 0x52, 0x49, 0x41]);
183        assert!(err.to_string().contains("GRIB"));
184
185        let err = GribError::UnsupportedEdition(3);
186        assert!(err.to_string().contains("GRIB1 and GRIB2"));
187
188        let err = GribError::InvalidParameter {
189            discipline: 0,
190            category: 1,
191            number: 255,
192        };
193        assert!(err.to_string().contains("discipline=0"));
194    }
195
196    #[test]
197    fn test_error_constructors() {
198        let err = GribError::parse("test message");
199        assert!(matches!(err, GribError::ParseError(_)));
200
201        let err = GribError::decode("test decode");
202        assert!(matches!(err, GribError::DecodingError(_)));
203
204        let err = GribError::not_impl("complex packing");
205        assert!(matches!(err, GribError::NotImplemented(_)));
206    }
207}