mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-25 11:16:49 -06:00
Accounting format: quote the symbol as format text, refuse control characters (review #1930)
The symbol was interpolated with Rust's Debug formatting, which escapes a control character into literal `\n`-style text. It is now written into the format's quoted literal directly, and a symbol holding a control character is refused along with a quote or a backslash, so nothing is escaped behind the user's back. Test: `accounting_format_takes_the_symbol_it_is_given` covers `\n` and a tab. `cargo xtask ci`: 8/8 steps, 0 failures.
This commit is contained in:
@@ -204,14 +204,17 @@ const COMMA: &str = "_(* #,##0.00_);_(* \\(#,##0.00\\);_(* \"-\"??_);_(@_)";
|
||||
/// currency; `symbol` is how the control channel picks another one, e.g. `"L"` for lempiras).
|
||||
fn accounting(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
let symbol = str_param(p, "symbol").unwrap_or("$");
|
||||
if symbol.contains(['"', '\\']) {
|
||||
return Err(bad("home.accounting", "the symbol can't contain a quote or a backslash"));
|
||||
// The symbol goes into a quoted literal: a quote or a backslash would end it, and a control
|
||||
// character would reach the format's text as-is.
|
||||
if symbol.contains(['"', '\\']) || symbol.chars().any(char::is_control) {
|
||||
return Err(bad("home.accounting", "the symbol can't contain a quote, a backslash or a control character"));
|
||||
}
|
||||
set_fmt(s, p, &accounting_format(symbol))
|
||||
}
|
||||
|
||||
fn accounting_format(symbol: &str) -> String {
|
||||
format!("_({symbol:?}* #,##0.00_);_({symbol:?}* \\(#,##0.00\\);_({symbol:?}* \"-\"??_);_(@_)")
|
||||
let symbol = format!("\"{symbol}\"");
|
||||
format!("_({symbol}* #,##0.00_);_({symbol}* \\(#,##0.00\\);_({symbol}* \"-\"??_);_(@_)")
|
||||
}
|
||||
|
||||
fn with(p: &Json, key: &str, v: Json) -> Json {
|
||||
|
||||
@@ -1274,9 +1274,10 @@ fn accounting_format_takes_the_symbol_it_is_given() {
|
||||
// Without a symbol it stays the default `$` format, character for character.
|
||||
s.execute("home.accounting", json!({"range": "A1"})).unwrap();
|
||||
assert_eq!(text(&s, "A1"), " $ 1,234.50 ");
|
||||
// A quote or a backslash would break the format string, so the symbol is refused.
|
||||
for bad in ["\"", "\\"] {
|
||||
assert!(s.execute("home.accounting", json!({"range": "A1", "symbol": bad})).is_err(), "{bad}");
|
||||
// A quote or a backslash would end the format's quoted text, and a control character would
|
||||
// reach it as-is, so those symbols are refused.
|
||||
for bad in ["\"", "\\", "\n", "L\t"] {
|
||||
assert!(s.execute("home.accounting", json!({"range": "A1", "symbol": bad})).is_err(), "{bad:?}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user