Previously Expr::BinOp(String, ...) accepted any string as an operator. Invalid operators (e.g. "diagonal") would compile fine and silently return CellValue::Empty at eval time. Now BinOp is an enum with variants Add/Sub/Mul/Div/Pow/Eq/Ne/Lt/Gt/Le/Ge. The parser produces enum variants directly; the evaluator pattern-matches exhaustively with no fallback branch. An invalid operator is now a compile error at the call site, and the compiler ensures every variant is handled in both eval_expr and eval_bool. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
78 lines
1.8 KiB
Rust
78 lines
1.8 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum AggFunc {
|
|
Sum,
|
|
Avg,
|
|
Min,
|
|
Max,
|
|
Count,
|
|
}
|
|
|
|
/// Arithmetic and comparison operators used in binary expressions.
|
|
/// Having an enum (rather than a raw String) means the parser must
|
|
/// produce a valid operator; invalid operators are caught at parse
|
|
/// time rather than silently returning Empty at eval time.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
|
pub enum BinOp {
|
|
Add,
|
|
Sub,
|
|
Mul,
|
|
Div,
|
|
Pow,
|
|
Eq,
|
|
Ne,
|
|
Lt,
|
|
Gt,
|
|
Le,
|
|
Ge,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Filter {
|
|
pub category: String,
|
|
pub item: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub enum Expr {
|
|
Number(f64),
|
|
Ref(String),
|
|
BinOp(BinOp, Box<Expr>, Box<Expr>),
|
|
UnaryMinus(Box<Expr>),
|
|
Agg(AggFunc, Box<Expr>, Option<Filter>),
|
|
If(Box<Expr>, Box<Expr>, Box<Expr>),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Formula {
|
|
/// The raw formula text, e.g. "Profit = Revenue - Cost"
|
|
pub raw: String,
|
|
/// The item/dimension name this formula computes, e.g. "Profit"
|
|
pub target: String,
|
|
/// The category containing the target item
|
|
pub target_category: String,
|
|
/// The expression to evaluate
|
|
pub expr: Expr,
|
|
/// Optional WHERE filter
|
|
pub filter: Option<Filter>,
|
|
}
|
|
|
|
impl Formula {
|
|
pub fn new(
|
|
raw: impl Into<String>,
|
|
target: impl Into<String>,
|
|
target_category: impl Into<String>,
|
|
expr: Expr,
|
|
filter: Option<Filter>,
|
|
) -> Self {
|
|
Self {
|
|
raw: raw.into(),
|
|
target: target.into(),
|
|
target_category: target_category.into(),
|
|
expr,
|
|
filter,
|
|
}
|
|
}
|
|
}
|