Skip to main content

logicaffeine_compile/semantics/
format.rs

1//! Interpolated-string format specifiers (`{x$}`, `{x.2}`, `{x>8}`, …).
2
3use crate::interpreter::RuntimeValue;
4
5/// Apply a format spec to a value.
6pub fn apply_format_spec(val: &RuntimeValue, spec: &str) -> String {
7    // Currency: $
8    if spec == "$" {
9        // A BigInt is an exact integer number of units — print it exactly with cents,
10        // rather than routing through a lossy f64.
11        if let RuntimeValue::BigInt(b) = val {
12            return format!("${}.00", b);
13        }
14        let f = match val {
15            RuntimeValue::Float(f) => *f,
16            RuntimeValue::Int(n) => *n as f64,
17            _ => return format!("${}", val.to_display_string()),
18        };
19        return format!("${:.2}", f);
20    }
21    // Precision: .N
22    if spec.starts_with('.') {
23        if let Ok(precision) = spec[1..].parse::<usize>() {
24            match val {
25                RuntimeValue::Float(f) => return format!("{:.prec$}", f, prec = precision),
26                RuntimeValue::Int(n) => return format!("{:.prec$}", *n as f64, prec = precision),
27                RuntimeValue::BigInt(b) => return format!("{:.prec$}", b.to_f64(), prec = precision),
28                _ => return val.to_display_string(),
29            }
30        }
31    }
32    // Alignment: >N, <N, ^N
33    if spec.len() >= 2 {
34        let first = spec.as_bytes()[0];
35        if first == b'>' || first == b'<' || first == b'^' {
36            if let Ok(width) = spec[1..].parse::<usize>() {
37                let s = val.to_display_string();
38                return match first {
39                    b'>' => format!("{:>w$}", s, w = width),
40                    b'<' => format!("{:<w$}", s, w = width),
41                    b'^' => format!("{:^w$}", s, w = width),
42                    _ => unreachable!(),
43                };
44            }
45        }
46    }
47    // Bare width: N (right-align by default, matching Rust's behavior)
48    if let Ok(width) = spec.parse::<usize>() {
49        let s = val.to_display_string();
50        return format!("{:>w$}", s, w = width);
51    }
52    val.to_display_string()
53}