rpp_parser/
serialize.rs

1use crate::parser::{Child, Element};
2use std::borrow::Cow;
3
4fn serialise_term(text: &str) -> Cow<str> {
5    if text.is_empty() {
6        return "\"\"".into();
7    }
8
9    let mut text: Cow<str> = text.into();
10
11    // backticks are not allowed in .rpp files, they are always replaced with single quotes
12    if text.contains('`') {
13        text = text.replace('`', "'").into();
14    }
15
16    let first_char = text.chars().next().unwrap();
17    let mut needs_to_be_quoted = first_char == '\'' || first_char == '"';
18
19    let mut has_dbl_quote = false;
20    let mut has_sgl_quote = false;
21    for x in text.chars() {
22        if x == ' ' {
23            needs_to_be_quoted = true;
24        } else if x == '\'' {
25            has_sgl_quote = true;
26        } else if x == '"' {
27            has_dbl_quote = true;
28        }
29    }
30
31    if !needs_to_be_quoted {
32        return text;
33    }
34
35    let quote_char = {
36        if has_dbl_quote && has_sgl_quote {
37            '`'
38        } else if has_dbl_quote {
39            '\''
40        } else {
41            '"'
42        }
43    };
44
45    format!("{}{}{}", quote_char, text, quote_char).into()
46}
47
48/// Serialise an element back to a [String] following the RPP format.
49pub fn serialize_to_string(element: &Element) -> String {
50    let mut buf = String::new();
51    process(&mut buf, element, 0);
52    buf
53}
54
55fn process(buf: &mut String, element: &Element, indent_level: usize) {
56    // first line
57    for _ in 0..indent_level {
58        buf.push_str("  ")
59    }
60    buf.push('<');
61    buf.push_str(element.tag);
62
63    for x in &element.attr {
64        let x = serialise_term(x);
65        buf.push(' ');
66        buf.push_str(&x);
67    }
68
69    buf.push('\n');
70
71    for child in element.children.iter() {
72        match child {
73            Child::Line(child) => {
74                for _ in 0..(indent_level + 1) {
75                    buf.push_str("  ")
76                }
77
78                let mut is_first = true;
79                for x in child {
80                    let x = serialise_term(x);
81                    if is_first {
82                        is_first = false;
83                    } else {
84                        buf.push(' ')
85                    }
86                    buf.push_str(&x);
87                }
88
89                buf.push('\n');
90            }
91            Child::Element(child) => {
92                process(buf, child, indent_level + 1);
93                buf.push('\n');
94            }
95        }
96    }
97
98    // last line
99    for _ in 0..indent_level {
100        buf.push_str("  ")
101    }
102    buf.push('>');
103}