embedded_charts/
error.rs

1//! Error types and result handling for the embedded graphics chart library.
2//!
3//! This module provides comprehensive error handling for all chart operations while maintaining
4//! compatibility with both `std` and `no_std` environments. All error types implement the
5//! necessary traits for proper error propagation and debugging.
6//!
7//! # Error Categories
8//!
9//! The library uses a hierarchical error system with specific error types for different
10//! operation categories:
11//!
12//! - [`ChartError`] - Main error type for high-level chart operations
13//! - [`DataError`] - Errors related to data management and processing
14//! - [`RenderError`] - Errors during rendering and drawing operations
15//! - [`LayoutError`] - Errors in chart layout and positioning
16//! - [`AnimationError`] - Errors in animation system (feature-gated)
17//!
18//! # Error Handling Patterns
19//!
20//! ## Basic Error Handling
21//! ```rust
22//! use embedded_charts::prelude::*;
23//! use embedded_graphics::pixelcolor::Rgb565;
24//!
25//! let mut data: StaticDataSeries<Point2D, 256> = StaticDataSeries::new();
26//! match data.push(Point2D::new(0.0, 10.0)) {
27//!     Ok(()) => println!("Data added successfully"),
28//!     Err(DataError::BufferFull { .. }) => println!("Data series is full"),
29//!     Err(e) => println!("Other error: {}", e),
30//! }
31//! ```
32//!
33//! ## Using Result Types
34//! ```rust
35//! # #[cfg(feature = "line")]
36//! # {
37//! use embedded_charts::prelude::*;
38//! use embedded_charts::error::ChartResult;
39//!
40//! fn create_chart() -> ChartResult<LineChart<embedded_graphics::pixelcolor::Rgb565>> {
41//!     LineChart::builder()
42//!         .line_color(embedded_graphics::pixelcolor::Rgb565::BLUE)
43//!         .build()
44//! }
45//! # }
46//! ```
47//!
48//! ## Error Propagation
49//! ```rust
50//! use embedded_charts::prelude::*;
51//! use embedded_charts::error::{ChartResult, DataError};
52//!
53//! fn process_data() -> ChartResult<()> {
54//!     let mut data: StaticDataSeries<Point2D, 256> = StaticDataSeries::new();
55//!     data.push(Point2D::new(0.0, 10.0))?; // Automatically converts DataError to ChartError
56//!     Ok(())
57//! }
58//! ```
59//!
60//! # no_std Compatibility
61//!
62//! All error types are designed to work in `no_std` environments:
63//! - No heap allocation for error messages
64//! - Implement `core::fmt::Display` instead of `std::fmt::Display`
65//! - Optional `std::error::Error` implementation when `std` feature is enabled
66//!
67//! # Memory Efficiency
68//!
69//! Error types are designed for minimal memory usage:
70//! - All error variants are `Copy` types
71//! - No dynamic string allocation
72//! - Efficient error code representation
73
74#[cfg(feature = "std")]
75extern crate std;
76
77/// Error context information for better debugging
78///
79/// This struct provides additional context for errors while maintaining
80/// no_std compatibility by using static string references.
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct ErrorContext {
83    /// The operation that was being performed when the error occurred
84    pub operation: &'static str,
85    /// A hint for resolving the error
86    pub hint: &'static str,
87    /// Optional numeric context (e.g., data point count, buffer size)
88    pub numeric_context: Option<usize>,
89}
90
91impl ErrorContext {
92    /// Create a new error context
93    pub const fn new(operation: &'static str, hint: &'static str) -> Self {
94        Self {
95            operation,
96            hint,
97            numeric_context: None,
98        }
99    }
100
101    /// Create error context with numeric information
102    pub const fn with_numeric(operation: &'static str, hint: &'static str, value: usize) -> Self {
103        Self {
104            operation,
105            hint,
106            numeric_context: Some(value),
107        }
108    }
109}
110
111/// Main error type for chart operations.
112///
113/// This is the primary error type returned by most chart operations. It encompasses
114/// all possible error conditions that can occur during chart creation, configuration,
115/// and rendering.
116///
117/// # Error Variants
118///
119/// - **Data-related errors**: Issues with data series, points, or bounds
120/// - **Rendering errors**: Problems during drawing operations
121/// - **Configuration errors**: Invalid chart or component configuration
122/// - **Memory errors**: Buffer overflow or allocation failures
123/// - **Animation errors**: Issues with animation system (feature-gated)
124///
125/// # Examples
126///
127/// ```rust,no_run
128/// use embedded_charts::prelude::*;
129/// use embedded_charts::error::ChartError;
130///
131/// // Example function that might return a ChartError
132/// fn render_chart() -> Result<(), ChartError> {
133///     // This would be actual chart rendering logic
134///     Err(ChartError::InsufficientData)
135/// }
136///
137/// match render_chart() {
138///     Ok(()) => println!("Chart rendered successfully"),
139///     Err(ChartError::InsufficientData) => println!("Not enough data to render"),
140///     Err(ChartError::MemoryFull) => println!("Out of memory"),
141///     Err(e) => println!("Other error: {}", e),
142/// }
143/// ```
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145pub enum ChartError {
146    /// Insufficient data to render the chart.
147    ///
148    /// This error occurs when a chart requires a minimum number of data points
149    /// but the provided data series doesn't meet that requirement.
150    InsufficientData,
151    /// Invalid range specified for axis or data.
152    ///
153    /// Returned when axis ranges are invalid (e.g., min > max) or when
154    /// data values fall outside expected ranges.
155    InvalidRange,
156    /// Invalid data provided.
157    ///
158    /// Generic error for data that doesn't meet the chart's requirements,
159    /// such as NaN values, infinite values, or malformed data points.
160    InvalidData,
161    /// Memory allocation failed or buffer is full.
162    ///
163    /// Occurs when static buffers reach capacity or when memory allocation
164    /// fails in `std` environments.
165    MemoryFull,
166    /// Error occurred during rendering.
167    ///
168    /// Generic rendering error for issues during the drawing process.
169    RenderingError,
170    /// Invalid configuration provided.
171    ///
172    /// Returned when chart configuration contains invalid or conflicting settings.
173    InvalidConfiguration,
174    /// Configuration error occurred.
175    ///
176    /// More specific configuration error, typically with additional context.
177    ConfigurationError,
178    /// Render error occurred.
179    ///
180    /// Specific rendering error with detailed error information.
181    RenderError(RenderError),
182    /// Layout error occurred.
183    ///
184    /// Error during chart layout calculation or component positioning.
185    LayoutError(LayoutError),
186    /// Data error occurred.
187    ///
188    /// Specific data-related error with detailed error information.
189    DataError(DataError),
190    /// Animation error occurred.
191    ///
192    /// Error in the animation system (only available with "animations" feature).
193    #[cfg(feature = "animations")]
194    AnimationError(AnimationError),
195}
196
197/// Error type for data operations.
198///
199/// This error type covers all data-related operations including data series
200/// management, data point validation, and data processing operations.
201///
202/// # Common Scenarios
203///
204/// - Adding data to a full buffer
205/// - Accessing data with invalid indices
206/// - Processing invalid data points
207/// - Data scaling and transformation errors
208///
209/// # Examples
210///
211/// ```rust
212/// use embedded_charts::prelude::*;
213/// use embedded_charts::error::DataError;
214///
215/// let mut series = StaticDataSeries::<Point2D, 10>::new();
216///
217/// // Fill the series to capacity
218/// for i in 0..10 {
219///     series.push(Point2D::new(i as f32, i as f32)).unwrap();
220/// }
221///
222/// // This will return BufferFull error with context
223/// match series.push(Point2D::new(10.0, 10.0)) {
224///     Err(DataError::BufferFull { context: Some(ctx) }) => {
225///         println!("Series is full: {}", ctx.hint);
226///     },
227///     _ => unreachable!(),
228/// }
229/// ```
230#[derive(Debug, Clone, Copy, PartialEq, Eq)]
231pub enum DataError {
232    /// Requested data series was not found.
233    ///
234    /// Occurs when trying to access a data series by name or index
235    /// that doesn't exist in the collection.
236    SeriesNotFound {
237        /// Optional context information
238        context: Option<ErrorContext>,
239    },
240    /// Index is out of bounds for the data collection.
241    ///
242    /// Returned when accessing data with an invalid index.
243    IndexOutOfBounds {
244        /// Optional context information
245        context: Option<ErrorContext>,
246    },
247    /// Invalid data point provided.
248    ///
249    /// Occurs when a data point contains invalid values such as
250    /// NaN, infinity, or values outside acceptable ranges.
251    InvalidDataPoint {
252        /// Optional context information
253        context: Option<ErrorContext>,
254    },
255    /// Error occurred during data scaling.
256    ///
257    /// Returned when data scaling or normalization operations fail,
258    /// typically due to invalid ranges or mathematical errors.
259    ScalingError {
260        /// Optional context information
261        context: Option<ErrorContext>,
262    },
263    /// Buffer capacity exceeded.
264    ///
265    /// Occurs when trying to add data to a full static buffer.
266    BufferFull {
267        /// Optional context information
268        context: Option<ErrorContext>,
269    },
270    /// Insufficient data to perform operation.
271    ///
272    /// Returned when an operation requires a minimum amount of data
273    /// that isn't available.
274    InsufficientData {
275        /// Optional context information
276        context: Option<ErrorContext>,
277    },
278}
279
280impl DataError {
281    /// Create a BufferFull error with context
282    pub const fn buffer_full(operation: &'static str, capacity: usize) -> Self {
283        Self::BufferFull {
284            context: Some(ErrorContext::with_numeric(
285                operation,
286                "Increase buffer capacity or remove old data",
287                capacity,
288            )),
289        }
290    }
291
292    /// Create an InsufficientData error with context
293    pub const fn insufficient_data(
294        operation: &'static str,
295        _required: usize,
296        found: usize,
297    ) -> Self {
298        Self::InsufficientData {
299            context: Some(ErrorContext::with_numeric(
300                operation,
301                "Add more data points",
302                found,
303            )),
304        }
305    }
306
307    /// Create an IndexOutOfBounds error with context
308    pub const fn index_out_of_bounds(
309        operation: &'static str,
310        _index: usize,
311        length: usize,
312    ) -> Self {
313        Self::IndexOutOfBounds {
314            context: Some(ErrorContext::with_numeric(
315                operation,
316                "Check array bounds before accessing",
317                length,
318            )),
319        }
320    }
321
322    /// Create an InvalidDataPoint error with context
323    pub const fn invalid_data_point(operation: &'static str) -> Self {
324        Self::InvalidDataPoint {
325            context: Some(ErrorContext::new(
326                operation,
327                "Ensure data points contain valid finite values",
328            )),
329        }
330    }
331
332    /// Create a simple error without context (for backwards compatibility)
333    pub const fn simple(kind: DataErrorKind) -> Self {
334        match kind {
335            DataErrorKind::SeriesNotFound => Self::SeriesNotFound { context: None },
336            DataErrorKind::IndexOutOfBounds => Self::IndexOutOfBounds { context: None },
337            DataErrorKind::InvalidDataPoint => Self::InvalidDataPoint { context: None },
338            DataErrorKind::ScalingError => Self::ScalingError { context: None },
339            DataErrorKind::BufferFull => Self::BufferFull { context: None },
340            DataErrorKind::InsufficientData => Self::InsufficientData { context: None },
341        }
342    }
343}
344
345// Backwards compatibility constants
346impl DataError {
347    /// Backwards compatibility: SeriesNotFound variant without context
348    pub const SERIES_NOT_FOUND: Self = Self::SeriesNotFound { context: None };
349
350    /// Backwards compatibility: IndexOutOfBounds variant without context  
351    pub const INDEX_OUT_OF_BOUNDS: Self = Self::IndexOutOfBounds { context: None };
352
353    /// Backwards compatibility: InvalidDataPoint variant without context
354    pub const INVALID_DATA_POINT: Self = Self::InvalidDataPoint { context: None };
355
356    /// Backwards compatibility: ScalingError variant without context
357    pub const SCALING_ERROR: Self = Self::ScalingError { context: None };
358
359    /// Backwards compatibility: BufferFull variant without context
360    pub const BUFFER_FULL: Self = Self::BufferFull { context: None };
361
362    /// Backwards compatibility: InsufficientData variant without context
363    pub const INSUFFICIENT_DATA: Self = Self::InsufficientData { context: None };
364}
365
366/// Data error kinds for backwards compatibility
367#[derive(Debug, Clone, Copy, PartialEq, Eq)]
368pub enum DataErrorKind {
369    /// Requested data series was not found
370    SeriesNotFound,
371    /// Index is out of bounds for the data collection
372    IndexOutOfBounds,
373    /// Invalid data point provided
374    InvalidDataPoint,
375    /// Error occurred during data scaling
376    ScalingError,
377    /// Buffer capacity exceeded
378    BufferFull,
379    /// Insufficient data to perform operation
380    InsufficientData,
381}
382
383/// Error type for animation operations.
384///
385/// This error type is only available when the "animations" feature is enabled.
386/// It covers all animation-related operations including animation scheduling,
387/// interpolation, and state management.
388///
389/// # Common Scenarios
390///
391/// - Invalid animation duration
392/// - Animation scheduler at capacity
393/// - Interpolation failures
394/// - Invalid animation state transitions
395///
396/// # Examples
397///
398/// ```rust,no_run
399/// # #[cfg(feature = "animations")]
400/// # {
401/// use embedded_charts::prelude::*;
402/// use embedded_charts::error::AnimationError;
403///
404/// // Example function that might return an AnimationError
405/// fn start_animation() -> Result<u32, AnimationError> {
406///     // This would be actual animation logic
407///     Err(AnimationError::InvalidDuration)
408/// }
409///
410/// match start_animation() {
411///     Ok(animation_id) => println!("Animation started: {}", animation_id),
412///     Err(AnimationError::InvalidDuration) => println!("Duration must be positive"),
413///     Err(AnimationError::SchedulerFull) => println!("Too many active animations"),
414///     Err(e) => println!("Animation error: {}", e),
415/// }
416/// # }
417/// ```
418#[cfg(feature = "animations")]
419#[derive(Debug, Clone, Copy, PartialEq, Eq)]
420pub enum AnimationError {
421    /// Invalid duration specified.
422    ///
423    /// Occurs when animation duration is zero, negative, or exceeds
424    /// maximum allowed duration.
425    InvalidDuration,
426    /// Animation with specified ID was not found.
427    ///
428    /// Returned when trying to access or modify an animation that
429    /// doesn't exist or has already completed.
430    AnimationNotFound,
431    /// Animation scheduler is full.
432    ///
433    /// Occurs when trying to start a new animation but the scheduler
434    /// has reached its maximum capacity.
435    SchedulerFull,
436    /// Error occurred during interpolation.
437    ///
438    /// Returned when interpolation between animation keyframes fails,
439    /// typically due to incompatible data types or invalid values.
440    InterpolationError,
441    /// Animation state is invalid.
442    ///
443    /// Occurs when an animation operation is attempted on an animation
444    /// in an inappropriate state (e.g., trying to pause a completed animation).
445    InvalidState,
446}
447
448/// Error type for layout operations.
449///
450/// This error type covers chart layout calculations, component positioning,
451/// and space allocation operations.
452///
453/// # Common Scenarios
454///
455/// - Insufficient space for chart components
456/// - Invalid layout configuration
457/// - Component positioning failures
458///
459/// # Examples
460///
461/// ```rust,no_run
462/// use embedded_charts::prelude::*;
463/// use embedded_charts::error::LayoutError;
464/// use embedded_graphics::prelude::*;
465///
466/// // Example function that might return a LayoutError
467/// fn check_layout_error() -> Result<(), LayoutError> {
468///     // This would be actual layout calculation logic
469///     Err(LayoutError::InsufficientSpace)
470/// }
471///
472/// match check_layout_error() {
473///     Ok(()) => println!("Layout calculated successfully"),
474///     Err(LayoutError::InsufficientSpace) => println!("Viewport too small"),
475///     Err(LayoutError::InvalidConfiguration) => println!("Invalid layout config"),
476///     Err(e) => println!("Layout error: {}", e),
477/// }
478/// ```
479#[derive(Debug, Clone, Copy, PartialEq, Eq)]
480pub enum LayoutError {
481    /// Insufficient space for layout.
482    ///
483    /// Occurs when the available viewport is too small to accommodate
484    /// the chart and its components with the current configuration.
485    InsufficientSpace,
486    /// Invalid layout configuration.
487    ///
488    /// Returned when layout parameters are invalid or conflicting,
489    /// such as negative margins or impossible component arrangements.
490    InvalidConfiguration,
491    /// Component positioning failed.
492    ///
493    /// Occurs when individual chart components cannot be positioned
494    /// within the available space, even with valid overall layout.
495    PositioningFailed,
496}
497
498/// Error type for rendering operations.
499///
500/// This error type covers low-level rendering operations including drawing
501/// primitives, text rendering, and color operations.
502///
503/// # Common Scenarios
504///
505/// - Drawing operation failures
506/// - Text rendering issues
507/// - Color conversion problems
508/// - Clipping operation failures
509///
510/// # Examples
511///
512/// ```rust,no_run
513/// use embedded_charts::prelude::*;
514/// use embedded_charts::error::RenderError;
515///
516/// // Example function that might return a RenderError
517/// fn draw_something() -> Result<(), RenderError> {
518///     // This would be actual rendering logic
519///     Err(RenderError::DrawingFailed)
520/// }
521///
522/// match draw_something() {
523///     Ok(()) => println!("Drawing completed successfully"),
524///     Err(RenderError::DrawingFailed) => println!("Failed to draw"),
525///     Err(RenderError::ColorConversionFailed) => println!("Invalid color"),
526///     Err(e) => println!("Render error: {}", e),
527/// }
528/// ```
529#[derive(Debug, Clone, Copy, PartialEq, Eq)]
530pub enum RenderError {
531    /// Drawing operation failed.
532    ///
533    /// Generic error for drawing operations that fail due to
534    /// display driver issues or invalid drawing parameters.
535    DrawingFailed,
536    /// Text rendering failed.
537    ///
538    /// Occurs when text cannot be rendered, typically due to
539    /// font issues, invalid characters, or display limitations.
540    TextRenderingFailed,
541    /// Clipping operation failed.
542    ///
543    /// Returned when clipping regions cannot be established
544    /// or when clipping operations fail.
545    ClippingFailed,
546    /// Color conversion failed.
547    ///
548    /// Occurs when color values cannot be converted between
549    /// different color spaces or pixel formats.
550    ColorConversionFailed,
551}
552
553impl From<&str> for DataError {
554    fn from(_msg: &str) -> Self {
555        // For no_std compatibility, we can't store the string message
556        // so we return a generic error variant
557        DataError::simple(DataErrorKind::InvalidDataPoint)
558    }
559}
560
561impl From<DataError> for ChartError {
562    fn from(error: DataError) -> Self {
563        ChartError::DataError(error)
564    }
565}
566
567#[cfg(feature = "animations")]
568impl From<AnimationError> for ChartError {
569    fn from(error: AnimationError) -> Self {
570        ChartError::AnimationError(error)
571    }
572}
573
574impl From<LayoutError> for ChartError {
575    fn from(_error: LayoutError) -> Self {
576        ChartError::InvalidConfiguration
577    }
578}
579
580impl From<RenderError> for ChartError {
581    fn from(_error: RenderError) -> Self {
582        ChartError::RenderingError
583    }
584}
585
586/// Result type for chart operations
587pub type ChartResult<T> = Result<T, ChartError>;
588
589/// Result type for data operations
590pub type DataResult<T> = Result<T, DataError>;
591
592/// Result type for animation operations
593#[cfg(feature = "animations")]
594pub type AnimationResult<T> = Result<T, AnimationError>;
595
596/// Result type for layout operations
597pub type LayoutResult<T> = Result<T, LayoutError>;
598
599/// Result type for rendering operations
600pub type RenderResult<T> = Result<T, RenderError>;
601
602// Implement std::error::Error trait for error types when std is available
603#[cfg(feature = "std")]
604impl std::error::Error for ChartError {
605    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
606        match self {
607            ChartError::DataError(err) => Some(err),
608            ChartError::RenderError(err) => Some(err),
609            ChartError::LayoutError(err) => Some(err),
610            #[cfg(feature = "animations")]
611            ChartError::AnimationError(err) => Some(err),
612            _ => None,
613        }
614    }
615}
616
617#[cfg(feature = "std")]
618impl std::error::Error for DataError {}
619
620#[cfg(feature = "animations")]
621#[cfg(feature = "std")]
622impl std::error::Error for AnimationError {}
623
624#[cfg(feature = "std")]
625impl std::error::Error for LayoutError {}
626
627#[cfg(feature = "std")]
628impl std::error::Error for RenderError {}
629
630// Implement Display trait for error types
631impl core::fmt::Display for ChartError {
632    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
633        match self {
634            ChartError::InsufficientData => write!(f, "Insufficient data to render the chart"),
635            ChartError::InvalidRange => write!(f, "Invalid range specified for axis or data"),
636            ChartError::InvalidData => write!(f, "Invalid data provided"),
637            ChartError::MemoryFull => write!(f, "Memory allocation failed or buffer is full"),
638            ChartError::RenderingError => write!(f, "Error occurred during rendering"),
639            ChartError::InvalidConfiguration => write!(f, "Invalid configuration provided"),
640            ChartError::ConfigurationError => write!(f, "Configuration error occurred"),
641            ChartError::RenderError(err) => write!(f, "Render error: {err}"),
642            ChartError::LayoutError(err) => write!(f, "Layout error: {err}"),
643            ChartError::DataError(err) => write!(f, "Data error: {err}"),
644            #[cfg(feature = "animations")]
645            ChartError::AnimationError(err) => write!(f, "Animation error: {err}"),
646        }
647    }
648}
649
650impl core::fmt::Display for DataError {
651    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
652        match self {
653            DataError::SeriesNotFound { context } => {
654                write!(f, "Requested data series was not found")?;
655                if let Some(ctx) = context {
656                    write!(f, " during {} (hint: {})", ctx.operation, ctx.hint)?;
657                }
658                Ok(())
659            }
660            DataError::IndexOutOfBounds { context } => {
661                write!(f, "Index is out of bounds for the data collection")?;
662                if let Some(ctx) = context {
663                    write!(f, " during {} (hint: {})", ctx.operation, ctx.hint)?;
664                    if let Some(value) = ctx.numeric_context {
665                        write!(f, " [length: {value}]")?;
666                    }
667                }
668                Ok(())
669            }
670            DataError::InvalidDataPoint { context } => {
671                write!(f, "Invalid data point provided")?;
672                if let Some(ctx) = context {
673                    write!(f, " during {} (hint: {})", ctx.operation, ctx.hint)?;
674                }
675                Ok(())
676            }
677            DataError::ScalingError { context } => {
678                write!(f, "Error occurred during data scaling")?;
679                if let Some(ctx) = context {
680                    write!(f, " during {} (hint: {})", ctx.operation, ctx.hint)?;
681                }
682                Ok(())
683            }
684            DataError::BufferFull { context } => {
685                write!(f, "Buffer capacity exceeded")?;
686                if let Some(ctx) = context {
687                    write!(f, " during {} (hint: {})", ctx.operation, ctx.hint)?;
688                    if let Some(capacity) = ctx.numeric_context {
689                        write!(f, " [capacity: {capacity}]")?;
690                    }
691                }
692                Ok(())
693            }
694            DataError::InsufficientData { context } => {
695                write!(f, "Insufficient data to perform operation")?;
696                if let Some(ctx) = context {
697                    write!(f, " during {} (hint: {})", ctx.operation, ctx.hint)?;
698                    if let Some(found) = ctx.numeric_context {
699                        write!(f, " [found: {found}]")?;
700                    }
701                }
702                Ok(())
703            }
704        }
705    }
706}
707
708#[cfg(feature = "animations")]
709impl core::fmt::Display for AnimationError {
710    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
711        match self {
712            AnimationError::InvalidDuration => write!(f, "Invalid duration specified"),
713            AnimationError::AnimationNotFound => {
714                write!(f, "Animation with specified ID was not found")
715            }
716            AnimationError::SchedulerFull => write!(f, "Animation scheduler is full"),
717            AnimationError::InterpolationError => write!(f, "Error occurred during interpolation"),
718            AnimationError::InvalidState => write!(f, "Animation state is invalid"),
719        }
720    }
721}
722
723impl core::fmt::Display for LayoutError {
724    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
725        match self {
726            LayoutError::InsufficientSpace => write!(f, "Insufficient space for layout"),
727            LayoutError::InvalidConfiguration => write!(f, "Invalid layout configuration"),
728            LayoutError::PositioningFailed => write!(f, "Component positioning failed"),
729        }
730    }
731}
732
733impl core::fmt::Display for RenderError {
734    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
735        match self {
736            RenderError::DrawingFailed => write!(f, "Drawing operation failed"),
737            RenderError::TextRenderingFailed => write!(f, "Text rendering failed"),
738            RenderError::ClippingFailed => write!(f, "Clipping operation failed"),
739            RenderError::ColorConversionFailed => write!(f, "Color conversion failed"),
740        }
741    }
742}