use thiserror::Error;
#[cfg(feature = "tracing")]
use tracing::{debug, error, warn};
pub type ImGuiResult<T> = Result<T, ImGuiError>;
#[derive(Error, Debug)]
pub enum ImGuiError {
#[error("Failed to create Dear ImGui context: {reason}")]
ContextCreation { reason: String },
#[error("A Dear ImGui context is already active")]
ContextAlreadyActive,
#[error("Invalid operation: {operation}")]
InvalidOperation { operation: String },
#[error("Resource allocation failed: {resource}")]
ResourceAllocation { resource: String },
#[error("Font loading failed: {reason}")]
FontLoading { reason: String },
#[error("Texture operation failed: {operation}")]
TextureOperation { operation: String },
#[error("Renderer error")]
Renderer(#[from] Box<dyn std::error::Error + Send + Sync>),
#[error("IO operation failed: {operation}")]
IoOperation { operation: String },
#[error("Configuration error: {setting}")]
Configuration { setting: String },
#[error("{message}")]
Generic { message: String },
}
impl ImGuiError {
pub fn context_creation(reason: impl Into<String>) -> Self {
let reason = reason.into();
#[cfg(feature = "tracing")]
error!("Context creation failed: {}", reason);
Self::ContextCreation { reason }
}
pub fn invalid_operation(operation: impl Into<String>) -> Self {
let operation = operation.into();
#[cfg(feature = "tracing")]
warn!("Invalid operation: {}", operation);
Self::InvalidOperation { operation }
}
pub fn resource_allocation(resource: impl Into<String>) -> Self {
let resource = resource.into();
#[cfg(feature = "tracing")]
error!("Resource allocation failed: {}", resource);
Self::ResourceAllocation { resource }
}
pub fn font_loading(reason: impl Into<String>) -> Self {
let reason = reason.into();
#[cfg(feature = "tracing")]
error!("Font loading failed: {}", reason);
Self::FontLoading { reason }
}
pub fn texture_operation(operation: impl Into<String>) -> Self {
let operation = operation.into();
#[cfg(feature = "tracing")]
error!("Texture operation failed: {}", operation);
Self::TextureOperation { operation }
}
pub fn io_operation(operation: impl Into<String>) -> Self {
let operation = operation.into();
#[cfg(feature = "tracing")]
warn!("IO operation failed: {}", operation);
Self::IoOperation { operation }
}
pub fn configuration(setting: impl Into<String>) -> Self {
let setting = setting.into();
#[cfg(feature = "tracing")]
warn!("Configuration error: {}", setting);
Self::Configuration { setting }
}
pub fn generic(message: impl Into<String>) -> Self {
let message = message.into();
#[cfg(feature = "tracing")]
debug!("Generic error: {}", message);
Self::Generic { message }
}
}
pub trait IntoImGuiError {
fn into_imgui_error(self) -> ImGuiError;
}
impl<E> IntoImGuiError for E
where
E: std::error::Error + Send + Sync + 'static,
{
fn into_imgui_error(self) -> ImGuiError {
ImGuiError::Renderer(Box::new(self))
}
}
pub trait ImGuiRenderer {
type Error: std::error::Error + Send + Sync + 'static;
fn init(&mut self) -> Result<(), Self::Error>;
fn render(&mut self, draw_data: &crate::render::DrawData) -> Result<(), Self::Error>;
fn device_lost(&mut self) -> Result<(), Self::Error> {
Ok(())
}
fn shutdown(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
pub trait ImGuiPlatform {
type Error: std::error::Error + Send + Sync + 'static;
fn init(&mut self) -> Result<(), Self::Error>;
fn handle_event(&mut self, event: &dyn std::any::Any) -> Result<bool, Self::Error>;
fn new_frame(&mut self) -> Result<(), Self::Error>;
fn shutdown(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
pub trait SafeStringConversion {
fn to_cstring_safe(&self) -> Result<std::ffi::CString, ImGuiError>;
}
impl SafeStringConversion for str {
fn to_cstring_safe(&self) -> Result<std::ffi::CString, ImGuiError> {
std::ffi::CString::new(self).map_err(|_| ImGuiError::InvalidOperation {
operation: format!("String contains null byte: {}", self),
})
}
}
impl SafeStringConversion for String {
fn to_cstring_safe(&self) -> Result<std::ffi::CString, ImGuiError> {
self.as_str().to_cstring_safe()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
#[test]
fn test_error_creation() {
let err = ImGuiError::context_creation("test reason");
assert!(err.to_string().contains("test reason"));
}
#[test]
fn test_error_chain() {
let source_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let imgui_err = source_err.into_imgui_error();
assert!(imgui_err.source().is_some());
}
}