use serde::de::StdError; use std::fmt; use std::sync::PoisonError; use thiserror::Error; pub type NyxResult = Result; #[derive(Debug, Error)] pub enum NyxError { #[error("I/O error: {0}")] Io(#[from] std::io::Error), #[error("TOML parse error: {0}")] Toml(#[from] toml::de::Error), #[error("SQLite error: {0}")] Sql(#[from] rusqlite::Error), #[error("tree-sitter error: {0}")] TreeSitter(#[from] tree_sitter::LanguageError), #[error("connection-pool error: {0}")] Pool(#[from] r2d2::Error), #[error("time error: {0}")] Time(#[from] std::time::SystemTimeError), #[error("poisoned lock: {0}")] Poison(String), #[error(transparent)] Other(#[from] Box), #[error("{0}")] Msg(String), } impl From> for NyxError where T: fmt::Debug, { fn from(err: PoisonError) -> Self { NyxError::Poison(err.to_string()) } } impl From<&str> for NyxError { fn from(s: &str) -> Self { NyxError::Msg(s.to_owned()) } } impl From for NyxError { fn from(s: String) -> Self { NyxError::Msg(s) } } impl From> for NyxError { fn from(err: Box) -> Self { NyxError::Msg(err.to_string()) } } #[test] fn io_conversion_retains_message() { let e = std::io::Error::other("boom!"); let n: NyxError = e.into(); assert!(matches!(n, NyxError::Io(_))); assert!(n.to_string().contains("boom")); } #[test] fn poison_conversion_maps_correct_variant() { let lock = std::sync::Arc::new(std::sync::Mutex::new(())); { let lock2 = std::sync::Arc::clone(&lock); std::thread::spawn(move || { let _guard = lock2.lock().unwrap(); panic!("intentional – poison the mutex"); }) .join() .ok(); } let poison = lock.lock().unwrap_err(); let nyx: NyxError = poison.into(); assert!(matches!(nyx, NyxError::Poison(_))); } #[test] fn simple_string_into_msg() { let nyx: NyxError = "plain msg".into(); assert!(matches!(nyx, NyxError::Msg(s) if s == "plain msg")); }