chore: format
This commit is contained in:
@ -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}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user