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