src/filter/dsl/error.rs

//! DSL error type.
//!
//! We use `thiserror` so the error plays nicely with `anyhow::Error` at the
//! CLI boundary while still carrying structured location info.

use thiserror::Error;

#[derive(Debug, Error, PartialEq, Eq)]
pub enum DslError {
    #[error("syntax error at column {column}: {message}")]
    Syntax { column: usize, message: String },

    #[error("unknown field `{field}` in filter expression")]
    UnknownField { field: String },

    #[error("invalid regex `/{pattern}/`: {reason}")]
    InvalidRegex { pattern: String, reason: String },

    #[error("operator `{op}` cannot be applied to {kind} value")]
    TypeMismatch { op: &'static str, kind: &'static str },
}

pub type DslResult<T> = Result<T, DslError>;

impl DslError {
    /// The 1-indexed column where this error occurred, or `None` for global
    /// errors that aren't tied to a position.
    pub fn column(&self) -> Option<usize> {
        match self {
            DslError::Syntax { column, .. } => Some(*column),
            _ => None,
        }
    }

    /// A short kind tag for exit-code mapping.
    pub fn kind(&self) -> &'static str {
        match self {
            DslError::Syntax { .. } => "syntax",
            DslError::UnknownField { .. } => "unknown_field",
            DslError::InvalidRegex { .. } => "invalid_regex",
            DslError::TypeMismatch { .. } => "type_mismatch",
        }
    }
}