1use thiserror::Error;
7
8#[cfg(feature = "tracing")]
9use tracing::{debug, error, warn};
10
11pub type ImGuiResult<T> = Result<T, ImGuiError>;
13
14#[derive(Error, Debug)]
16pub enum ImGuiError {
17 #[error("Failed to create Dear ImGui context: {reason}")]
19 ContextCreation { reason: String },
20
21 #[error("A Dear ImGui context is already active")]
23 ContextAlreadyActive,
24
25 #[error("Invalid operation: {operation}")]
27 InvalidOperation { operation: String },
28
29 #[error("Resource allocation failed: {resource}")]
31 ResourceAllocation { resource: String },
32
33 #[error("Font loading failed: {reason}")]
35 FontLoading { reason: String },
36
37 #[error("Texture operation failed: {operation}")]
39 TextureOperation { operation: String },
40
41 #[error("Renderer error")]
43 Renderer(#[from] Box<dyn std::error::Error + Send + Sync>),
44
45 #[error("IO operation failed: {operation}")]
47 IoOperation { operation: String },
48
49 #[error("Configuration error: {setting}")]
51 Configuration { setting: String },
52
53 #[error("{message}")]
55 Generic { message: String },
56}
57
58impl ImGuiError {
59 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 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 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 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 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 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 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 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
124pub 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
138pub trait ImGuiRenderer {
140 type Error: std::error::Error + Send + Sync + 'static;
142
143 fn init(&mut self) -> Result<(), Self::Error>;
145
146 fn render(&mut self, draw_data: &crate::render::DrawData) -> Result<(), Self::Error>;
148
149 fn device_lost(&mut self) -> Result<(), Self::Error> {
151 Ok(())
153 }
154
155 fn shutdown(&mut self) -> Result<(), Self::Error> {
157 Ok(())
159 }
160}
161
162pub trait ImGuiPlatform {
164 type Error: std::error::Error + Send + Sync + 'static;
166
167 fn init(&mut self) -> Result<(), Self::Error>;
169
170 fn handle_event(&mut self, event: &dyn std::any::Any) -> Result<bool, Self::Error>;
172
173 fn new_frame(&mut self) -> Result<(), Self::Error>;
175
176 fn shutdown(&mut self) -> Result<(), Self::Error> {
178 Ok(())
180 }
181}
182
183pub trait SafeStringConversion {
185 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}