Skip to main content

asc_engine/
text.rs

1//! The text form shared by every tool: one record per line, `|` between
2//! columns, strings quoted and escaped as JSON does ([`asc_arsc::quote`]).
3
4use std::borrow::Cow;
5
6/// `s` in double quotes, escaped so it stays on one line.
7pub fn quoted(s: &str) -> String {
8    let mut out = String::with_capacity(s.len() + 2);
9    asc_arsc::quote(s, &mut out);
10    out
11}
12
13/// The value of a `key=value` pair: as it is, or [`quoted`] when it is empty
14/// or holds whitespace, a quote, a backslash, `=` or a control character,
15/// which would otherwise blur where the pair ends.
16pub fn value(s: &str) -> Cow<'_, str> {
17    let plain = !s.is_empty() && !s.chars().any(|c| c.is_whitespace() || c.is_control() || matches!(c, '"' | '\\' | '='));
18    if plain { Cow::Borrowed(s) } else { Cow::Owned(quoted(s)) }
19}
20
21#[cfg(test)]
22mod tests {
23    use super::*;
24
25    #[test]
26    fn quoted_strings_stay_on_one_line_and_parse_as_json() {
27        let raw = "a\"b\\c\nd\te\r\u{1b}\u{7f}\u{85}é😀";
28        let q = quoted(raw);
29        assert_eq!(q, r#""a\"b\\c\nd\te\r\u001b\u007f\u0085é😀""#);
30        assert_eq!(serde_json::from_str::<String>(&q).unwrap(), raw);
31    }
32
33    #[test]
34    fn values_are_quoted_only_when_ambiguous() {
35        assert_eq!(value("com.demo.Main"), "com.demo.Main");
36        assert_eq!(value("signature|privileged"), "signature|privileged");
37        assert_eq!(value("1.0 beta"), r#""1.0 beta""#);
38        assert_eq!(value("a=b"), r#""a=b""#);
39        assert_eq!(value(""), r#""""#);
40    }
41}