use std::backtrace::Backtrace;
use std::collections::HashMap;
use std::panic::Location;
#[derive(Debug)]
struct CodexErrorData {
pub name: String,
pub cause: String,
pub suggestion: Option<String>,
pub location: &'static Location<'static>,
pub backtrace: Backtrace,
pub metadata: HashMap<String, String>,
}
#[derive(Debug)]
pub struct CodexError {
inner: Box<CodexErrorData>,
}
impl CodexError {
#[track_caller]
pub fn builder<N: Into<String>, C: Into<String>>(name: N, cause: C) -> Self {
Self {
inner: Box::new(CodexErrorData {
name: name.into(),
cause: cause.into(),
suggestion: None,
location: Location::caller(),
backtrace: Backtrace::capture(),
metadata: HashMap::new(),
}),
}
}
pub fn with_suggestion<S: Into<String>>(mut self, suggestion: S) -> Self {
self.inner.suggestion = Some(suggestion.into());
self
}
pub fn with_meta<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
self.inner.metadata.insert(key.into(), value.into());
self
}
pub fn name(&self) -> &str {
&self.inner.name
}
pub fn cause(&self) -> &str {
&self.inner.cause
}
pub fn suggestion(&self) -> Option<&String> {
self.inner.suggestion.as_ref()
}
pub fn metadata(&self) -> &HashMap<String, String> {
&self.inner.metadata
}
pub fn backtrace(&self) -> &Backtrace {
&self.inner.backtrace
}
pub fn location(&self) -> &'static Location<'static> {
self.inner.location
}
}
impl std::fmt::Display for CodexError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}] {}", self.inner.name, self.inner.cause)?;
if let Some(sug) = &self.inner.suggestion {
write!(f, "\nSuggestion: {}", sug)?;
}
Ok(())
}
}
impl std::error::Error for CodexError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_codex_error_display_and_creation() {
let error = CodexError::builder("SYS_FAULT", "Insufficient memory")
.with_suggestion("Increase the swap limit")
.with_meta("process_id", "1234");
let error_string = format!("{}", error);
assert!(error_string.contains("[SYS_FAULT] Insufficient memory"));
assert!(error_string.contains("Suggestion: Increase the swap limit"));
let loc = error.location();
assert!(loc.file().ends_with("error.rs"), "Should capture the current file name");
assert_eq!(error.metadata().get("process_id").unwrap(), "1234");
}
}