dear_imgui_rs/
error.rs

1//! Error types for Dear ImGui
2//!
3//! This module provides comprehensive error handling for the Dear ImGui library,
4//! covering context creation, resource allocation, and runtime errors.
5
6use thiserror::Error;
7
8#[cfg(feature = "tracing")]
9use tracing::{debug, error, warn};
10
11/// Result type for Dear ImGui operations
12pub type ImGuiResult<T> = Result<T, ImGuiError>;
13
14/// Errors that can occur in Dear ImGui operations
15#[derive(Error, Debug)]
16pub enum ImGuiError {
17    /// Context creation failed
18    #[error("Failed to create Dear ImGui context: {reason}")]
19    ContextCreation { reason: String },
20
21    /// Context is already active
22    #[error("A Dear ImGui context is already active")]
23    ContextAlreadyActive,
24
25    /// Invalid operation attempted
26    #[error("Invalid operation: {operation}")]
27    InvalidOperation { operation: String },
28
29    /// Resource allocation failed
30    #[error("Resource allocation failed: {resource}")]
31    ResourceAllocation { resource: String },
32
33    /// Font loading error
34    #[error("Font loading failed: {reason}")]
35    FontLoading { reason: String },
36
37    /// Texture operation error
38    #[error("Texture operation failed: {operation}")]
39    TextureOperation { operation: String },
40
41    /// Renderer error (from backends)
42    #[error("Renderer error")]
43    Renderer(#[from] Box<dyn std::error::Error + Send + Sync>),
44
45    /// IO operation error
46    #[error("IO operation failed: {operation}")]
47    IoOperation { operation: String },
48
49    /// Configuration error
50    #[error("Configuration error: {setting}")]
51    Configuration { setting: String },
52
53    /// Generic error with custom message
54    #[error("{message}")]
55    Generic { message: String },
56}
57
58impl ImGuiError {
59    /// Create a context creation error
60    pub fn context_creation(reason: impl Into<String>) -> Self {
61        let reason = reason.into();
62        #[cfg(feature = "tracing")]
63        error!("Context creation failed: {}", reason);
64        Self::ContextCreation { reason }
65    }
66
67    /// Create an invalid operation error
68    pub fn invalid_operation(operation: impl Into<String>) -> Self {
69        let operation = operation.into();
70        #[cfg(feature = "tracing")]
71        warn!("Invalid operation: {}", operation);
72        Self::InvalidOperation { operation }
73    }
74
75    /// Create a resource allocation error
76    pub fn resource_allocation(resource: impl Into<String>) -> Self {
77        let resource = resource.into();
78        #[cfg(feature = "tracing")]
79        error!("Resource allocation failed: {}", resource);
80        Self::ResourceAllocation { resource }
81    }
82
83    /// Create a font loading error
84    pub fn font_loading(reason: impl Into<String>) -> Self {
85        let reason = reason.into();
86        #[cfg(feature = "tracing")]
87        error!("Font loading failed: {}", reason);
88        Self::FontLoading { reason }
89    }
90
91    /// Create a texture operation error
92    pub fn texture_operation(operation: impl Into<String>) -> Self {
93        let operation = operation.into();
94        #[cfg(feature = "tracing")]
95        error!("Texture operation failed: {}", operation);
96        Self::TextureOperation { operation }
97    }
98
99    /// Create an IO operation error
100    pub fn io_operation(operation: impl Into<String>) -> Self {
101        let operation = operation.into();
102        #[cfg(feature = "tracing")]
103        warn!("IO operation failed: {}", operation);
104        Self::IoOperation { operation }
105    }
106
107    /// Create a configuration error
108    pub fn configuration(setting: impl Into<String>) -> Self {
109        let setting = setting.into();
110        #[cfg(feature = "tracing")]
111        warn!("Configuration error: {}", setting);
112        Self::Configuration { setting }
113    }
114
115    /// Create a generic error
116    pub fn generic(message: impl Into<String>) -> Self {
117        let message = message.into();
118        #[cfg(feature = "tracing")]
119        debug!("Generic error: {}", message);
120        Self::Generic { message }
121    }
122}
123
124/// Trait for converting backend errors to ImGuiError
125pub trait IntoImGuiError {
126    fn into_imgui_error(self) -> ImGuiError;
127}
128
129impl<E> IntoImGuiError for E
130where
131    E: std::error::Error + Send + Sync + 'static,
132{
133    fn into_imgui_error(self) -> ImGuiError {
134        ImGuiError::Renderer(Box::new(self))
135    }
136}
137
138/// Trait for backend renderers with unified error handling
139pub trait ImGuiRenderer {
140    /// Backend-specific error type
141    type Error: std::error::Error + Send + Sync + 'static;
142
143    /// Initialize the renderer
144    fn init(&mut self) -> Result<(), Self::Error>;
145
146    /// Render the current frame
147    fn render(&mut self, draw_data: &crate::render::DrawData) -> Result<(), Self::Error>;
148
149    /// Handle device lost/reset scenarios
150    fn device_lost(&mut self) -> Result<(), Self::Error> {
151        // Default implementation does nothing
152        Ok(())
153    }
154
155    /// Clean up resources
156    fn shutdown(&mut self) -> Result<(), Self::Error> {
157        // Default implementation does nothing
158        Ok(())
159    }
160}
161
162/// Trait for platform backends with unified error handling
163pub trait ImGuiPlatform {
164    /// Platform-specific error type
165    type Error: std::error::Error + Send + Sync + 'static;
166
167    /// Initialize the platform backend
168    fn init(&mut self) -> Result<(), Self::Error>;
169
170    /// Handle platform events
171    fn handle_event(&mut self, event: &dyn std::any::Any) -> Result<bool, Self::Error>;
172
173    /// Update platform state for new frame
174    fn new_frame(&mut self) -> Result<(), Self::Error>;
175
176    /// Clean up platform resources
177    fn shutdown(&mut self) -> Result<(), Self::Error> {
178        // Default implementation does nothing
179        Ok(())
180    }
181}
182
183/// Helper trait for safe string conversion
184pub trait SafeStringConversion {
185    /// Convert to CString safely, returning an error if the string contains null bytes
186    fn to_cstring_safe(&self) -> Result<std::ffi::CString, ImGuiError>;
187}
188
189impl SafeStringConversion for str {
190    fn to_cstring_safe(&self) -> Result<std::ffi::CString, ImGuiError> {
191        std::ffi::CString::new(self).map_err(|_| ImGuiError::InvalidOperation {
192            operation: format!("String contains null byte: {}", self),
193        })
194    }
195}
196
197impl SafeStringConversion for String {
198    fn to_cstring_safe(&self) -> Result<std::ffi::CString, ImGuiError> {
199        self.as_str().to_cstring_safe()
200    }
201}
202
203#[cfg(test)]
204mod tests {
205    use super::*;
206    use std::error::Error;
207
208    #[test]
209    fn test_error_creation() {
210        let err = ImGuiError::context_creation("test reason");
211        assert!(err.to_string().contains("test reason"));
212    }
213
214    #[test]
215    fn test_error_chain() {
216        let source_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
217        let imgui_err = source_err.into_imgui_error();
218        assert!(imgui_err.source().is_some());
219    }
220}