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 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 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 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
65pub 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
204pub 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 _ => result.push(c),
220 }
221 }
222 std::borrow::Cow::Owned(result)
223 } else {
224 std::borrow::Cow::Borrowed(input.as_ref())
225 }
226}