chore: format

This commit is contained in:
Edward Langley
2026-04-13 21:30:37 -07:00
parent af74dc3d3f
commit 6370f8b19f
19 changed files with 445 additions and 249 deletions

View File

@ -1,6 +1,6 @@
use std::collections::HashMap;
use anyhow::{anyhow, Result};
use anyhow::{Result, anyhow};
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
@ -274,7 +274,12 @@ impl Model {
pub fn measure_item_names(&self) -> Vec<String> {
let mut names: Vec<String> = self
.category("_Measure")
.map(|c| c.ordered_item_names().iter().map(|s| s.to_string()).collect())
.map(|c| {
c.ordered_item_names()
.iter()
.map(|s| s.to_string())
.collect()
})
.unwrap_or_default();
for f in &self.formulas {
if f.target_category == "_Measure" && !names.iter().any(|n| n == &f.target) {
@ -292,7 +297,12 @@ impl Model {
self.measure_item_names()
} else {
self.category(cat_name)
.map(|c| c.ordered_item_names().iter().map(|s| s.to_string()).collect())
.map(|c| {
c.ordered_item_names()
.iter()
.map(|s| s.to_string())
.collect()
})
.unwrap_or_default()
}
}
@ -504,8 +514,8 @@ impl Model {
match expr {
Expr::Number(n) => Ok(*n),
Expr::Ref(name) => {
let cat = find_item_category(model, name)
.ok_or_else(|| format!("ref:{name}"))?;
let cat =
find_item_category(model, name).ok_or_else(|| format!("ref:{name}"))?;
let ref_key = context.clone().with(cat, name);
// Check formula cache first, then aggregate raw data
if let Some(cached) = model.formula_cache.get(&ref_key) {
@ -529,12 +539,26 @@ impl Model {
BinOp::Add => Ok(lv + rv),
BinOp::Sub => Ok(lv - rv),
BinOp::Mul => Ok(lv * rv),
BinOp::Div => if rv == 0.0 { Err("div/0".into()) } else { Ok(lv / rv) },
BinOp::Div => {
if rv == 0.0 {
Err("div/0".into())
} else {
Ok(lv / rv)
}
}
BinOp::Pow => Ok(lv.powf(rv)),
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge => Err("type".into()),
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge => {
Err("type".into())
}
}
}
Expr::UnaryMinus(e) => Ok(-eval_expr_cached(e, context, model, target_category, none_cats)?),
Expr::UnaryMinus(e) => Ok(-eval_expr_cached(
e,
context,
model,
target_category,
none_cats,
)?),
Expr::Agg(func, inner, agg_filter) => {
use crate::formula::AggFunc;
let mut partial = context.without(target_category);
@ -554,9 +578,23 @@ impl Model {
.collect();
match func {
AggFunc::Sum => Ok(values.iter().sum()),
AggFunc::Avg => if values.is_empty() { Err("empty".into()) } else { Ok(values.iter().sum::<f64>() / values.len() as f64) },
AggFunc::Min => values.iter().cloned().reduce(f64::min).ok_or_else(|| "empty".into()),
AggFunc::Max => values.iter().cloned().reduce(f64::max).ok_or_else(|| "empty".into()),
AggFunc::Avg => {
if values.is_empty() {
Err("empty".into())
} else {
Ok(values.iter().sum::<f64>() / values.len() as f64)
}
}
AggFunc::Min => values
.iter()
.cloned()
.reduce(f64::min)
.ok_or_else(|| "empty".into()),
AggFunc::Max => values
.iter()
.cloned()
.reduce(f64::max)
.ok_or_else(|| "empty".into()),
AggFunc::Count => Ok(values.len() as f64),
}
}
@ -597,7 +635,13 @@ impl Model {
}
}
match eval_expr_cached(&formula.expr, context, self, &formula.target_category, none_cats) {
match eval_expr_cached(
&formula.expr,
context,
self,
&formula.target_category,
none_cats,
) {
Ok(n) => Some(CellValue::Number(n)),
Err(e) => Some(CellValue::Error(e)),
}
@ -679,8 +723,8 @@ impl Model {
match expr {
Expr::Number(n) => Ok(*n),
Expr::Ref(name) => {
let cat = find_item_category(model, name)
.ok_or_else(|| format!("ref:{name}"))?;
let cat =
find_item_category(model, name).ok_or_else(|| format!("ref:{name}"))?;
let new_key = context.clone().with(cat, name);
match model.evaluate_depth(&new_key, depth) {
Some(CellValue::Number(n)) => Ok(n),
@ -735,8 +779,16 @@ impl Model {
Ok(values.iter().sum::<f64>() / values.len() as f64)
}
}
AggFunc::Min => values.iter().cloned().reduce(f64::min).ok_or_else(|| "empty".into()),
AggFunc::Max => values.iter().cloned().reduce(f64::max).ok_or_else(|| "empty".into()),
AggFunc::Min => values
.iter()
.cloned()
.reduce(f64::min)
.ok_or_else(|| "empty".into()),
AggFunc::Max => values
.iter()
.cloned()
.reduce(f64::max)
.ok_or_else(|| "empty".into()),
AggFunc::Count => Ok(values.len() as f64),
}
}
@ -779,7 +831,13 @@ impl Model {
}
}
match eval_expr(&formula.expr, context, self, &formula.target_category, depth) {
match eval_expr(
&formula.expr,
context,
self,
&formula.target_category,
depth,
) {
Ok(n) => Some(CellValue::Number(n)),
Err(e) => Some(CellValue::Error(e)),
}
@ -1011,11 +1069,19 @@ mod model_tests {
m.category_mut("_Measure").unwrap().add_item("Amount");
m.set_cell(
coord(&[("Payee", "Acme"), ("Date", "Jan-01"), ("_Measure", "Amount")]),
coord(&[
("Payee", "Acme"),
("Date", "Jan-01"),
("_Measure", "Amount"),
]),
CellValue::Number(100.0),
);
m.set_cell(
coord(&[("Payee", "Acme"), ("Date", "Jan-02"), ("_Measure", "Amount")]),
coord(&[
("Payee", "Acme"),
("Date", "Jan-02"),
("_Measure", "Amount"),
]),
CellValue::Number(50.0),
);
@ -1780,8 +1846,10 @@ mod five_category {
.evaluate(&coord(region, product, channel, time, "Margin"))
.and_then(|v| v.as_f64())
.unwrap_or_else(|| panic!("Margin empty at {region}/{product}/{channel}/{time}"));
assert!(approx(actual, expected),
"Margin at {region}/{product}/{channel}/{time}: expected {expected:.4}, got {actual:.4}");
assert!(
approx(actual, expected),
"Margin at {region}/{product}/{channel}/{time}: expected {expected:.4}, got {actual:.4}"
);
}
}