1use compact_str::CompactString;
85use serde::Serialize;
86use serde_json::ser::Formatter;
87use serde_json::Serializer;
88use std::fmt;
89use std::io::{self, Write};
90
91#[derive(Clone, Debug, Eq, Hash, PartialEq)]
105pub struct JsonFormat {
106 indent: Option<CompactString>,
107 comma: CompactString,
108 colon: CompactString,
109 ascii: bool,
110}
111
112impl JsonFormat {
113 pub fn new() -> Self {
122 JsonFormat {
123 indent: None,
124 comma: ",".into(),
125 colon: ":".into(),
126 ascii: false,
127 }
128 }
129
130 pub fn pretty() -> Self {
139 JsonFormat {
140 indent: Some(" ".into()),
141 comma: ",".into(),
142 colon: ": ".into(),
143 ascii: false,
144 }
145 }
146
147 pub fn ascii(mut self, flag: bool) -> Self {
152 self.ascii = flag;
153 self
154 }
155
156 pub fn comma<S: AsRef<str>>(mut self, s: S) -> Result<Self, JsonSyntaxError> {
166 self.comma = validate_string(s, Some(','))?;
167 Ok(self)
168 }
169
170 pub fn colon<S: AsRef<str>>(mut self, s: S) -> Result<Self, JsonSyntaxError> {
180 self.colon = validate_string(s, Some(':'))?;
181 Ok(self)
182 }
183
184 pub fn indent<S: AsRef<str>>(mut self, s: Option<S>) -> Result<Self, JsonSyntaxError> {
197 self.indent = s.map(|s| validate_string(s, None)).transpose()?;
198 Ok(self)
199 }
200
201 pub fn indent_width(self, n: Option<usize>) -> Self {
208 let Ok(me) = self.indent(n.map(|i| CompactString::from(" ").repeat(i))) else {
209 unreachable!("repeated spaces should be valid indentation");
210 };
211 me
212 }
213
214 pub fn format_to_string<T: ?Sized + Serialize>(
221 &self,
222 value: &T,
223 ) -> Result<String, serde_json::Error> {
224 let bytes = self.format_to_vec(value)?;
225 let Ok(s) = String::from_utf8(bytes) else {
226 unreachable!("serialized JSON should be valid UTF-8");
227 };
228 Ok(s)
229 }
230
231 pub fn format_to_vec<T: ?Sized + Serialize>(
238 &self,
239 value: &T,
240 ) -> Result<Vec<u8>, serde_json::Error> {
241 let mut vec = Vec::with_capacity(128);
242 self.format_to_writer(&mut vec, value)?;
243 Ok(vec)
244 }
245
246 pub fn format_to_writer<T: ?Sized + Serialize, W: Write>(
253 &self,
254 writer: W,
255 value: &T,
256 ) -> Result<(), serde_json::Error> {
257 let mut ser = Serializer::with_formatter(writer, self.as_formatter());
258 value.serialize(&mut ser)
259 }
260
261 pub fn build(self) -> JsonFormatter {
270 JsonFormatter::new(self)
271 }
272
273 pub fn as_formatter(&self) -> JsonFrmtr<'_> {
285 JsonFrmtr::new(internal::JsonFmt {
286 indent: self.indent.as_ref().map(CompactString::as_bytes),
287 comma: self.comma.as_bytes(),
288 colon: self.colon.as_bytes(),
289 ascii: self.ascii,
290 })
291 }
292}
293
294impl Default for JsonFormat {
295 fn default() -> Self {
297 JsonFormat::new()
298 }
299}
300
301mod internal {
304 use super::*;
305
306 pub trait OptionsData {
307 fn indent(&self) -> Option<&[u8]>;
308 fn comma(&self) -> &[u8];
309 fn colon(&self) -> &[u8];
310 fn ascii(&self) -> bool;
311 }
312
313 impl OptionsData for JsonFormat {
314 fn indent(&self) -> Option<&[u8]> {
315 self.indent.as_ref().map(CompactString::as_bytes)
316 }
317
318 fn comma(&self) -> &[u8] {
319 self.comma.as_bytes()
320 }
321
322 fn colon(&self) -> &[u8] {
323 self.colon.as_bytes()
324 }
325
326 fn ascii(&self) -> bool {
327 self.ascii
328 }
329 }
330
331 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
332 pub struct JsonFmt<'a> {
333 pub indent: Option<&'a [u8]>,
334 pub comma: &'a [u8],
335 pub colon: &'a [u8],
336 pub ascii: bool,
337 }
338
339 impl OptionsData for JsonFmt<'_> {
340 fn indent(&self) -> Option<&[u8]> {
341 self.indent
342 }
343
344 fn comma(&self) -> &[u8] {
345 self.comma
346 }
347
348 fn colon(&self) -> &[u8] {
349 self.colon
350 }
351
352 fn ascii(&self) -> bool {
353 self.ascii
354 }
355 }
356
357 #[derive(Clone, Debug, Eq, Hash, PartialEq)]
358 pub struct JsonFormatterBase<O> {
359 indent_level: usize,
360 indent_next: bool,
361 options: O,
362 }
363
364 impl<O: OptionsData> JsonFormatterBase<O> {
365 pub fn new(options: O) -> Self {
366 JsonFormatterBase {
367 indent_level: 0,
368 indent_next: false,
369 options,
370 }
371 }
372
373 fn print_indent<W: ?Sized + Write>(&self, writer: &mut W) -> io::Result<()> {
374 if let Some(indent) = self.options.indent() {
375 writer.write_all(b"\n")?;
376 for _ in 0..self.indent_level {
377 writer.write_all(indent)?;
378 }
379 }
380 Ok(())
381 }
382 }
383
384 impl<O: OptionsData> Formatter for JsonFormatterBase<O> {
385 fn begin_array<W: ?Sized + Write>(&mut self, writer: &mut W) -> io::Result<()> {
386 self.indent_level += 1;
387 self.indent_next = false;
388 writer.write_all(b"[")
389 }
390
391 fn begin_array_value<W: ?Sized + Write>(
392 &mut self,
393 writer: &mut W,
394 first: bool,
395 ) -> io::Result<()> {
396 if !first {
397 writer.write_all(self.options.comma())?;
398 }
399 self.print_indent(writer)
400 }
401
402 fn end_array_value<W: ?Sized + Write>(&mut self, _writer: &mut W) -> io::Result<()> {
403 self.indent_next = true;
404 Ok(())
405 }
406
407 fn end_array<W: ?Sized + Write>(&mut self, writer: &mut W) -> io::Result<()> {
408 self.indent_level -= 1;
409 if self.indent_next {
410 self.print_indent(writer)?;
411 }
412 writer.write_all(b"]")
413 }
414
415 fn begin_object<W: ?Sized + Write>(&mut self, writer: &mut W) -> io::Result<()> {
416 self.indent_level += 1;
417 self.indent_next = false;
418 writer.write_all(b"{")
419 }
420
421 fn begin_object_key<W: ?Sized + Write>(
422 &mut self,
423 writer: &mut W,
424 first: bool,
425 ) -> io::Result<()> {
426 if !first {
427 writer.write_all(self.options.comma())?;
428 }
429 self.print_indent(writer)
430 }
431
432 fn begin_object_value<W: ?Sized + Write>(&mut self, writer: &mut W) -> io::Result<()> {
433 writer.write_all(self.options.colon())
434 }
435
436 fn end_object_value<W: ?Sized + Write>(&mut self, _writer: &mut W) -> io::Result<()> {
437 self.indent_next = true;
438 Ok(())
439 }
440
441 fn end_object<W: ?Sized + Write>(&mut self, writer: &mut W) -> io::Result<()> {
442 self.indent_level -= 1;
443 if self.indent_next {
444 self.print_indent(writer)?;
445 }
446 writer.write_all(b"}")
447 }
448
449 fn write_string_fragment<W: ?Sized + Write>(
450 &mut self,
451 writer: &mut W,
452 fragment: &str,
453 ) -> io::Result<()> {
454 for ch in fragment.chars() {
455 if !self.options.ascii() || ch.is_ascii() {
456 writer.write_all(ch.encode_utf8(&mut [0; 4]).as_bytes())?;
457 } else {
458 for surrogate in ch.encode_utf16(&mut [0; 2]) {
459 write!(writer, "\\u{surrogate:04x}")?;
460 }
461 }
462 }
463 Ok(())
464 }
465 }
466}
467
468pub type JsonFormatter = internal::JsonFormatterBase<JsonFormat>;
472
473pub type JsonFrmtr<'a> = internal::JsonFormatterBase<internal::JsonFmt<'a>>;
479
480#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
483pub enum JsonSyntaxError {
484 InvalidCharacter(char),
487
488 MissingSeparator(char),
492
493 MultipleSeparators(char),
497}
498
499impl fmt::Display for JsonSyntaxError {
500 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
501 use JsonSyntaxError::*;
502 match self {
503 InvalidCharacter(c) => write!(f, "string contains unexpected character {c:?}"),
504 MissingSeparator(c) => write!(f, "no occurrence of {c:?} found in string"),
505 MultipleSeparators(c) => write!(f, "multiple occurrences of {c:?} found in string"),
506 }
507 }
508}
509
510impl std::error::Error for JsonSyntaxError {}
511
512fn validate_string<S: AsRef<str>>(
513 s: S,
514 sep: Option<char>,
515) -> Result<CompactString, JsonSyntaxError> {
516 let s = s.as_ref();
517 let mut seen_sep = false;
518 for ch in s.chars() {
519 match (sep, ch) {
520 (Some(sep_), ch) if sep_ == ch => {
521 if std::mem::replace(&mut seen_sep, true) {
522 return Err(JsonSyntaxError::MultipleSeparators(sep_));
523 }
524 }
525 (_, ' ' | '\t' | '\n' | '\r') => (),
527 (_, ch) => return Err(JsonSyntaxError::InvalidCharacter(ch)),
528 }
529 }
530 if let Some(sep_) = sep {
531 if !seen_sep {
532 return Err(JsonSyntaxError::MissingSeparator(sep_));
533 }
534 }
535 Ok(s.into())
536}
537
538#[cfg(test)]
539mod tests {
540 use super::*;
541 use indoc::indoc;
542 use rstest::rstest;
543 use serde_json::json;
544
545 #[rstest]
546 #[case("?", Ok("?".into()))]
547 #[case(" ?", Ok(" ?".into()))]
548 #[case("? ", Ok("? ".into()))]
549 #[case(" ? ", Ok(" ? ".into()))]
550 #[case(" \t?\r\n", Ok(" \t?\r\n".into()))]
551 #[case("", Err(JsonSyntaxError::MissingSeparator('?')))]
552 #[case(" ", Err(JsonSyntaxError::MissingSeparator('?')))]
553 #[case("??", Err(JsonSyntaxError::MultipleSeparators('?')))]
554 #[case("? ?", Err(JsonSyntaxError::MultipleSeparators('?')))]
555 #[case("\x0C", Err(JsonSyntaxError::InvalidCharacter('\x0C')))]
556 #[case("\x0B", Err(JsonSyntaxError::InvalidCharacter('\x0B')))]
557 #[case("\u{A0}", Err(JsonSyntaxError::InvalidCharacter('\u{A0}')))]
558 #[case("\u{85}", Err(JsonSyntaxError::InvalidCharacter('\u{85}')))]
559 #[case("\u{1680}", Err(JsonSyntaxError::InvalidCharacter('\u{1680}')))]
560 #[case("\u{180E}", Err(JsonSyntaxError::InvalidCharacter('\u{180E}')))]
561 #[case("\u{2000}", Err(JsonSyntaxError::InvalidCharacter('\u{2000}')))]
562 #[case("\u{2001}", Err(JsonSyntaxError::InvalidCharacter('\u{2001}')))]
563 #[case("\u{2002}", Err(JsonSyntaxError::InvalidCharacter('\u{2002}')))]
564 #[case("\u{2003}", Err(JsonSyntaxError::InvalidCharacter('\u{2003}')))]
565 #[case("\u{2004}", Err(JsonSyntaxError::InvalidCharacter('\u{2004}')))]
566 #[case("\u{2005}", Err(JsonSyntaxError::InvalidCharacter('\u{2005}')))]
567 #[case("\u{2006}", Err(JsonSyntaxError::InvalidCharacter('\u{2006}')))]
568 #[case("\u{2007}", Err(JsonSyntaxError::InvalidCharacter('\u{2007}')))]
569 #[case("\u{2008}", Err(JsonSyntaxError::InvalidCharacter('\u{2008}')))]
570 #[case("\u{2009}", Err(JsonSyntaxError::InvalidCharacter('\u{2009}')))]
571 #[case("\u{200A}", Err(JsonSyntaxError::InvalidCharacter('\u{200A}')))]
572 #[case("\u{200B}", Err(JsonSyntaxError::InvalidCharacter('\u{200B}')))]
573 #[case("\u{200C}", Err(JsonSyntaxError::InvalidCharacter('\u{200C}')))]
574 #[case("\u{200D}", Err(JsonSyntaxError::InvalidCharacter('\u{200D}')))]
575 #[case("\u{2028}", Err(JsonSyntaxError::InvalidCharacter('\u{2028}')))]
576 #[case("\u{2029}", Err(JsonSyntaxError::InvalidCharacter('\u{2029}')))]
577 #[case("\u{202F}", Err(JsonSyntaxError::InvalidCharacter('\u{202F}')))]
578 #[case("\u{205F}", Err(JsonSyntaxError::InvalidCharacter('\u{205F}')))]
579 #[case("\u{2060}", Err(JsonSyntaxError::InvalidCharacter('\u{2060}')))]
580 #[case("\u{3000}", Err(JsonSyntaxError::InvalidCharacter('\u{3000}')))]
581 #[case("\u{FEFF}", Err(JsonSyntaxError::InvalidCharacter('\u{FEFF}')))]
582 #[case("\x0C?", Err(JsonSyntaxError::InvalidCharacter('\x0C')))]
583 #[case("?\x0C", Err(JsonSyntaxError::InvalidCharacter('\x0C')))]
584 #[case("?\x0C?", Err(JsonSyntaxError::InvalidCharacter('\x0C')))]
585 #[case("??\x0C", Err(JsonSyntaxError::MultipleSeparators('?')))]
586 #[case(".", Err(JsonSyntaxError::InvalidCharacter('.')))]
587 #[case(".?", Err(JsonSyntaxError::InvalidCharacter('.')))]
588 #[case("?.", Err(JsonSyntaxError::InvalidCharacter('.')))]
589 #[case("?.?", Err(JsonSyntaxError::InvalidCharacter('.')))]
590 #[case("??.", Err(JsonSyntaxError::MultipleSeparators('?')))]
591 #[case("☃", Err(JsonSyntaxError::InvalidCharacter('☃')))]
592 #[case("☃?", Err(JsonSyntaxError::InvalidCharacter('☃')))]
593 #[case("?☃", Err(JsonSyntaxError::InvalidCharacter('☃')))]
594 #[case("?☃?", Err(JsonSyntaxError::InvalidCharacter('☃')))]
595 #[case("??☃", Err(JsonSyntaxError::MultipleSeparators('?')))]
596 fn test_validate_string_sep(
597 #[case] s: &str,
598 #[case] r: Result<CompactString, JsonSyntaxError>,
599 ) {
600 assert_eq!(validate_string(s, Some('?')), r);
601 }
602
603 #[rstest]
604 #[case("", Ok("".into()))]
605 #[case(" ", Ok(" ".into()))]
606 #[case(" ", Ok(" ".into()))]
607 #[case(" \t\r\n", Ok(" \t\r\n".into()))]
608 #[case("?", Err(JsonSyntaxError::InvalidCharacter('?')))]
609 #[case(" ?", Err(JsonSyntaxError::InvalidCharacter('?')))]
610 #[case("? ", Err(JsonSyntaxError::InvalidCharacter('?')))]
611 #[case(" ? ", Err(JsonSyntaxError::InvalidCharacter('?')))]
612 #[case("??", Err(JsonSyntaxError::InvalidCharacter('?')))]
613 #[case("\x0C", Err(JsonSyntaxError::InvalidCharacter('\x0C')))]
614 #[case("\x0B", Err(JsonSyntaxError::InvalidCharacter('\x0B')))]
615 #[case("\u{A0}", Err(JsonSyntaxError::InvalidCharacter('\u{A0}')))]
616 #[case("\u{85}", Err(JsonSyntaxError::InvalidCharacter('\u{85}')))]
617 #[case("\u{1680}", Err(JsonSyntaxError::InvalidCharacter('\u{1680}')))]
618 #[case("\u{180E}", Err(JsonSyntaxError::InvalidCharacter('\u{180E}')))]
619 #[case("\u{2000}", Err(JsonSyntaxError::InvalidCharacter('\u{2000}')))]
620 #[case("\u{2001}", Err(JsonSyntaxError::InvalidCharacter('\u{2001}')))]
621 #[case("\u{2002}", Err(JsonSyntaxError::InvalidCharacter('\u{2002}')))]
622 #[case("\u{2003}", Err(JsonSyntaxError::InvalidCharacter('\u{2003}')))]
623 #[case("\u{2004}", Err(JsonSyntaxError::InvalidCharacter('\u{2004}')))]
624 #[case("\u{2005}", Err(JsonSyntaxError::InvalidCharacter('\u{2005}')))]
625 #[case("\u{2006}", Err(JsonSyntaxError::InvalidCharacter('\u{2006}')))]
626 #[case("\u{2007}", Err(JsonSyntaxError::InvalidCharacter('\u{2007}')))]
627 #[case("\u{2008}", Err(JsonSyntaxError::InvalidCharacter('\u{2008}')))]
628 #[case("\u{2009}", Err(JsonSyntaxError::InvalidCharacter('\u{2009}')))]
629 #[case("\u{200A}", Err(JsonSyntaxError::InvalidCharacter('\u{200A}')))]
630 #[case("\u{200B}", Err(JsonSyntaxError::InvalidCharacter('\u{200B}')))]
631 #[case("\u{200C}", Err(JsonSyntaxError::InvalidCharacter('\u{200C}')))]
632 #[case("\u{200D}", Err(JsonSyntaxError::InvalidCharacter('\u{200D}')))]
633 #[case("\u{2028}", Err(JsonSyntaxError::InvalidCharacter('\u{2028}')))]
634 #[case("\u{2029}", Err(JsonSyntaxError::InvalidCharacter('\u{2029}')))]
635 #[case("\u{202F}", Err(JsonSyntaxError::InvalidCharacter('\u{202F}')))]
636 #[case("\u{205F}", Err(JsonSyntaxError::InvalidCharacter('\u{205F}')))]
637 #[case("\u{2060}", Err(JsonSyntaxError::InvalidCharacter('\u{2060}')))]
638 #[case("\u{3000}", Err(JsonSyntaxError::InvalidCharacter('\u{3000}')))]
639 #[case("\u{FEFF}", Err(JsonSyntaxError::InvalidCharacter('\u{FEFF}')))]
640 #[case("\x0C ", Err(JsonSyntaxError::InvalidCharacter('\x0C')))]
641 #[case(" \x0C", Err(JsonSyntaxError::InvalidCharacter('\x0C')))]
642 #[case(" \x0C ", Err(JsonSyntaxError::InvalidCharacter('\x0C')))]
643 #[case(".", Err(JsonSyntaxError::InvalidCharacter('.')))]
644 #[case(". ", Err(JsonSyntaxError::InvalidCharacter('.')))]
645 #[case(" .", Err(JsonSyntaxError::InvalidCharacter('.')))]
646 #[case(" . ", Err(JsonSyntaxError::InvalidCharacter('.')))]
647 #[case("☃", Err(JsonSyntaxError::InvalidCharacter('☃')))]
648 #[case("☃ ", Err(JsonSyntaxError::InvalidCharacter('☃')))]
649 #[case(" ☃", Err(JsonSyntaxError::InvalidCharacter('☃')))]
650 #[case(" ☃ ", Err(JsonSyntaxError::InvalidCharacter('☃')))]
651 fn test_validate_string_no_sep(
652 #[case] s: &str,
653 #[case] r: Result<CompactString, JsonSyntaxError>,
654 ) {
655 assert_eq!(validate_string(s, None), r);
656 }
657
658 #[test]
659 fn test_format_default() {
660 let value = json!({
661 "colors": ["red", "blue", "taupe"],
662 "sub": {
663 "name": "Foo",
664 "on": true,
665 "size": 17
666 }
667 });
668 let s = JsonFormat::new().format_to_string(&value).unwrap();
669 assert_eq!(
670 s,
671 r#"{"colors":["red","blue","taupe"],"sub":{"name":"Foo","on":true,"size":17}}"#
672 );
673 }
674
675 #[test]
676 fn test_format_pretty() {
677 let value = json!({
678 "colors": ["red", "blue", "taupe"],
679 "sub": {
680 "name": "Foo",
681 "on": true,
682 "size": 17
683 }
684 });
685 let s = JsonFormat::pretty().format_to_string(&value).unwrap();
686 assert_eq!(
687 s,
688 indoc! {r#"{
689 "colors": [
690 "red",
691 "blue",
692 "taupe"
693 ],
694 "sub": {
695 "name": "Foo",
696 "on": true,
697 "size": 17
698 }
699 }"#}
700 );
701 }
702
703 #[test]
704 fn test_format_default_is_new() {
705 let value = json!({
706 "colors": ["red", "blue", "taupe"],
707 "sub": {
708 "name": "Foo",
709 "on": true,
710 "size": 17
711 }
712 });
713 assert_eq!(
714 JsonFormat::new().format_to_string(&value).unwrap(),
715 JsonFormat::default().format_to_string(&value).unwrap(),
716 );
717 }
718
719 #[test]
720 fn test_format_default_matches_serde_json() {
721 let value = json!({
722 "colors": ["red", "blue", "taupe"],
723 "sub": {
724 "name": "Foo",
725 "on": true,
726 "size": 17
727 }
728 });
729 assert_eq!(
730 JsonFormat::new().format_to_string(&value).unwrap(),
731 serde_json::to_string(&value).unwrap(),
732 );
733 }
734
735 #[test]
736 fn test_format_pretty_matches_serde_json() {
737 let value = json!({
738 "colors": ["red", "blue", "taupe"],
739 "sub": {
740 "name": "Foo",
741 "on": true,
742 "size": 17
743 }
744 });
745 assert_eq!(
746 JsonFormat::pretty().format_to_string(&value).unwrap(),
747 serde_json::to_string_pretty(&value).unwrap(),
748 );
749 }
750
751 #[test]
752 fn test_format_pretty_complicated() {
753 let value = json!({
754 "colors": [
755 "red",
756 "blue",
757 "taupe"
758 ],
759 "sampler": {
760 "empty_list": [],
761 "empty_object": {},
762 "nested": {
763 "list": [
764 1,
765 {
766 "strange": "charmed",
767 "truth": "beauty",
768 "up": "down"
769 },
770 3
771 ],
772 },
773 "null": null,
774 "singleton_list": [
775 42
776 ],
777 "singleton_object": {
778 "key": "value"
779 }
780 },
781 "sub": {
782 "name": "Foo",
783 "size": 17,
784 "on": true
785 }
786 });
787 let s = JsonFormat::pretty().format_to_string(&value).unwrap();
788 assert_eq!(
789 s,
790 indoc! {r#"{
791 "colors": [
792 "red",
793 "blue",
794 "taupe"
795 ],
796 "sampler": {
797 "empty_list": [],
798 "empty_object": {},
799 "nested": {
800 "list": [
801 1,
802 {
803 "strange": "charmed",
804 "truth": "beauty",
805 "up": "down"
806 },
807 3
808 ]
809 },
810 "null": null,
811 "singleton_list": [
812 42
813 ],
814 "singleton_object": {
815 "key": "value"
816 }
817 },
818 "sub": {
819 "name": "Foo",
820 "on": true,
821 "size": 17
822 }
823 }"#}
824 );
825 }
826
827 #[test]
828 fn test_format_pretty_complicated_indent_4() {
829 let value = json!({
830 "colors": [
831 "red",
832 "blue",
833 "taupe"
834 ],
835 "sampler": {
836 "empty_list": [],
837 "empty_object": {},
838 "nested": {
839 "list": [
840 1,
841 {
842 "strange": "charmed",
843 "truth": "beauty",
844 "up": "down"
845 },
846 3
847 ],
848 },
849 "null": null,
850 "singleton_list": [
851 42
852 ],
853 "singleton_object": {
854 "key": "value"
855 }
856 },
857 "sub": {
858 "name": "Foo",
859 "size": 17,
860 "on": true
861 }
862 });
863 let s = JsonFormat::pretty()
864 .indent_width(Some(4))
865 .format_to_string(&value)
866 .unwrap();
867 assert_eq!(
868 s,
869 indoc! {r#"{
870 "colors": [
871 "red",
872 "blue",
873 "taupe"
874 ],
875 "sampler": {
876 "empty_list": [],
877 "empty_object": {},
878 "nested": {
879 "list": [
880 1,
881 {
882 "strange": "charmed",
883 "truth": "beauty",
884 "up": "down"
885 },
886 3
887 ]
888 },
889 "null": null,
890 "singleton_list": [
891 42
892 ],
893 "singleton_object": {
894 "key": "value"
895 }
896 },
897 "sub": {
898 "name": "Foo",
899 "on": true,
900 "size": 17
901 }
902 }"#}
903 );
904 }
905
906 #[test]
907 fn test_format_pretty_empty_indent() {
908 let value = json!({
909 "nested": {
910 "list": [
911 1,
912 {
913 "strange": "charmed",
914 "truth": "beauty",
915 "up": "down"
916 },
917 3
918 ]
919 }
920 });
921 let s = JsonFormat::pretty()
922 .indent(Some(""))
923 .unwrap()
924 .format_to_string(&value)
925 .unwrap();
926 assert_eq!(
927 s,
928 indoc! {r#"{
929 "nested": {
930 "list": [
931 1,
932 {
933 "strange": "charmed",
934 "truth": "beauty",
935 "up": "down"
936 },
937 3
938 ]
939 }
940 }"#}
941 );
942 }
943
944 #[test]
945 fn test_format_pretty_zero_indent_width() {
946 let value = json!({
947 "nested": {
948 "list": [
949 1,
950 {
951 "strange": "charmed",
952 "truth": "beauty",
953 "up": "down"
954 },
955 3
956 ]
957 }
958 });
959 let s = JsonFormat::pretty()
960 .indent_width(Some(0))
961 .format_to_string(&value)
962 .unwrap();
963 assert_eq!(
964 s,
965 indoc! {r#"{
966 "nested": {
967 "list": [
968 1,
969 {
970 "strange": "charmed",
971 "truth": "beauty",
972 "up": "down"
973 },
974 3
975 ]
976 }
977 }"#}
978 );
979 }
980
981 #[test]
982 fn test_format_pretty_tab_indent() {
983 let value = json!({
984 "nested": {
985 "list": [
986 1,
987 {
988 "strange": "charmed",
989 "truth": "beauty",
990 "up": "down"
991 },
992 3
993 ]
994 }
995 });
996 let s = JsonFormat::pretty()
997 .indent(Some("\t"))
998 .unwrap()
999 .format_to_string(&value)
1000 .unwrap();
1001 assert_eq!(
1002 s,
1003 indoc! {"{
1004 \t\"nested\": {
1005 \t\t\"list\": [
1006 \t\t\t1,
1007 \t\t\t{
1008 \t\t\t\t\"strange\": \"charmed\",
1009 \t\t\t\t\"truth\": \"beauty\",
1010 \t\t\t\t\"up\": \"down\"
1011 \t\t\t},
1012 \t\t\t3
1013 \t\t]
1014 \t}
1015 }"}
1016 );
1017 }
1018
1019 #[test]
1020 fn test_format_spaced_separators() {
1021 let value = json!({
1022 "colors": ["red", "blue", "taupe"],
1023 "sub": {
1024 "name": "Foo",
1025 "on": true,
1026 "size": 17
1027 }
1028 });
1029 let s = JsonFormat::new()
1030 .comma(", ")
1031 .unwrap()
1032 .colon(": ")
1033 .unwrap()
1034 .format_to_string(&value)
1035 .unwrap();
1036 assert_eq!(
1037 s,
1038 r#"{"colors": ["red", "blue", "taupe"], "sub": {"name": "Foo", "on": true, "size": 17}}"#
1039 );
1040 }
1041
1042 #[test]
1043 fn test_format_weird_separators() {
1044 let value = json!({
1045 "colors": ["red", "blue", "taupe"],
1046 "sub": {
1047 "name": "Foo",
1048 "on": true,
1049 "size": 17
1050 }
1051 });
1052 let s = JsonFormat::new()
1053 .comma("\n,")
1054 .unwrap()
1055 .colon("\t:\t")
1056 .unwrap()
1057 .format_to_string(&value)
1058 .unwrap();
1059 assert_eq!(
1060 s,
1061 "{\"colors\"\t:\t[\"red\"\n,\"blue\"\n,\"taupe\"]\n,\"sub\"\t:\t{\"name\"\t:\t\"Foo\"\n,\"on\"\t:\ttrue\n,\"size\"\t:\t17}}"
1062 );
1063 }
1064
1065 #[test]
1066 fn test_format_unicode() {
1067 let value = json!({
1068 "föö": "snow☃man",
1069 "\u{1F410}": "\u{1F600}",
1070 });
1071 let s = JsonFormat::new().format_to_string(&value).unwrap();
1072 assert_eq!(s, "{\"föö\":\"snow☃man\",\"\u{1F410}\":\"\u{1F600}\"}");
1073 }
1074
1075 #[test]
1076 fn test_format_unicode_in_ascii() {
1077 let value = json!({
1078 "föö": "snow☃man",
1079 "\u{1F410}": "\u{1F600}",
1080 });
1081 let s = JsonFormat::new()
1082 .ascii(true)
1083 .format_to_string(&value)
1084 .unwrap();
1085 assert_eq!(
1086 s,
1087 r#"{"f\u00f6\u00f6":"snow\u2603man","\ud83d\udc10":"\ud83d\ude00"}"#
1088 );
1089 }
1090
1091 #[test]
1092 fn test_format_top_level_array() {
1093 let value = json!(["apple", ["banana"], {"grape": "raisin"}]);
1094 let s = JsonFormat::new().format_to_string(&value).unwrap();
1095 assert_eq!(s, r#"["apple",["banana"],{"grape":"raisin"}]"#);
1096 }
1097
1098 #[test]
1099 fn test_format_top_level_array_pretty() {
1100 let value = json!(["apple", ["banana"], {"grape": "raisin"}]);
1101 let s = JsonFormat::pretty().format_to_string(&value).unwrap();
1102 assert_eq!(
1103 s,
1104 indoc! {r#"[
1105 "apple",
1106 [
1107 "banana"
1108 ],
1109 {
1110 "grape": "raisin"
1111 }
1112 ]"#}
1113 );
1114 }
1115
1116 #[test]
1117 fn test_format_top_level_int() {
1118 let s = JsonFormat::new().format_to_string(&42).unwrap();
1119 assert_eq!(s, "42");
1120 }
1121
1122 #[test]
1123 fn test_format_top_level_int_pretty() {
1124 let s = JsonFormat::pretty().format_to_string(&42).unwrap();
1125 assert_eq!(s, "42");
1126 }
1127
1128 #[test]
1129 fn test_format_top_level_float() {
1130 let s = JsonFormat::new().format_to_string(&6.022).unwrap();
1131 assert_eq!(s, "6.022");
1132 }
1133
1134 #[test]
1135 fn test_format_top_level_float_pretty() {
1136 let s = JsonFormat::pretty().format_to_string(&6.022).unwrap();
1137 assert_eq!(s, "6.022");
1138 }
1139
1140 #[test]
1141 fn test_format_top_level_string() {
1142 let s = JsonFormat::new().format_to_string("foo").unwrap();
1143 assert_eq!(s, r#""foo""#);
1144 }
1145
1146 #[test]
1147 fn test_format_top_level_string_pretty() {
1148 let s = JsonFormat::pretty().format_to_string("foo").unwrap();
1149 assert_eq!(s, r#""foo""#);
1150 }
1151
1152 #[test]
1153 fn test_format_top_level_bool() {
1154 let s = JsonFormat::new().format_to_string(&true).unwrap();
1155 assert_eq!(s, "true");
1156 }
1157
1158 #[test]
1159 fn test_format_top_level_bool_pretty() {
1160 let s = JsonFormat::pretty().format_to_string(&true).unwrap();
1161 assert_eq!(s, "true");
1162 }
1163
1164 #[test]
1165 fn test_format_top_level_null() {
1166 let value = json!(null);
1167 let s = JsonFormat::new().format_to_string(&value).unwrap();
1168 assert_eq!(s, "null");
1169 }
1170
1171 #[test]
1172 fn test_format_top_level_null_pretty() {
1173 let value = json!(null);
1174 let s = JsonFormat::pretty().format_to_string(&value).unwrap();
1175 assert_eq!(s, "null");
1176 }
1177
1178 #[test]
1179 fn test_display_invalid_character() {
1180 let e = JsonSyntaxError::InvalidCharacter('ö');
1181 assert_eq!(e.to_string(), "string contains unexpected character 'ö'");
1182 }
1183
1184 #[test]
1185 fn test_display_missing_separator() {
1186 let e = JsonSyntaxError::MissingSeparator('?');
1187 assert_eq!(e.to_string(), "no occurrence of '?' found in string");
1188 }
1189
1190 #[test]
1191 fn test_display_multiple_separators() {
1192 let e = JsonSyntaxError::MultipleSeparators('?');
1193 assert_eq!(e.to_string(), "multiple occurrences of '?' found in string");
1194 }
1195}