improv_core_model/
formula.rs1use crate::ids::{CategoryId, MeasureId};
8use crate::value::Value;
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
13pub struct FuncId(pub u32);
14
15#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
21pub struct DimensionSpec {
22 pub by: Vec<CategoryId>,
23 pub over: Vec<CategoryId>,
24 pub except: Vec<CategoryId>,
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
28pub enum UnaryOp {
29 Neg,
30 Not,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
34pub enum BinaryOp {
35 Add,
36 Sub,
37 Mul,
38 Div,
39 And,
40 Or,
41 Eq,
42 Ne,
43 Lt,
44 Le,
45 Gt,
46 Ge,
47 Pow,
49 Concat,
51}
52
53#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
56pub enum Expr {
57 Literal(Value),
58 Ref(MeasureId, DimensionSpec),
60 UnaryOp(UnaryOp, Box<Expr>),
61 BinaryOp(BinaryOp, Box<Expr>, Box<Expr>),
62 Call(FuncId, Vec<Expr>),
63 Category(CategoryId),
66}
67
68impl Expr {
69 pub fn referenced_measures(&self) -> Vec<MeasureId> {
72 let mut acc = Vec::new();
73 self.collect_refs(&mut acc);
74 acc
75 }
76
77 fn collect_refs(&self, acc: &mut Vec<MeasureId>) {
78 match self {
79 Expr::Literal(_) | Expr::Category(_) => {}
80 Expr::Ref(m, _) => acc.push(*m),
81 Expr::UnaryOp(_, e) => e.collect_refs(acc),
82 Expr::BinaryOp(_, l, r) => {
83 l.collect_refs(acc);
84 r.collect_refs(acc);
85 }
86 Expr::Call(_, args) => {
87 for a in args {
88 a.collect_refs(acc);
89 }
90 }
91 }
92 }
93}
94
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub struct Formula {
97 pub expr: Expr,
98}
99
100impl Formula {
101 pub fn new(expr: Expr) -> Self {
102 Formula { expr }
103 }
104
105 pub fn referenced_measures(&self) -> Vec<MeasureId> {
106 self.expr.referenced_measures()
107 }
108}
109
110#[cfg(test)]
111#[cfg_attr(coverage_nightly, coverage(off))]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn dependency_extraction() {
117 let f = Formula::new(Expr::BinaryOp(
119 BinaryOp::Mul,
120 Box::new(Expr::Ref(MeasureId(100), DimensionSpec::default())),
121 Box::new(Expr::Ref(MeasureId(101), DimensionSpec::default())),
122 ));
123 let mut deps = f.referenced_measures();
124 deps.sort();
125 assert_eq!(deps, vec![MeasureId(100), MeasureId(101)]);
126 }
127}