1use std::fmt::{Display, Write};
2
3use crate::content::Content;
4
5const COMPACT_MAX_CHARS: usize = 120;
8
9pub fn format_float<T: Display>(value: T) -> String {
10 let mut rv = format!("{}", value);
11 if !rv.contains('.') {
12 rv.push_str(".0");
13 }
14 rv
15}
16
17#[derive(PartialEq, Eq, Copy, Clone, Debug)]
18pub enum Format {
19 Condensed,
20 SingleLine,
21 Pretty,
22}
23
24pub struct Serializer {
26 out: String,
27 format: Format,
28 indentation: usize,
29}
30
31impl Serializer {
32 pub fn new() -> Serializer {
34 Serializer {
35 out: String::new(),
36 format: Format::Condensed,
37 indentation: 0,
38 }
39 }
40
41 pub fn into_result(self) -> String {
42 self.out
43 }
44
45 fn write_indentation(&mut self) {
46 if self.format == Format::Pretty {
47 write!(self.out, "{: ^1$}", "", self.indentation * 2).unwrap();
48 }
49 }
50
51 fn start_container(&mut self, c: char) {
52 self.write_char(c);
53 self.indentation += 1;
54 }
55
56 fn end_container(&mut self, c: char, empty: bool) {
57 self.indentation -= 1;
58 if self.format == Format::Pretty && !empty {
59 self.write_char('\n');
60 self.write_indentation();
61 }
62 self.write_char(c);
63 }
64
65 fn write_comma(&mut self, first: bool) {
66 match self.format {
67 Format::Pretty => {
68 if first {
69 self.write_char('\n');
70 } else {
71 self.write_str(",\n");
72 }
73 self.write_indentation();
74 }
75 Format::Condensed => {
76 if !first {
77 self.write_char(',');
78 }
79 }
80 Format::SingleLine => {
81 if !first {
82 self.write_str(", ");
83 }
84 }
85 }
86 }
87
88 fn write_colon(&mut self) {
89 match self.format {
90 Format::Pretty | Format::SingleLine => self.write_str(": "),
91 Format::Condensed => self.write_char(':'),
92 }
93 }
94
95 fn serialize_array(&mut self, items: &[Content]) {
96 self.start_container('[');
97 for (idx, item) in items.iter().enumerate() {
98 self.write_comma(idx == 0);
99 self.serialize(item);
100 }
101 self.end_container(']', items.is_empty());
102 }
103
104 fn serialize_object(&mut self, fields: &[(&str, Content)]) {
105 self.start_container('{');
106 for (idx, (key, value)) in fields.iter().enumerate() {
107 self.write_comma(idx == 0);
108 self.write_escaped_str(key);
109 self.write_colon();
110 self.serialize(value);
111 }
112 self.end_container('}', fields.is_empty());
113 }
114
115 pub fn serialize(&mut self, value: &Content) {
116 match value {
117 Content::Bool(true) => self.write_str("true"),
118 Content::Bool(false) => self.write_str("false"),
119 Content::U8(n) => write!(self.out, "{}", n).unwrap(),
120 Content::U16(n) => write!(self.out, "{}", n).unwrap(),
121 Content::U32(n) => write!(self.out, "{}", n).unwrap(),
122 Content::U64(n) => write!(self.out, "{}", n).unwrap(),
123 Content::U128(n) => write!(self.out, "{}", n).unwrap(),
124 Content::I8(n) => write!(self.out, "{}", n).unwrap(),
125 Content::I16(n) => write!(self.out, "{}", n).unwrap(),
126 Content::I32(n) => write!(self.out, "{}", n).unwrap(),
127 Content::I64(n) => write!(self.out, "{}", n).unwrap(),
128 Content::I128(n) => write!(self.out, "{}", n).unwrap(),
129 Content::F32(f) => {
130 if f.is_finite() {
131 self.write_str(&format_float(f));
132 } else {
133 self.write_str("null")
134 }
135 }
136 Content::F64(f) => {
137 if f.is_finite() {
138 self.write_str(&format_float(f));
139 } else {
140 self.write_str("null")
141 }
142 }
143 Content::Char(c) => self.write_escaped_str(&(*c).to_string()),
144 Content::String(s) => self.write_escaped_str(s),
145 Content::Bytes(bytes) => {
146 self.start_container('[');
147 for (idx, byte) in bytes.iter().enumerate() {
148 self.write_comma(idx == 0);
149 self.write_str(&byte.to_string());
150 }
151 self.end_container(']', bytes.is_empty());
152 }
153 Content::None | Content::Unit | Content::UnitStruct(_) => self.write_str("null"),
154 Content::Some(content) => self.serialize(content),
155 Content::UnitVariant(_, _, variant) => self.write_escaped_str(variant),
156 Content::NewtypeStruct(_, content) => self.serialize(content),
157 Content::NewtypeVariant(_, _, variant, content) => {
158 self.start_container('{');
159 self.write_comma(true);
160 self.write_escaped_str(variant);
161 self.write_colon();
162 self.serialize(content);
163 self.end_container('}', false);
164 }
165 Content::Seq(seq) | Content::Tuple(seq) | Content::TupleStruct(_, seq) => {
166 self.serialize_array(seq);
167 }
168 Content::TupleVariant(_, _, variant, seq) => {
169 self.start_container('{');
170 self.write_comma(true);
171 self.write_escaped_str(variant);
172 self.write_colon();
173 self.serialize_array(seq);
174 self.end_container('}', false);
175 }
176 Content::Map(map) => {
177 self.start_container('{');
178 for (idx, (key, value)) in map.iter().enumerate() {
179 self.write_comma(idx == 0);
180 let real_key = key.resolve_inner();
181 if let Content::String(ref s) = real_key {
182 self.write_escaped_str(s);
183 } else if let Some(num) = real_key.as_i64() {
184 self.write_escaped_str(&num.to_string());
185 } else if let Some(num) = real_key.as_i128() {
186 self.write_escaped_str(&num.to_string());
187 } else {
188 panic!("cannot serialize maps without string keys to JSON");
189 }
190 self.write_colon();
191 self.serialize(value);
192 }
193 self.end_container('}', map.is_empty());
194 }
195 Content::Struct(_, fields) => {
196 self.serialize_object(fields);
197 }
198 Content::StructVariant(_, _, variant, fields) => {
199 self.start_container('{');
200 self.write_comma(true);
201 self.write_escaped_str(variant);
202 self.write_colon();
203 self.serialize_object(fields);
204 self.end_container('}', false);
205 }
206 }
207 }
208
209 fn write_str(&mut self, s: &str) {
210 self.out.push_str(s);
211 }
212
213 fn write_char(&mut self, c: char) {
214 self.out.push(c);
215 }
216
217 fn write_escaped_str(&mut self, value: &str) {
218 self.write_char('"');
219
220 let bytes = value.as_bytes();
221 let mut start = 0;
222
223 for (i, &byte) in bytes.iter().enumerate() {
224 let escape = ESCAPE[byte as usize];
225 if escape == 0 {
226 continue;
227 }
228
229 if start < i {
230 self.write_str(&value[start..i]);
231 }
232
233 match escape {
234 self::BB => self.write_str("\\b"),
235 self::TT => self.write_str("\\t"),
236 self::NN => self.write_str("\\n"),
237 self::FF => self.write_str("\\f"),
238 self::RR => self.write_str("\\r"),
239 self::QU => self.write_str("\\\""),
240 self::BS => self.write_str("\\\\"),
241 self::U => {
242 static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef";
243 self.write_str("\\u00");
244 self.write_char(HEX_DIGITS[(byte >> 4) as usize] as char);
245 self.write_char(HEX_DIGITS[(byte & 0xF) as usize] as char);
246 }
247 _ => unreachable!(),
248 }
249
250 start = i + 1;
251 }
252
253 if start != bytes.len() {
254 self.write_str(&value[start..]);
255 }
256
257 self.write_char('"');
258 }
259}
260
261const BB: u8 = b'b'; const TT: u8 = b't'; const NN: u8 = b'n'; const FF: u8 = b'f'; const RR: u8 = b'r'; const QU: u8 = b'"'; const BS: u8 = b'\\'; const U: u8 = b'u'; #[rustfmt::skip]
273static ESCAPE: [u8; 256] = [
274 U, U, U, U, U, U, U, U, BB, TT, NN, U, FF, RR, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, U, 0, 0, QU, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, BS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
292
293pub fn to_string(value: &Content) -> String {
295 let mut ser = Serializer::new();
296 ser.serialize(value);
297 ser.into_result()
298}
299
300#[allow(unused)]
302pub fn to_string_compact(value: &Content) -> String {
303 let mut ser = Serializer::new();
304 ser.format = Format::SingleLine;
305 ser.serialize(value);
306 let rv = ser.into_result();
307 if rv.chars().count() > COMPACT_MAX_CHARS {
311 to_string_pretty(value)
312 } else {
313 rv
314 }
315}
316
317#[allow(unused)]
319pub fn to_string_pretty(value: &Content) -> String {
320 let mut ser = Serializer::new();
321 ser.format = Format::Pretty;
322 ser.serialize(value);
323 ser.into_result()
324}
325
326#[test]
327fn test_to_string() {
328 let json = to_string(&Content::Map(vec![
329 (
330 Content::from("environments"),
331 Content::Seq(vec![
332 Content::from("development"),
333 Content::from("production"),
334 ]),
335 ),
336 (Content::from("cmdline"), Content::Seq(vec![])),
337 (Content::from("extra"), Content::Map(vec![])),
338 ]));
339 crate::assert_snapshot!(&json, @r###"{"environments":["development","production"],"cmdline":[],"extra":{}}"###);
340}
341
342#[test]
343fn test_to_string_pretty() {
344 let json = to_string_pretty(&Content::Map(vec![
345 (
346 Content::from("environments"),
347 Content::Seq(vec![
348 Content::from("development"),
349 Content::from("production"),
350 ]),
351 ),
352 (Content::from("cmdline"), Content::Seq(vec![])),
353 (Content::from("extra"), Content::Map(vec![])),
354 ]));
355 crate::assert_snapshot!(&json, @r###"
356 {
357 "environments": [
358 "development",
359 "production"
360 ],
361 "cmdline": [],
362 "extra": {}
363 }
364 "###);
365}
366
367#[test]
368fn test_to_string_num_keys() {
369 let content = Content::Map(vec![
370 (Content::from(42u32), Content::from(true)),
371 (Content::from(-23i32), Content::from(false)),
372 ]);
373 let json = to_string_pretty(&content);
374 crate::assert_snapshot!(&json, @r###"
375 {
376 "42": true,
377 "-23": false
378 }
379 "###);
380}
381
382#[test]
383fn test_to_string_pretty_complex() {
384 let content = Content::Map(vec![
385 (
386 Content::from("is_alive"),
387 Content::NewtypeStruct("Some", Content::from(true).into()),
388 ),
389 (
390 Content::from("newtype_variant"),
391 Content::NewtypeVariant(
392 "Foo",
393 0,
394 "variant_a",
395 Box::new(Content::Struct(
396 "VariantA",
397 vec![
398 ("field_a", Content::String("value_a".into())),
399 ("field_b", 42u32.into()),
400 ],
401 )),
402 ),
403 ),
404 (
405 Content::from("struct_variant"),
406 Content::StructVariant(
407 "Foo",
408 0,
409 "variant_b",
410 vec![
411 ("field_a", Content::String("value_a".into())),
412 ("field_b", 42u32.into()),
413 ],
414 ),
415 ),
416 (
417 Content::from("tuple_variant"),
418 Content::TupleVariant(
419 "Foo",
420 0,
421 "variant_c",
422 vec![(Content::String("value_a".into())), (42u32.into())],
423 ),
424 ),
425 (Content::from("empty_array"), Content::Seq(vec![])),
426 (Content::from("empty_object"), Content::Map(vec![])),
427 (Content::from("array"), Content::Seq(vec![true.into()])),
428 (
429 Content::from("object"),
430 Content::Map(vec![("foo".into(), true.into())]),
431 ),
432 (
433 Content::from("array_of_objects"),
434 Content::Seq(vec![Content::Struct(
435 "MyType",
436 vec![
437 ("foo", Content::from("bar".to_string())),
438 ("bar", Content::from("xxx".to_string())),
439 ],
440 )]),
441 ),
442 (
443 Content::from("unit_variant"),
444 Content::UnitVariant("Stuff", 0, "value"),
445 ),
446 (Content::from("u8"), Content::U8(8)),
447 (Content::from("u16"), Content::U16(16)),
448 (Content::from("u32"), Content::U32(32)),
449 (Content::from("u64"), Content::U64(64)),
450 (Content::from("u128"), Content::U128(128)),
451 (Content::from("i8"), Content::I8(8)),
452 (Content::from("i16"), Content::I16(16)),
453 (Content::from("i32"), Content::I32(32)),
454 (Content::from("i64"), Content::I64(64)),
455 (Content::from("i128"), Content::I128(128)),
456 (Content::from("f32"), Content::F32(32.0)),
457 (Content::from("f64"), Content::F64(64.0)),
458 (Content::from("char"), Content::Char('A')),
459 (Content::from("bytes"), Content::Bytes(b"hehe".to_vec())),
460 (Content::from("null"), Content::None),
461 (Content::from("unit"), Content::Unit),
462 (
463 Content::from("crazy_string"),
464 Content::String((0u8..=126).map(|x| x as char).collect()),
465 ),
466 ]);
467 let json = to_string_pretty(&content);
468
469 crate::assert_snapshot!(&json, @r###"
470 {
471 "is_alive": true,
472 "newtype_variant": {
473 "variant_a": {
474 "field_a": "value_a",
475 "field_b": 42
476 }
477 },
478 "struct_variant": {
479 "variant_b": {
480 "field_a": "value_a",
481 "field_b": 42
482 }
483 },
484 "tuple_variant": {
485 "variant_c": [
486 "value_a",
487 42
488 ]
489 },
490 "empty_array": [],
491 "empty_object": {},
492 "array": [
493 true
494 ],
495 "object": {
496 "foo": true
497 },
498 "array_of_objects": [
499 {
500 "foo": "bar",
501 "bar": "xxx"
502 }
503 ],
504 "unit_variant": "value",
505 "u8": 8,
506 "u16": 16,
507 "u32": 32,
508 "u64": 64,
509 "u128": 128,
510 "i8": 8,
511 "i16": 16,
512 "i32": 32,
513 "i64": 64,
514 "i128": 128,
515 "f32": 32.0,
516 "f64": 64.0,
517 "char": "A",
518 "bytes": [
519 104,
520 101,
521 104,
522 101
523 ],
524 "null": null,
525 "unit": null,
526 "crazy_string": "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
527 }
528 "###);
529}