formualizer_eval/locale.rs
1/// Locale contract for the engine.
2///
3/// Milestone 0 intentionally uses an invariant locale:
4///
5/// - Numeric parsing is ASCII/invariant only (`.` decimal separator; no thousands separators).
6/// - Strings are case-folded with ASCII-only rules (`to_ascii_lowercase`).
7///
8/// This means locale-dependent inputs like `"1.234,56"` are *not* interpreted as numbers.
9/// Callers should surface `#VALUE!` for locale-dependent numeric coercions (e.g. `VALUE()`)
10/// rather than silently producing a wrong number.
11#[derive(Copy, Clone, Debug, Eq, PartialEq)]
12pub struct Locale;
13
14impl Locale {
15 pub const fn invariant() -> Self {
16 Locale
17 }
18
19 /// Parse a number using invariant rules (ASCII, dot decimal separator).
20 pub fn parse_number_invariant(&self, s: &str) -> Option<f64> {
21 s.trim().parse::<f64>().ok()
22 }
23
24 /// Case folding for comparisons; invariant = ASCII lower.
25 pub fn fold_case_invariant(&self, s: &str) -> String {
26 s.to_ascii_lowercase()
27 }
28}