Skip to main content

oxigdal_embedded/
error.rs

1//! Error types for embedded OxiGDAL operations
2//!
3//! This module provides error types optimized for no_std environments with minimal memory overhead.
4
5use core::fmt;
6
7#[cfg(feature = "std")]
8use std::error::Error as StdError;
9
10/// Result type alias for embedded operations
11pub type Result<T> = core::result::Result<T, EmbeddedError>;
12
13/// Error types for embedded OxiGDAL operations
14///
15/// Designed to be lightweight and suitable for no_std environments
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum EmbeddedError {
19    /// Memory allocation failed
20    AllocationFailed,
21
22    /// Memory pool exhausted
23    PoolExhausted,
24
25    /// Buffer too small for operation
26    BufferTooSmall {
27        /// Required size
28        required: usize,
29        /// Available size
30        available: usize,
31    },
32
33    /// Invalid buffer alignment
34    InvalidAlignment {
35        /// Required alignment
36        required: usize,
37        /// Actual alignment
38        actual: usize,
39    },
40
41    /// Operation not supported in current configuration
42    UnsupportedOperation,
43
44    /// Invalid parameter provided
45    InvalidParameter,
46
47    /// Out of bounds access
48    OutOfBounds {
49        /// Index attempted
50        index: usize,
51        /// Maximum valid index
52        max: usize,
53    },
54
55    /// Power mode transition failed
56    PowerModeTransitionFailed,
57
58    /// A real hardware power transition was requested but no board-support
59    /// `PowerController` is installed (see the `power` module).
60    ///
61    /// This is returned only by the strict transition APIs (e.g.
62    /// `PowerManager::request_mode_strict`). It exists so callers that genuinely
63    /// need silicon-level control can fail loudly instead of silently recording a
64    /// mode change that never reached hardware.
65    NoPowerController,
66
67    /// Real-time deadline missed
68    DeadlineMissed {
69        /// Actual time taken (microseconds)
70        actual_us: u64,
71        /// Deadline (microseconds)
72        deadline_us: u64,
73    },
74
75    /// Resource busy
76    ResourceBusy,
77
78    /// Timeout occurred
79    Timeout,
80
81    /// Initialization failed
82    InitializationFailed,
83
84    /// Hardware error
85    HardwareError,
86
87    /// Invalid state for operation
88    InvalidState,
89
90    /// Data format error
91    FormatError,
92
93    /// Checksum mismatch
94    ChecksumMismatch,
95
96    /// Target-specific error
97    TargetSpecific(u8),
98}
99
100impl EmbeddedError {
101    /// Returns true if the error is recoverable
102    pub const fn is_recoverable(&self) -> bool {
103        matches!(
104            self,
105            Self::BufferTooSmall { .. }
106                | Self::ResourceBusy
107                | Self::Timeout
108                | Self::DeadlineMissed { .. }
109        )
110    }
111
112    /// Returns true if the error is a resource exhaustion error
113    pub const fn is_resource_exhaustion(&self) -> bool {
114        matches!(
115            self,
116            Self::AllocationFailed | Self::PoolExhausted | Self::BufferTooSmall { .. }
117        )
118    }
119
120    /// Returns true if the error is a validation error
121    pub const fn is_validation_error(&self) -> bool {
122        matches!(
123            self,
124            Self::InvalidParameter
125                | Self::InvalidAlignment { .. }
126                | Self::OutOfBounds { .. }
127                | Self::FormatError
128                | Self::ChecksumMismatch
129        )
130    }
131
132    /// Returns the error code as a u32 for FFI boundaries
133    pub const fn error_code(&self) -> u32 {
134        match self {
135            Self::AllocationFailed => 1,
136            Self::PoolExhausted => 2,
137            Self::BufferTooSmall { .. } => 3,
138            Self::InvalidAlignment { .. } => 4,
139            Self::UnsupportedOperation => 5,
140            Self::InvalidParameter => 6,
141            Self::OutOfBounds { .. } => 7,
142            Self::PowerModeTransitionFailed => 8,
143            Self::NoPowerController => 17,
144            Self::DeadlineMissed { .. } => 9,
145            Self::ResourceBusy => 10,
146            Self::Timeout => 11,
147            Self::InitializationFailed => 12,
148            Self::HardwareError => 13,
149            Self::InvalidState => 14,
150            Self::FormatError => 15,
151            Self::ChecksumMismatch => 16,
152            Self::TargetSpecific(_) => 100,
153        }
154    }
155}
156
157impl fmt::Display for EmbeddedError {
158    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159        match self {
160            Self::AllocationFailed => write!(f, "Memory allocation failed"),
161            Self::PoolExhausted => write!(f, "Memory pool exhausted"),
162            Self::BufferTooSmall {
163                required,
164                available,
165            } => write!(
166                f,
167                "Buffer too small: required {} bytes, available {} bytes",
168                required, available
169            ),
170            Self::InvalidAlignment { required, actual } => write!(
171                f,
172                "Invalid alignment: required {}, got {}",
173                required, actual
174            ),
175            Self::UnsupportedOperation => write!(f, "Operation not supported"),
176            Self::InvalidParameter => write!(f, "Invalid parameter"),
177            Self::OutOfBounds { index, max } => {
178                write!(f, "Out of bounds: index {} exceeds max {}", index, max)
179            }
180            Self::PowerModeTransitionFailed => write!(f, "Power mode transition failed"),
181            Self::NoPowerController => {
182                write!(f, "No hardware power controller installed")
183            }
184            Self::DeadlineMissed {
185                actual_us,
186                deadline_us,
187            } => write!(
188                f,
189                "Real-time deadline missed: {} us > {} us",
190                actual_us, deadline_us
191            ),
192            Self::ResourceBusy => write!(f, "Resource is busy"),
193            Self::Timeout => write!(f, "Operation timed out"),
194            Self::InitializationFailed => write!(f, "Initialization failed"),
195            Self::HardwareError => write!(f, "Hardware error"),
196            Self::InvalidState => write!(f, "Invalid state"),
197            Self::FormatError => write!(f, "Data format error"),
198            Self::ChecksumMismatch => write!(f, "Checksum mismatch"),
199            Self::TargetSpecific(code) => write!(f, "Target-specific error: {}", code),
200        }
201    }
202}
203
204#[cfg(feature = "std")]
205impl StdError for EmbeddedError {}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    #[test]
212    fn test_error_categories() {
213        let err = EmbeddedError::BufferTooSmall {
214            required: 100,
215            available: 50,
216        };
217        assert!(err.is_recoverable());
218        assert!(err.is_resource_exhaustion());
219        assert!(!err.is_validation_error());
220
221        let err = EmbeddedError::InvalidParameter;
222        assert!(!err.is_recoverable());
223        assert!(!err.is_resource_exhaustion());
224        assert!(err.is_validation_error());
225    }
226
227    #[test]
228    fn test_error_codes() {
229        assert_eq!(EmbeddedError::AllocationFailed.error_code(), 1);
230        assert_eq!(EmbeddedError::PoolExhausted.error_code(), 2);
231        assert_eq!(EmbeddedError::TargetSpecific(42).error_code(), 100);
232    }
233}