1use std::fmt;
2
3#[derive(Debug, PartialEq, Eq)]
4pub(crate) enum SerializerError {
5 InvalidKey,
6 FmtError,
7}
8
9impl From<std::fmt::Error> for SerializerError {
10 fn from(_: std::fmt::Error) -> Self {
11 SerializerError::FmtError
12 }
13}
14
15pub(crate) struct Serializer<W> {
17 pub(crate) writer: W,
18 writing_first_entry: bool,
19 #[cfg(feature = "ansi_logs")]
20 with_ansi_color: bool,
21}
22
23impl<W> Serializer<W>
24where
25 W: fmt::Write,
26{
27 #[inline]
28 pub(crate) fn new(writer: W, #[cfg(feature = "ansi_logs")] with_ansi_color: bool) -> Self {
29 Serializer {
30 writer,
31 writing_first_entry: true,
32 #[cfg(feature = "ansi_logs")]
33 with_ansi_color,
34 }
35 }
36
37 #[cfg(not(feature = "ansi_logs"))]
38 pub(crate) fn serialize_entry(
39 &mut self,
40 key: &str,
41 value: &str,
42 ) -> Result<(), SerializerError> {
43 self.serialize_entry_with(key, value, |this, value| this.serialize_value(value))
44 }
45
46 #[cfg(feature = "ansi_logs")]
47 pub(crate) fn serialize_entry(
48 &mut self,
49 key: &str,
50 value: &str,
51 ) -> Result<(), SerializerError> {
52 if let "level" = key {
53 self.serialize_entry_with(key, value, |this, value| this.serialize_level(value))
54 } else {
55 self.serialize_entry_with(key, value, |this, value| this.serialize_value(value))
56 }
57 }
58
59 pub(crate) fn serialize_entry_no_quote(
60 &mut self,
61 key: &str,
62 value: impl fmt::Debug,
63 ) -> Result<(), SerializerError> {
64 self.serialize_entry_with(key, value, |this, value| {
65 this.serialize_value_no_quote(value)
66 })
67 }
68
69 fn serialize_entry_with<F, T>(
70 &mut self,
71 key: &str,
72 value: T,
73 serialize_value: F,
74 ) -> Result<(), SerializerError>
75 where
76 F: FnOnce(&mut Self, T) -> Result<(), SerializerError>,
77 {
78 self.serialize_key(key)?;
79 self.writer.write_char('=')?;
80 serialize_value(self, value)?;
81
82 Ok(())
83 }
84 pub(crate) fn serialize_key(&mut self, key: &str) -> Result<(), SerializerError> {
85 if !self.writing_first_entry {
86 self.writer.write_char(' ')?;
87 }
88 self.writing_first_entry = false;
89
90 let mut chars = key.chars().filter(|&ch| !need_quote(ch)).peekable();
91
92 if chars.peek().is_none() {
93 return Err(SerializerError::InvalidKey);
94 }
95
96 #[cfg(not(feature = "ansi_logs"))]
97 {
98 for c in chars {
99 self.writer.write_char(c)?;
100 }
101 }
102
103 #[cfg(feature = "ansi_logs")]
104 {
105 if self.with_ansi_color {
106 let mut s = String::new();
107 for c in chars {
108 s.push(c);
109 }
110 self.writer.write_str(
111 &nu_ansi_term::Color::Rgb(109, 139, 140)
112 .bold()
113 .paint(s)
114 .to_string(),
115 )?;
116 } else {
117 for c in chars {
118 self.writer.write_char(c)?;
119 }
120 }
121 }
122 Ok(())
123 }
124
125 pub(crate) fn serialize_value(&mut self, value: &str) -> Result<(), SerializerError> {
126 if value.chars().any(need_quote) {
127 self.writer.write_char('"')?;
128 write!(self.writer, "{}", value.escape_debug())?;
129 self.writer.write_char('"')?;
130 } else {
131 self.writer.write_str(value)?;
132 }
133
134 Ok(())
135 }
136
137 fn serialize_value_no_quote(&mut self, value: impl fmt::Debug) -> Result<(), SerializerError> {
138 write!(self.writer, "{:?}", value)?;
139 Ok(())
140 }
141
142 #[cfg(feature = "ansi_logs")]
143 fn serialize_level(&mut self, value: &str) -> Result<(), SerializerError> {
144 write!(self.writer, "{}", value)?;
145 Ok(())
146 }
147}
148
149#[inline]
150pub(crate) fn need_quote(ch: char) -> bool {
151 ch <= ' ' || matches!(ch, '=' | '"')
152}
153
154impl<W> std::io::Write for Serializer<W>
155where
156 W: fmt::Write,
157{
158 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
159 if let Ok(buf) = std::str::from_utf8(buf) {
160 self.writer
161 .write_str(buf)
162 .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err))?;
163 }
164 Ok(buf.len())
165 }
166
167 fn flush(&mut self) -> std::io::Result<()> {
168 Ok(())
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 #[cfg(not(feature = "ansi_logs"))]
178 fn test_serialize_entries() {
179 let mut output = String::new();
180 let mut s = Serializer::new(&mut output);
181 assert!(s.serialize_entry("key", "value").is_ok());
182 assert!(s.serialize_entry("key2", "value2").is_ok());
183
184 assert_eq!(output, "key=value key2=value2");
185 }
186
187 #[test]
188 #[cfg(not(feature = "ansi_logs"))]
189 fn test_serialize_entry() {
190 let tests = vec![
191 (("key", "value"), "key=value"),
192 (("ke=y", "value="), "key=\"value=\""),
193 (("key ", "value "), "key=\"value \""),
194 (("ke\"y", "valu\"e"), "key=\"valu\\\"e\""),
195 (("ke\ny", "valu\ne"), "key=\"valu\\ne\""),
196 ];
197
198 for ((k, v), expected_output) in tests {
199 let mut output = String::new();
200 let mut s = Serializer::new(&mut output);
201 assert!(s.serialize_entry(k, v).is_ok());
202 assert_eq!(output, expected_output,);
203 }
204 }
205
206 #[test]
207 #[cfg(feature = "ansi_logs")]
208 fn test_serialize_entry() {
209 let tests = vec![
210 (("key", "value"), make_ansi_key_value("key", "=value")),
211 (
212 ("ke=y", "value="),
213 make_ansi_key_value("key", "=\"value=\""),
214 ),
215 (
216 ("key ", "value "),
217 make_ansi_key_value("key", "=\"value \""),
218 ),
219 (("lev\"el", "info"), make_ansi_key_value("level", "=info")),
220 (
221 ("ke\ny", "valu\ne"),
222 make_ansi_key_value("key", "=\"valu\\ne\""),
223 ),
224 ];
225
226 for ((k, v), expected_output) in tests {
227 let mut output = String::new();
228 let mut s = Serializer::new(&mut output, true);
229 assert!(s.serialize_entry(k, v).is_ok());
230 assert_eq!(output, expected_output,);
231 }
232
233 fn make_ansi_key_value(key: &str, value: &str) -> String {
234 let mut key = nu_ansi_term::Color::Rgb(109, 139, 140)
235 .bold()
236 .paint(key)
237 .to_string();
238 key.push_str(value);
239 key
240 }
241 }
242
243 #[test]
244 #[cfg(not(feature = "ansi_logs"))]
245 fn test_serialize_key() {
246 let tests = vec![
247 ("key", "key"),
248 ("k ey", "key"),
249 ("k\"ey", "key"),
250 ("k=ey", "key"),
251 ("k\ney", "key"),
252 ];
253 for (input, expected_output) in tests {
254 let mut output = String::new();
255
256 let mut s = Serializer::new(&mut output);
257 assert!(s.serialize_key(input).is_ok());
258
259 assert_eq!(output, expected_output);
260 }
261 }
262
263 #[test]
264 fn test_serialize_key_invalid() {
265 let tests = vec![
266 ("", SerializerError::InvalidKey),
267 (" ", SerializerError::InvalidKey),
268 ("=", SerializerError::InvalidKey),
269 ("\"", SerializerError::InvalidKey),
270 ];
271
272 for (input, expected_error) in tests {
273 let mut output = String::new();
274
275 #[cfg(not(feature = "ansi_logs"))]
276 let mut s = Serializer::new(&mut output);
277 #[cfg(feature = "ansi_logs")]
278 let mut s = Serializer::new(&mut output, true);
279 assert_eq!(s.serialize_key(input), Err(expected_error));
280 }
281 }
282
283 #[test]
284 fn test_serialize_value() {
285 let tests = vec![
286 ("", ""),
287 ("v", "v"),
288 (" ", r#"" ""#),
289 ("=", r#""=""#),
290 (r#"\"#, r#"\"#),
291 (r#"""#, r#""\"""#),
292 (r#"\""#, r#""\\\"""#),
293 ("\n", r#""\n""#),
294 ("\x00", r#""\0""#),
295 ("\x10", r#""\u{10}""#),
296 ("\x1F", r#""\u{1f}""#),
297 ("µ", r#"µ"#),
298 ("åäö", r#"åäö"#),
299 ];
300
301 for (input, expected_output) in tests {
302 let mut output = String::new();
303
304 #[cfg(not(feature = "ansi_logs"))]
305 let mut s = Serializer::new(&mut output);
306 #[cfg(feature = "ansi_logs")]
307 let mut s = Serializer::new(&mut output, true);
308
309 assert!(s.serialize_value(input).is_ok());
310
311 assert_eq!(output, expected_output);
312 }
313 }
314}