pub fn sanitize_csv_field(value: &str) -> String {
let normalized = value.replace(['\r', '\n', '\t'], " ");
let trimmed = normalized.trim_start();
if trimmed.starts_with('=')
|| trimmed.starts_with('+')
|| trimmed.starts_with('-')
|| trimmed.starts_with('@')
{
format!("'{normalized}")
} else {
normalized
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn passthrough_safe_value() {
assert_eq!(sanitize_csv_field("Hello World"), "Hello World");
}
#[test]
fn prepend_quote_for_equals() {
assert_eq!(sanitize_csv_field("=CMD()"), "'=CMD()");
}
#[test]
fn prepend_quote_for_plus() {
assert_eq!(sanitize_csv_field("+1234"), "'+1234");
}
#[test]
fn prepend_quote_for_minus() {
assert_eq!(sanitize_csv_field("-formula"), "'-formula");
}
#[test]
fn prepend_quote_for_at() {
assert_eq!(sanitize_csv_field("@SUM(A1)"), "'@SUM(A1)");
}
#[test]
fn prepend_quote_for_leading_whitespace_then_equals() {
assert_eq!(sanitize_csv_field(" =CMD()"), "' =CMD()");
}
#[test]
fn replaces_control_characters() {
assert_eq!(
sanitize_csv_field("line1\r\nline2\ttab"),
"line1 line2 tab"
);
}
#[test]
fn empty_string_passthrough() {
assert_eq!(sanitize_csv_field(""), "");
}
#[test]
fn newline_followed_by_formula_char() {
assert_eq!(sanitize_csv_field("\n=CMD()"), "' =CMD()");
}
}