agprefs/
composer.rs

1use crate::types::{Agpref, Value};
2use cookie_factory::{combinator::string, sequence::tuple, GenResult};
3use std::io::BufWriter;
4use std::io::Write;
5
6impl Agpref<'_> {
7    /// Write the struct to a buffer
8    pub fn write<W: Write>(&self, mut w: W) -> Result<(), crate::errors::Errors> {
9        let mut bw = BufWriter::new(&mut w);
10        let cfw = cookie_factory::WriteContext::from(&mut bw);
11        gen_agpref(self, cfw)?;
12        Ok(())
13    }
14    /// Write the struct to a string
15    pub fn to_str(&self) -> Result<String, crate::errors::Errors> {
16        let mut buf = Vec::new();
17        let cfw = cookie_factory::WriteContext::from(&mut buf);
18        gen_agpref(self, cfw)?;
19        Ok(String::from_utf8(buf)?)
20    }
21}
22
23fn gen_agpref<W: Write>(
24    agpref: &Agpref,
25    writer: cookie_factory::WriteContext<W>,
26) -> cookie_factory::GenResult<W> {
27    let mut result = writer;
28    result = string(&agpref.name)(result)?;
29    result = string(" = ")(result)?;
30
31    // let mut len = match agpref.values {
32    //     Value::Struct(ref s) => s.len(),
33    //     Value::Values(ref v) => v.len(),
34    //     _ => panic!("Impossible"),
35    // };
36    // for (name, value) in agpref.values.iter() {
37    //     result = string(name)(result)?;
38    //     result = string(" = ")(result)?;
39    //     result = compose_value(value, result)?;
40    //     if len > 1 {
41    //         result = string(",\n")(result)?;
42    //         len -= 1;
43    //     }
44    // }
45    result = compose_value(
46        &agpref.values,
47        Info {
48            newline: true,
49            ..Default::default()
50        },
51        result,
52    )?;
53
54    result = string("\n")(result)?;
55    Ok(result)
56}
57
58#[derive(Debug, Clone, Copy, Default)]
59pub struct Info {
60    newline: bool,
61    inherit: bool,
62    depth: usize,
63}
64
65// #[derive(Debug, Clone, Copy, Default)]
66// pub struct ComposeInfo {
67//     pub newline: bool,
68//     pub inherit: Inherit,
69//     pub indent: Indent,
70// }
71
72// impl ComposeInfo {
73//     pub fn step_in(self) -> Self {
74//         Self {
75//             indent: Indent {
76//                 indent: self.indent.indent,
77//                 depth: self.indent.depth + 1,
78//             },
79//             inherit: Inherit {
80//                 inherit: self.inherit.inherit,
81//                 max_depth: self.inherit.max_depth - 1,
82//             },
83//             ..self
84//         }
85//     }
86// }
87
88// #[derive(Debug, Clone, Copy, Default)]
89// pub struct Inherit {
90//     pub inherit: bool,
91//     pub max_depth: usize,
92// }
93
94// #[derive(Debug, Clone, Copy, Default)]
95// pub struct Indent {
96//     pub indent: bool,
97//     pub depth: usize,
98// }
99
100pub fn compose_value<W: Write>(
101    value: &Value,
102    info: Info,
103    writer: cookie_factory::WriteContext<W>,
104) -> GenResult<W> {
105    let result = match value {
106        Value::String(s) => tuple((string("\""), string(escape_string(s)), string("\"")))(writer)?,
107        Value::Int(i) => string(i.to_string())(writer)?,
108        Value::Float(f) => string(f.to_string())(writer)?,
109        Value::Bool(b) => string(b.to_string())(writer)?,
110        Value::Values(values) => {
111            let mut result = writer;
112            if info.newline {
113                result = string("{\n")(result)?;
114            } else {
115                result = string("{ ")(result)?;
116            }
117
118            let mut len = values.len();
119            for value in values {
120                result = compose_value(
121                    value,
122                    Info {
123                        inherit: if info.depth > 0 { info.inherit } else { false },
124                        depth: if info.depth > 1 { info.depth - 1 } else { 0 },
125                        newline: if info.depth > 0 { info.newline } else { false },
126                    },
127                    result,
128                )?;
129                if len > 1 {
130                    result = string(",\n")(result)?;
131                    len -= 1;
132                }
133            }
134            result = string(" }")(result)?;
135            result
136        }
137        Value::Struct(s) => {
138            let mut result = writer;
139            if info.newline {
140                result = string("{\n")(result)?;
141            } else {
142                result = string("{ ")(result)?;
143            }
144            let mut len = s.len();
145            for (name, value) in s {
146                result = string(name)(result)?;
147                result = string(" = ")(result)?;
148                result = compose_value(
149                    value,
150                    Info {
151                        inherit: if info.depth > 0 { info.inherit } else { false },
152                        depth: if info.depth > 1 { info.depth - 1 } else { 0 },
153                        newline: if info.depth > 0 { info.newline } else { false },
154                    },
155                    result,
156                )?;
157                if len > 1 {
158                    result = string(",\n")(result)?;
159                    len -= 1;
160                }
161            }
162            if info.newline {
163                result = string("\n}")(result)?;
164            } else {
165                result = string(" }")(result)?;
166            }
167            result
168        }
169        Value::Unit => string("{ }")(writer)?,
170    };
171    Ok(result)
172}
173
174#[cfg(feature = "namedlist")]
175pub fn compose_namedlist<W: Write>(
176    namedlist: &crate::types::NamedList,
177    writer: cookie_factory::WriteContext<W>,
178) -> GenResult<W> {
179    let mut result = writer;
180    result = string("\"")(result)?;
181    result = string(&namedlist.name)(result)?;
182    result = string(" = {\\\n")(result)?;
183    let mut len = namedlist.values.len();
184    for value in &namedlist.values {
185        result = if let Value::String(s) = value {
186            tuple((
187                string("\\\""),
188                string(escape_string(&escape_string(s))),
189                string("\\\""),
190            ))(result)?
191        } else {
192            compose_value(&value, result)?
193        };
194        if len > 1 {
195            result = string(",\\\n")(result)?;
196            len -= 1;
197        }
198    }
199    result = string(" }\\\n")(result)?;
200    result = string("\"")(result)?;
201    Ok(result)
202}
203
204// #[cfg(feature = "namedlist")]
205pub fn escape_string<'str>(
206    input: &'str (impl AsRef<str> + 'str + ?Sized),
207) -> std::borrow::Cow<'str, str> {
208    if memchr::memchr3(b'\\', b'"', b'\n', input.as_ref().as_bytes()).is_some()
209        || memchr::memchr2(b'\r', b'\t', input.as_ref().as_bytes()).is_some()
210    {
211        let mut result = String::with_capacity(input.as_ref().len() + 20);
212        for c in input.as_ref().chars() {
213            match c {
214                '\\' => result.push_str("\\\\"),
215                '"' => result.push_str("\\\""),
216                '\n' => result.push_str("\\\n"),
217                '\r' => result.push_str("\\r"),
218                // '\t' => result.push_str("\\t"),
219                _ => result.push(c),
220            }
221        }
222        std::borrow::Cow::Owned(result)
223    } else {
224        std::borrow::Cow::Borrowed(input.as_ref())
225    }
226}