use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct XtlError {
pub code: String,
pub message: String,
}
impl XtlError {
pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
XtlError {
code: code.into(),
message: message.into(),
}
}
}
impl fmt::Display for XtlError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[{}] {}", self.code, self.message)
}
}
impl std::error::Error for XtlError {}
pub fn is_xtl_error(err: &anyhow::Error) -> Option<&XtlError> {
err.downcast_ref::<XtlError>()
}
pub mod code {
pub const SOURCE_SHEET_MISSING: &str = "xl3/source/sheet-missing";
pub const SOURCE_NO_HEADER: &str = "xl3/source/no-header";
pub const SOURCE_DUPLICATE_COLUMN: &str = "xl3/source/duplicate-column";
pub const EVAL_DIV_BY_ZERO: &str = "xl3/eval/div-by-zero";
pub const EVAL_UNSUPPORTED_SYNTAX: &str = "xl3/eval/unsupported-syntax";
pub const EVAL_UNKNOWN_NAME: &str = "xl3/expression/unknown-name";
pub const EVAL_ARITY_MISMATCH: &str = "xl3/eval/arity-mismatch";
pub const EVAL_OPERAND_COERCION: &str = "xl3/eval/operand-coercion";
pub const DIRECTIVE_BAD_JOIN: &str = "xl3/directive/bad-join";
pub const XLOOKUP_BARE_BRACKET: &str = "xl3/xlookup/bare-bracket";
pub const XLOOKUP_SOURCE_MISMATCH: &str = "xl3/xlookup/source-mismatch";
pub const TEMPLATE_NO_SHEETS: &str = "xl3/template/no-visible-sheets";
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;
fn fails_with_code() -> Result<()> {
Err(XtlError::new(code::EVAL_DIV_BY_ZERO, "test message").into())
}
#[test]
fn downcasts_through_anyhow() {
let err = fails_with_code().unwrap_err();
let xtl = is_xtl_error(&err).expect("expected XtlError");
assert_eq!(xtl.code, "xl3/eval/div-by-zero");
assert_eq!(xtl.message, "test message");
}
#[test]
fn non_xtl_error_returns_none() {
let err: anyhow::Error = anyhow::anyhow!("plain anyhow");
assert!(is_xtl_error(&err).is_none());
}
#[test]
fn display_uses_bracket_code() {
let e = XtlError::new(code::SOURCE_SHEET_MISSING, "foo");
assert_eq!(format!("{e}"), "[xl3/source/sheet-missing] foo");
}
}