Skip to main content

rustledger_core/format/
helpers.rs

1//! Shared helper functions for formatting.
2
3use super::format_amount;
4use crate::MetaValue;
5
6/// Format a metadata value.
7pub fn format_meta_value(value: &MetaValue) -> String {
8    match value {
9        MetaValue::String(s) => format!("\"{}\"", escape_string(s)),
10        MetaValue::Account(a) => a.to_string(),
11        MetaValue::Currency(c) => c.to_string(),
12        MetaValue::Tag(t) => format!("#{t}"),
13        MetaValue::Link(l) => format!("^{l}"),
14        MetaValue::Date(d) => d.to_string(),
15        MetaValue::Number(n) => n.to_string(),
16        MetaValue::Amount(a) => format_amount(a),
17        MetaValue::Bool(b) => if *b { "TRUE" } else { "FALSE" }.to_string(),
18        MetaValue::None => String::new(),
19        MetaValue::Int(i) => i.to_string(),
20    }
21}
22
23/// Escape a string for output (handle quotes and backslashes).
24pub fn escape_string(s: &str) -> String {
25    let mut out = String::with_capacity(s.len());
26    for c in s.chars() {
27        match c {
28            '"' => out.push_str("\\\""),
29            '\\' => out.push_str("\\\\"),
30            '\n' => out.push_str("\\n"),
31            // The parser decodes `\t`/`\r` into literal tab/CR, so re-escape
32            // them here rather than emitting raw control bytes inside quotes
33            // (hostile to terminals/logs, and not round-trippable).
34            '\t' => out.push_str("\\t"),
35            '\r' => out.push_str("\\r"),
36            _ => out.push(c),
37        }
38    }
39    out
40}
41
42#[cfg(test)]
43mod tests {
44    use super::escape_string;
45
46    #[test]
47    fn escapes_quote_backslash_and_controls() {
48        assert_eq!(escape_string("a\"b"), "a\\\"b");
49        assert_eq!(escape_string("a\\b"), "a\\\\b");
50        assert_eq!(escape_string("a\nb"), "a\\nb");
51        // The parser decodes `\t`/`\r` to literal tab/CR; Display must re-escape
52        // them rather than emit raw control bytes inside the quotes.
53        assert_eq!(escape_string("a\tb"), "a\\tb");
54        assert_eq!(escape_string("a\rb"), "a\\rb");
55    }
56
57    #[test]
58    fn leaves_plain_text_untouched() {
59        assert_eq!(escape_string("plain text 123"), "plain text 123");
60    }
61}