truecalc_core/engine/
mod.rs1use std::collections::HashMap;
2
3use crate::eval::functions::Registry;
4use crate::eval::{evaluate_expr, Context, EvalCtx};
5use crate::parser::{parse_formula, Expr};
6use crate::types::{ErrorKind, ParseError, Value};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10enum Flavor {
11 Sheets,
12 Excel,
13}
14
15pub struct Engine {
16 flavor: Flavor,
17 registry: Registry,
18}
19
20impl Engine {
21 pub fn sheets() -> Self {
23 Self { flavor: Flavor::Sheets, registry: Registry::new() }
24 }
25
26 pub fn excel() -> Self {
33 Self { flavor: Flavor::Excel, registry: Registry::new() }
34 }
35
36 #[deprecated(note = "use Engine::sheets() — engine flavor is required; see ADR 2026-04-27")]
38 pub fn google_sheets() -> Self {
39 Self::sheets()
40 }
41
42 pub fn parse(&self, formula: &str) -> Result<Expr, ParseError> {
47 parse_formula(formula)
48 }
49
50 pub fn validate(&self, formula: &str) -> Result<(), ParseError> {
53 self.parse(formula).map(|_| ())
54 }
55
56 pub fn evaluate(&self, formula: &str, variables: &HashMap<String, Value>) -> Value {
57 if self.flavor == Flavor::Excel {
58 return Value::Error(ErrorKind::NA);
60 }
61 match parse_formula(formula) {
62 Err(_) => Value::Error(ErrorKind::Value),
63 Ok(expr) => {
64 let ctx = Context::new(variables.clone());
65 let mut eval_ctx = EvalCtx::new(ctx, &self.registry);
66 first_of_array(evaluate_expr(&expr, &mut eval_ctx))
67 }
68 }
69 }
70}
71
72fn first_of_array(v: Value) -> Value {
73 match v {
74 Value::Array(elems) if !elems.is_empty() => {
75 first_of_array(elems.into_iter().next().unwrap())
76 }
77 other => other,
78 }
79}
80
81#[cfg(test)]
82mod tests;