1pub mod formatters;
184pub mod impls;
185
186use crate::formatters::identity_formatter;
187pub use crud_pretty_struct_derive::*;
188use miette::Result;
190use owo_colors::OwoColorize;
191use pad::PadStr;
192use std::fmt::Write;
193
194pub type Formatter = dyn Fn(&dyn ToString, bool) -> Result<(String, bool)>;
195pub enum MetaValue<'a> {
196 String {
197 value: &'a dyn ToString,
198 formatter: Option<&'a Formatter>,
199 },
200 Pretty(&'a dyn PrettyPrint),
201 OptionString {
202 value: Option<&'a dyn ToString>,
203 formatter: Option<&'a Formatter>,
204 skip_none: bool,
205 },
206 OptionPretty {
207 value: Option<&'a dyn PrettyPrint>,
208 skip_none: bool,
209 },
210 VecString(Vec<&'a dyn ToString>),
211 VecPretty(Vec<&'a dyn PrettyPrint>),
212 OptionVecString {
213 value: Option<Vec<&'a dyn ToString>>,
214 skip_none: bool,
215 },
216 OptionVecPretty {
217 value: Option<Vec<&'a dyn PrettyPrint>>,
218 skip_none: bool,
219 },
220 Variant {
221 value: &'a dyn ToString,
222 formatter: Option<&'a Formatter>,
223 },
224}
225
226#[derive(PartialEq)]
227pub enum Color {
228 Black,
229 Blue,
230 Cyan,
231 Green,
232 Magenta,
233 Red,
234 White,
235 Yellow,
236}
237
238#[derive(PartialEq)]
239pub enum FieldPrefix<'a> {
240 Label {
241 label: &'a str,
242 label_color: Option<Color>,
243 },
244 Multiline,
245 None,
246}
247
248pub struct MetaField<'a> {
249 pub profiles: Vec<&'a str>,
250 pub field_prefix: FieldPrefix<'a>,
251 pub color: Option<Color>,
252 pub value: MetaValue<'a>,
253}
254
255pub struct Meta<'a> {
256 pub padding: usize,
257 pub separator: Option<&'a str>,
258 pub fields: Vec<MetaField<'a>>,
259}
260
261fn coloring(value: String, color: &Option<Color>) -> String {
262 match color {
263 Some(color) => match color {
264 Color::Red => value.red().bold().to_string(),
265 Color::Black => value.black().bold().to_string(),
266 Color::Blue => value.blue().bold().to_string(),
267 Color::Cyan => value.cyan().bold().to_string(),
268 Color::Green => value.green().bold().to_string(),
269 Color::Magenta => value.magenta().bold().to_string(),
270 Color::White => value.white().bold().to_string(),
271 Color::Yellow => value.yellow().bold().to_string(),
272 },
273 None => value.bright_white().bold().to_string(),
274 }
275}
276
277fn label_coloring(label: &str, colored: bool, color: &Option<Color>) -> String {
278 if colored {
279 match color {
280 Some(color) => match color {
281 Color::Red => label.red().to_string(),
282 Color::Black => label.black().to_string(),
283 Color::Blue => label.blue().to_string(),
284 Color::Cyan => label.cyan().to_string(),
285 Color::Green => label.green().to_string(),
286 Color::Magenta => label.magenta().to_string(),
287 Color::White => label.white().to_string(),
288 Color::Yellow => label.yellow().to_string(),
289 },
290 None => label.to_string(),
291 }
292 } else {
293 label.to_string()
294 }
295}
296
297pub trait PrettyPrint {
298 fn meta(&self) -> Meta;
299 fn pretty(&self, colored: bool, prefix: Option<String>, profile: Option<&str>) -> Result<String> {
300 let Meta {
301 fields,
302 separator,
303 padding,
304 } = self.meta();
305
306 let separator = separator.unwrap_or("= ");
307 let prefix_ = if let Some(prefix) = &prefix {
308 if colored {
309 prefix.truecolor(80, 80, 80).to_string()
310 } else {
311 prefix.to_owned()
312 }
313 } else {
314 "".into()
315 };
316 let prefix = prefix.unwrap_or_default();
317 fields
318 .into_iter()
319 .filter(|MetaField { profiles, .. }| {
320 if let Some(profile) = &profile {
321 profiles.contains(profile)
322 } else {
323 true
324 }
325 })
326 .map(
327 |MetaField {
328 field_prefix,
329 color,
330 value,
331 ..
332 }| {
333 match field_prefix {
334 FieldPrefix::None | FieldPrefix::Multiline => {
335 match value {
336 MetaValue::String { value, formatter } => {
337 let formatter = formatter.unwrap_or(&identity_formatter);
338 let (value, colored_value) = formatter(value, colored)?;
339 Ok(format!(
340 "{prefix_}{}\n",
341 if colored && !colored_value {
342 coloring(value, &color)
343 } else {
344 value
345 }
346 ))
347 }
348 MetaValue::Variant { value, formatter } => {
349 let formatter = formatter.unwrap_or(&identity_formatter);
350 let (value, colored_value) = formatter(value, colored)?;
351 Ok(format!(
352 "{prefix_}{}\n",
353 if colored && !colored_value {
354 coloring(value, &color)
355 } else {
356 value
357 }
358 ))
359 }
360 MetaValue::Pretty(value) => Ok(format!(
361 "{prefix_}{}",
362 value.pretty(colored, Some(prefix.clone()), profile)?
363 )),
364 MetaValue::OptionString {
365 value,
366 formatter,
367 skip_none,
368 } => Ok(if value.is_none() && skip_none {
369 String::new()
370 } else {
371 match value {
372 Some(value) => {
373 let formatter = formatter.unwrap_or(&identity_formatter);
374 let (value, colored_value) = formatter(value, colored)?;
375 format!(
376 "{prefix_}{}\n",
377 if colored && !colored_value {
378 coloring(value, &color)
379 } else {
380 value
381 }
382 )
383 }
384 None => {
385 format!(
386 "{prefix_}{}\n",
387 if colored {
388 "null".magenta().to_string() } else {
390 "null".to_string()
391 }
392 )
393 }
394 }
395 }),
396 MetaValue::OptionPretty { value, skip_none } => Ok(match value {
397 Some(value) => format!(
398 "{prefix_}{}",
399 value.pretty(colored, Some(prefix.clone() + "| "), profile)?
400 ),
401 None => {
402 if skip_none {
403 String::new()
404 } else {
405 format!(
406 "{prefix_}{}\n",
407 if colored {
408 "null".magenta().to_string() } else {
410 "null".to_string()
411 }
412 )
413 }
414 }
415 }),
416 MetaValue::VecString(vec) => Ok(format!(
417 "{prefix_}{}",
418 vec.iter().fold(String::new(), |mut output, i| {
419 let _ = writeln!(
420 output,
421 " - {}",
422 if colored {
423 coloring(i.to_string(), &color)
424 } else {
425 i.to_string()
426 }
427 );
428 output
429 })
430 )),
431 MetaValue::VecPretty(vec) => Ok(format!("{prefix_}{}", {
432 vec
433 .iter()
434 .map(|value| {
435 Ok(
436 value
437 .pretty(colored, Some(prefix.clone() + " "), profile)?
438 .replacen(" ", " - ", 1),
439 )
440 })
441 .collect::<Result<String>>()?
442 })),
443 MetaValue::OptionVecString { value, skip_none } => {
444 Ok(if value.is_none() && skip_none {
445 String::new()
446 } else {
447 format!(
448 "{prefix_}{}",
449 if colored {
450 match value {
451 Some(vec) => {
452 "\n".to_string()
453 + &vec.iter().fold(String::new(), |mut output, i| {
454 let _ = writeln!(output, " - {}", coloring(i.to_string(), &color));
455 output
456 })
457 }
458 None => " null\n".magenta().to_string(), }
460 } else {
461 match value {
462 Some(vec) => {
463 "\n".to_string()
464 + &vec.iter().fold(String::new(), |mut output, i| {
465 let _ = writeln!(output, " - {}", i.to_string());
466 output
467 })
468 }
469 None => " null\n".to_string(),
470 }
471 }
472 )
473 })
474 }
475 MetaValue::OptionVecPretty { value, skip_none } => {
476 Ok(if value.is_none() && skip_none {
477 String::new()
478 } else {
479 format!(
480 "{prefix_}{}",
481 match value {
482 Some(vec) =>
483 "\n".to_string()
484 + &vec
485 .iter()
486 .map(|i| Ok(
487 i.pretty(colored, Some(prefix.clone() + " "), profile)?
488 .replacen(" ", " - ", 1)
489 ))
490 .collect::<Result<String>>()?,
491 None =>
492 if colored {
493 " null\n".magenta().to_string() } else {
495 " null\n".to_string()
496 },
497 }
498 )
499 })
500 }
501 }
502 }
503 FieldPrefix::Label { label, label_color } => {
504 let label = label_coloring(label, colored, &label_color);
505 match value {
506 MetaValue::String { value, formatter } => {
507 let formatter = formatter.unwrap_or(&identity_formatter);
508 let (value, colored_value) = formatter(value, colored)?;
509 Ok(format!(
510 "{prefix_}{}{separator}{}\n",
511 label.pad_to_width(padding),
512 if colored && !colored_value {
513 coloring(value, &color)
514 } else {
515 value
516 }
517 ))
518 }
519 MetaValue::Variant { value, formatter } => {
520 let formatter = formatter.unwrap_or(&identity_formatter);
521 let (value, colored_value) = formatter(value, colored)?;
522 Ok(format!(
523 "{prefix_}{}{separator}{}\n",
524 label.pad_to_width(padding),
525 if colored && !colored_value {
526 coloring(value, &color)
527 } else {
528 value
529 }
530 ))
531 }
532 MetaValue::Pretty(value) => match value.meta().fields.first().unwrap().field_prefix {
533 FieldPrefix::None => Ok(format!(
534 "{prefix_}{}{separator}{}",
535 label.pad_to_width(padding),
536 value.pretty(colored, Some(prefix.clone()), profile)?
537 )),
538 FieldPrefix::Multiline => Ok(format!(
539 "{prefix_}{label} -->\n{}",
540 value
541 .pretty(colored, Some(prefix.clone() + "| "), profile)?
542 .replacen("| ", "", 1)
543 )),
544 _ => Ok(format!(
545 "{prefix_}{label} -->\n{}",
546 value.pretty(colored, Some(prefix.clone() + "| "), profile)?
547 )),
548 },
549 MetaValue::OptionString {
550 value,
551 formatter,
552 skip_none,
553 } => Ok(if value.is_none() && skip_none {
554 String::new()
555 } else {
556 match value {
557 Some(value) => {
558 let formatter = formatter.unwrap_or(&identity_formatter);
559 let (value, colored_value) = formatter(value, colored)?;
560 format!(
561 "{prefix_}{}{separator}{}\n",
562 label.pad_to_width(padding),
563 if colored && !colored_value {
564 coloring(value, &color)
565 } else {
566 value
567 }
568 )
569 }
570 None => {
571 format!(
572 "{prefix_}{}{separator}{}\n",
573 label.pad_to_width(padding),
574 if colored {
575 "null".magenta().to_string() } else {
577 "null".to_string()
578 }
579 )
580 }
581 }
582 }),
583 MetaValue::OptionPretty { value, skip_none } => Ok(match value {
584 Some(value) => {
585 if value.meta().fields.first().unwrap().field_prefix == FieldPrefix::None {
586 format!(
587 "{prefix_}{}{separator}{}",
588 label.pad_to_width(padding),
589 value.pretty(colored, Some(prefix.clone()), profile)?
590 )
591 } else {
592 format!(
593 "{prefix_}{label} -->\n{}",
594 value.pretty(colored, Some(prefix.clone() + "| "), profile)?
595 )
596 }
597 }
598 None => {
599 if skip_none {
600 String::new()
601 } else {
602 format!(
603 "{prefix_}{}{separator}{}\n",
604 label.pad_to_width(padding),
605 if colored {
606 "null".magenta().to_string() } else {
608 "null".to_string()
609 }
610 )
611 }
612 }
613 }),
614 MetaValue::VecString(vec) => Ok(format!(
615 "{prefix_}{label} :\n{}",
616 vec.iter().fold(String::new(), |mut output, i| {
617 let _ = writeln!(
618 output,
619 " - {}",
620 if colored {
621 coloring(i.to_string(), &color)
622 } else {
623 i.to_string()
624 }
625 );
626 output
627 })
628 )),
629 MetaValue::VecPretty(vec) => Ok(format!("{prefix_}{label} :\n{}", {
630 vec
631 .iter()
632 .map(|value| {
633 Ok(
634 value
635 .pretty(colored, Some(prefix.clone() + " "), profile)?
636 .replacen(" ", " - ", 1),
637 )
638 })
639 .collect::<Result<String>>()?
640 })),
641 MetaValue::OptionVecString { value, skip_none } => {
642 Ok(if value.is_none() && skip_none {
643 String::new()
644 } else {
645 format!(
646 "{prefix_}{label} :{}",
647 if colored {
648 match value {
649 Some(vec) => {
650 "\n".to_string()
651 + &vec.iter().fold(String::new(), |mut output, i| {
652 let _ = writeln!(output, " - {}", coloring(i.to_string(), &color));
653 output
654 })
655 }
656 None => " null\n".magenta().to_string(), }
658 } else {
659 match value {
660 Some(vec) => {
661 "\n".to_string()
662 + &vec.iter().fold(String::new(), |mut output, i| {
663 let _ = writeln!(output, " - {}", i.to_string());
664 output
665 })
666 }
667 None => " null\n".to_string(),
668 }
669 }
670 )
671 })
672 }
673 MetaValue::OptionVecPretty { value, skip_none } => {
674 Ok(if value.is_none() && skip_none {
675 String::new()
676 } else {
677 format!(
678 "{prefix_}{label} :{}",
679 match value {
680 Some(vec) =>
681 "\n".to_string()
682 + &vec
683 .iter()
684 .map(|i| Ok(
685 i.pretty(colored, Some(prefix.clone() + " "), profile)?
686 .replacen(" ", " - ", 1)
687 ))
688 .collect::<Result<String>>()?,
689 None =>
690 if colored {
691 " null\n".magenta().to_string() } else {
693 " null\n".to_string()
694 },
695 }
696 )
697 })
698 }
699 }
700 }
701 }
702 },
703 )
704 .collect::<Result<String>>()
705 }
706}
707
708#[cfg(test)]
709mod tests {
710 use crate::{coloring, Color, FieldPrefix, Meta, MetaField, MetaValue, PrettyPrint};
711
712 #[test]
713 fn empty_struct() {
714 struct T1 {}
715 impl PrettyPrint for T1 {
716 fn meta(&self) -> Meta {
717 Meta {
718 padding: 1,
719 separator: None,
720 fields: vec![],
721 }
722 }
723 }
724
725 let s = T1 {};
726 assert_eq!(s.pretty(false, None, None).unwrap(), "".to_string());
727 }
728
729 #[test]
730 fn simple_struct() {
731 struct T1 {
732 a: u32,
733 bb: String,
734 cccc: bool,
735 }
736
737 impl PrettyPrint for T1 {
738 fn meta(&self) -> Meta {
739 Meta {
740 padding: 5,
741 separator: None,
742 fields: vec![
743 MetaField {
744 profiles: vec![],
745 field_prefix: FieldPrefix::Label {
746 label: "a",
747 label_color: None,
748 },
749 color: None,
750 value: MetaValue::String {
751 value: &self.a,
752 formatter: None,
753 },
754 },
755 MetaField {
756 profiles: vec![],
757 field_prefix: FieldPrefix::Label {
758 label: "bb",
759 label_color: None,
760 },
761 color: None,
762 value: MetaValue::String {
763 value: &self.bb,
764 formatter: None,
765 },
766 },
767 MetaField {
768 profiles: vec![],
769 field_prefix: FieldPrefix::Label {
770 label: "cccc",
771 label_color: None,
772 },
773 color: None,
774 value: MetaValue::String {
775 value: &self.cccc,
776 formatter: None,
777 },
778 },
779 ],
780 }
781 }
782 }
783
784 let s = T1 {
785 a: 5,
786 bb: "string".to_string(),
787 cccc: false,
788 };
789 assert_eq!(
791 s.pretty(false, None, None).unwrap(),
792 "a = 5\nbb = string\ncccc = false\n".to_string()
793 );
794 }
795
796 #[test]
797 fn struct_with_profile() {
798 struct T1 {
799 a: u32,
800 bb: String,
801 cccc: bool,
802 }
803
804 impl PrettyPrint for T1 {
805 fn meta(&self) -> Meta {
806 Meta {
807 padding: 5,
808 separator: None,
809 fields: vec![
810 MetaField {
811 profiles: vec!["a"],
812 field_prefix: FieldPrefix::Label {
813 label: "a",
814 label_color: None,
815 },
816 color: None,
817 value: MetaValue::String {
818 value: &self.a,
819 formatter: None,
820 },
821 },
822 MetaField {
823 profiles: vec!["b"],
824 field_prefix: FieldPrefix::Label {
825 label: "bb",
826 label_color: None,
827 },
828 color: None,
829 value: MetaValue::String {
830 value: &self.bb,
831 formatter: None,
832 },
833 },
834 MetaField {
835 profiles: vec!["a", "b"],
836 field_prefix: FieldPrefix::Label {
837 label: "cccc",
838 label_color: None,
839 },
840 color: None,
841 value: MetaValue::String {
842 value: &self.cccc,
843 formatter: None,
844 },
845 },
846 ],
847 }
848 }
849 }
850
851 let s = T1 {
852 a: 5,
853 bb: "string".to_string(),
854 cccc: false,
855 };
856 assert_eq!(
857 s.pretty(false, None, None).unwrap(),
858 "a = 5\nbb = string\ncccc = false\n".to_string()
859 );
860 assert_eq!(
861 s.pretty(false, None, Some("a")).unwrap(),
862 "a = 5\ncccc = false\n".to_string()
863 );
864 assert_eq!(
865 s.pretty(false, None, Some("b")).unwrap(),
866 "bb = string\ncccc = false\n".to_string()
867 );
868 assert_eq!(s.pretty(false, None, Some("c")).unwrap(), "".to_string());
869 }
870
871 #[test]
872 fn nested_struct() {
873 struct T1 {
874 a: u32,
875 bb: String,
876 cccc: bool,
877 }
878 impl PrettyPrint for T1 {
879 fn meta(&self) -> Meta {
880 Meta {
881 padding: 5,
882 separator: None,
883 fields: vec![
884 MetaField {
885 profiles: vec![],
886 field_prefix: FieldPrefix::Label {
887 label: "a",
888 label_color: None,
889 },
890 color: None,
891 value: MetaValue::String {
892 value: &self.a,
893 formatter: None,
894 },
895 },
896 MetaField {
897 profiles: vec![],
898 field_prefix: FieldPrefix::Label {
899 label: "bb",
900 label_color: None,
901 },
902 color: None,
903 value: MetaValue::String {
904 value: &self.bb,
905 formatter: None,
906 },
907 },
908 MetaField {
909 profiles: vec![],
910 field_prefix: FieldPrefix::Label {
911 label: "cccc",
912 label_color: None,
913 },
914 color: None,
915 value: MetaValue::String {
916 value: &self.cccc,
917 formatter: None,
918 },
919 },
920 ],
921 }
922 }
923 }
924 struct T2 {
925 a: u32,
926 n: T1,
927 }
928 impl PrettyPrint for T2 {
929 fn meta(&self) -> Meta {
930 Meta {
931 padding: 2,
932 separator: None,
933 fields: vec![
934 MetaField {
935 profiles: vec![],
936 field_prefix: FieldPrefix::Label {
937 label: "a",
938 label_color: None,
939 },
940 color: None,
941 value: MetaValue::String {
942 value: &self.a,
943 formatter: None,
944 },
945 },
946 MetaField {
947 profiles: vec![],
948 field_prefix: FieldPrefix::Label {
949 label: "n",
950 label_color: None,
951 },
952 color: None,
953 value: MetaValue::Pretty(&self.n),
954 },
955 ],
956 }
957 }
958 }
959 let s = T2 {
960 a: 5,
961 n: T1 {
962 a: 5,
963 bb: "string".to_string(),
964 cccc: false,
965 },
966 };
967 assert_eq!(
969 s.pretty(false, None, None).unwrap(),
970 "a = 5\nn -->\n| a = 5\n| bb = string\n| cccc = false\n".to_string()
971 );
972 }
973
974 #[test]
975 fn simple_struct_custom_separator() {
976 struct T1 {
977 a: u32,
978 bb: String,
979 cccc: bool,
980 }
981 impl PrettyPrint for T1 {
982 fn meta(&self) -> Meta {
983 Meta {
984 padding: 5,
985 separator: Some("-> "),
986 fields: vec![
987 MetaField {
988 profiles: vec![],
989 field_prefix: FieldPrefix::Label {
990 label: "a",
991 label_color: None,
992 },
993 color: None,
994 value: MetaValue::String {
995 value: &self.a,
996 formatter: None,
997 },
998 },
999 MetaField {
1000 profiles: vec![],
1001 field_prefix: FieldPrefix::Label {
1002 label: "bb",
1003 label_color: None,
1004 },
1005 color: None,
1006 value: MetaValue::String {
1007 value: &self.bb,
1008 formatter: None,
1009 },
1010 },
1011 MetaField {
1012 profiles: vec![],
1013 field_prefix: FieldPrefix::Label {
1014 label: "cccc",
1015 label_color: None,
1016 },
1017 color: None,
1018 value: MetaValue::String {
1019 value: &self.cccc,
1020 formatter: None,
1021 },
1022 },
1023 ],
1024 }
1025 }
1026 }
1027
1028 let s = T1 {
1029 a: 5,
1030 bb: "string".to_string(),
1031 cccc: false,
1032 };
1033 assert_eq!(
1035 s.pretty(false, None, None).unwrap(),
1036 "a -> 5\nbb -> string\ncccc -> false\n".to_string()
1037 );
1038 }
1039
1040 #[test]
1041 fn simple_struct_colored() {
1042 struct T1 {
1043 a: u32,
1044 bb: String,
1045 cccc: bool,
1046 }
1047 impl PrettyPrint for T1 {
1048 fn meta(&self) -> Meta {
1049 Meta {
1050 padding: 5,
1051 separator: None,
1052 fields: vec![
1053 MetaField {
1054 profiles: vec![],
1055 field_prefix: FieldPrefix::Label {
1056 label: "a",
1057 label_color: None,
1058 },
1059 color: None,
1060 value: MetaValue::String {
1061 value: &self.a,
1062 formatter: None,
1063 },
1064 },
1065 MetaField {
1066 profiles: vec![],
1067 field_prefix: FieldPrefix::Label {
1068 label: "bb",
1069 label_color: None,
1070 },
1071 color: None,
1072 value: MetaValue::String {
1073 value: &self.bb,
1074 formatter: None,
1075 },
1076 },
1077 MetaField {
1078 profiles: vec![],
1079 field_prefix: FieldPrefix::Label {
1080 label: "cccc",
1081 label_color: None,
1082 },
1083 color: None,
1084 value: MetaValue::String {
1085 value: &self.cccc,
1086 formatter: None,
1087 },
1088 },
1089 ],
1090 }
1091 }
1092 }
1093
1094 let s = T1 {
1095 a: 5,
1096 bb: "string".to_string(),
1097 cccc: false,
1098 };
1099 assert_eq!(
1101 s.pretty(true,None, None).unwrap(),
1102 "a = \u{1b}[1m\u{1b}[97m5\u{1b}[39m\u{1b}[0m\nbb = \u{1b}[1m\u{1b}[97mstring\u{1b}[39m\u{1b}[0m\ncccc = \u{1b}[1m\u{1b}[97mfalse\u{1b}[39m\u{1b}[0m\n".to_string()
1103 );
1104 }
1105
1106 #[test]
1107 fn simple_struct_custom_color() {
1108 struct T1 {
1109 a: u32,
1110 bb: String,
1111 cccc: bool,
1112 }
1113 impl PrettyPrint for T1 {
1114 fn meta(&self) -> Meta {
1115 Meta {
1116 padding: 5,
1117 separator: None,
1118 fields: vec![
1119 MetaField {
1120 profiles: vec![],
1121 field_prefix: FieldPrefix::Label {
1122 label: "a",
1123 label_color: None,
1124 },
1125 color: Some(Color::Green),
1126 value: MetaValue::String {
1127 value: &self.a,
1128 formatter: None,
1129 },
1130 },
1131 MetaField {
1132 profiles: vec![],
1133 field_prefix: FieldPrefix::Label {
1134 label: "bb",
1135 label_color: None,
1136 },
1137 color: Some(Color::Yellow),
1138 value: MetaValue::String {
1139 value: &self.bb,
1140 formatter: None,
1141 },
1142 },
1143 MetaField {
1144 profiles: vec![],
1145 field_prefix: FieldPrefix::Label {
1146 label: "cccc",
1147 label_color: None,
1148 },
1149 color: Some(Color::Magenta),
1150 value: MetaValue::String {
1151 value: &self.cccc,
1152 formatter: None,
1153 },
1154 },
1155 ],
1156 }
1157 }
1158 }
1159
1160 let s = T1 {
1161 a: 5,
1162 bb: "string".to_string(),
1163 cccc: false,
1164 };
1165 assert_eq!(
1167 s.pretty(true,None, None).unwrap(),
1168 "a = \u{1b}[1m\u{1b}[32m5\u{1b}[39m\u{1b}[0m\nbb = \u{1b}[1m\u{1b}[33mstring\u{1b}[39m\u{1b}[0m\ncccc = \u{1b}[1m\u{1b}[35mfalse\u{1b}[39m\u{1b}[0m\n".to_string()
1169 );
1170 }
1171
1172 #[test]
1173 fn simple_struct_custom_label_color() {
1174 struct T1 {
1175 a: u32,
1176 bb: String,
1177 cccc: bool,
1178 }
1179 impl PrettyPrint for T1 {
1180 fn meta(&self) -> Meta {
1181 Meta {
1182 padding: 5,
1183 separator: None,
1184 fields: vec![
1185 MetaField {
1186 profiles: vec![],
1187 field_prefix: FieldPrefix::Label {
1188 label: "a",
1189 label_color: Some(Color::Green),
1190 },
1191 color: None,
1192 value: MetaValue::String {
1193 value: &self.a,
1194 formatter: None,
1195 },
1196 },
1197 MetaField {
1198 profiles: vec![],
1199 field_prefix: FieldPrefix::Label {
1200 label: "bb",
1201 label_color: Some(Color::Yellow),
1202 },
1203 color: None,
1204 value: MetaValue::String {
1205 value: &self.bb,
1206 formatter: None,
1207 },
1208 },
1209 MetaField {
1210 profiles: vec![],
1211 field_prefix: FieldPrefix::Label {
1212 label: "cccc",
1213 label_color: Some(Color::Magenta),
1214 },
1215 color: None,
1216 value: MetaValue::String {
1217 value: &self.cccc,
1218 formatter: None,
1219 },
1220 },
1221 ],
1222 }
1223 }
1224 }
1225
1226 let s = T1 {
1227 a: 5,
1228 bb: "string".to_string(),
1229 cccc: false,
1230 };
1231 assert_eq!(
1233 s.pretty(true,None, None).unwrap(),
1234 "\u{1b}[32ma\u{1b}[39m= \u{1b}[1m\u{1b}[97m5\u{1b}[39m\u{1b}[0m\n\u{1b}[33mbb\u{1b}[39m= \u{1b}[1m\u{1b}[97mstring\u{1b}[39m\u{1b}[0m\n\u{1b}[35mcccc\u{1b}[39m= \u{1b}[1m\u{1b}[97mfalse\u{1b}[39m\u{1b}[0m\n".to_string()
1235 );
1236 }
1237
1238 #[test]
1239 fn option_struct() {
1240 struct T1 {
1241 a: Option<u32>,
1242 bb: Option<String>,
1243 }
1244
1245 impl PrettyPrint for T1 {
1246 fn meta(&self) -> Meta {
1247 Meta {
1248 padding: 5,
1249 separator: None,
1250 fields: vec![
1251 MetaField {
1252 profiles: vec![],
1253 field_prefix: FieldPrefix::Label {
1254 label: "a",
1255 label_color: None,
1256 },
1257 color: None,
1258 value: MetaValue::OptionString {
1259 value: self.a.as_ref().map(|x| x as &dyn ToString),
1260 formatter: None,
1261 skip_none: false,
1262 },
1263 },
1264 MetaField {
1265 profiles: vec![],
1266 field_prefix: FieldPrefix::Label {
1267 label: "bb",
1268 label_color: None,
1269 },
1270 color: None,
1271 value: MetaValue::OptionString {
1272 value: self.bb.as_ref().map(|x| x as &dyn ToString),
1273 formatter: None,
1274 skip_none: false,
1275 },
1276 },
1277 ],
1278 }
1279 }
1280 }
1281
1282 let s = T1 {
1283 a: Some(5),
1284 bb: None,
1285 };
1286 assert_eq!(
1288 s.pretty(false, None, None).unwrap(),
1289 "a = 5\nbb = null\n".to_string()
1290 );
1291 }
1292
1293 #[test]
1294 fn formatter_option_struct() {
1295 struct T1 {
1296 a: u32,
1297 bb: Option<String>,
1298 }
1299
1300 impl PrettyPrint for T1 {
1301 fn meta(&self) -> Meta {
1302 Meta {
1303 padding: 5,
1304 separator: None,
1305 fields: vec![
1306 MetaField {
1307 profiles: vec![],
1308 field_prefix: FieldPrefix::Label {
1309 label: "a",
1310 label_color: None,
1311 },
1312 color: None,
1313 value: MetaValue::String {
1314 value: &self.a,
1315 formatter: Some(&|x, _| Ok((format!("{} format", x.to_string()), false))),
1316 },
1317 },
1318 MetaField {
1319 profiles: vec![],
1320 field_prefix: FieldPrefix::Label {
1321 label: "bb",
1322 label_color: None,
1323 },
1324 color: None,
1325 value: MetaValue::OptionString {
1326 value: self.bb.as_ref().map(|x| x as &dyn ToString),
1327 formatter: Some(&|x, _| Ok((format!("{} format", x.to_string()), false))),
1328 skip_none: false,
1329 },
1330 },
1331 ],
1332 }
1333 }
1334 }
1335
1336 let s = T1 {
1337 a: 5,
1338 bb: Some("option".to_string()),
1339 };
1340 assert_eq!(
1342 s.pretty(false, None, None).unwrap(),
1343 "a = 5 format\nbb = option format\n".to_string()
1344 );
1345 }
1346
1347 #[test]
1348 fn skip_none_option_struct() {
1349 struct T1 {
1350 a: Option<u32>,
1351 bb: Option<String>,
1352 }
1353
1354 impl PrettyPrint for T1 {
1355 fn meta(&self) -> Meta {
1356 Meta {
1357 padding: 5,
1358 separator: None,
1359 fields: vec![
1360 MetaField {
1361 profiles: vec![],
1362 field_prefix: FieldPrefix::Label {
1363 label: "a",
1364 label_color: None,
1365 },
1366 color: None,
1367 value: MetaValue::OptionString {
1368 value: self.a.as_ref().map(|x| x as &dyn ToString),
1369 formatter: None,
1370 skip_none: true,
1371 },
1372 },
1373 MetaField {
1374 profiles: vec![],
1375 field_prefix: FieldPrefix::Label {
1376 label: "bb",
1377 label_color: None,
1378 },
1379 color: None,
1380 value: MetaValue::OptionString {
1381 value: self.bb.as_ref().map(|x| x as &dyn ToString),
1382 formatter: None,
1383 skip_none: false,
1384 },
1385 },
1386 ],
1387 }
1388 }
1389 }
1390
1391 let s = T1 { a: None, bb: None };
1392 assert_eq!(
1394 s.pretty(false, None, None).unwrap(),
1395 "bb = null\n".to_string()
1396 );
1397 }
1398
1399 #[test]
1400 fn option_struct_colored() {
1401 struct T1 {
1402 a: Option<u32>,
1403 bb: Option<String>,
1404 }
1405
1406 impl PrettyPrint for T1 {
1407 fn meta(&self) -> Meta {
1408 Meta {
1409 padding: 5,
1410 separator: None,
1411 fields: vec![
1412 MetaField {
1413 profiles: vec![],
1414 field_prefix: FieldPrefix::Label {
1415 label: "a",
1416 label_color: None,
1417 },
1418 color: None,
1419 value: MetaValue::OptionString {
1420 value: self.a.as_ref().map(|x| x as &dyn ToString),
1421 formatter: None,
1422 skip_none: false,
1423 },
1424 },
1425 MetaField {
1426 profiles: vec![],
1427 field_prefix: FieldPrefix::Label {
1428 label: "bb",
1429 label_color: None,
1430 },
1431 color: None,
1432 value: MetaValue::OptionString {
1433 value: self.bb.as_ref().map(|x| x as &dyn ToString),
1434 formatter: None,
1435 skip_none: false,
1436 },
1437 },
1438 ],
1439 }
1440 }
1441 }
1442
1443 let s = T1 {
1444 a: Some(5),
1445 bb: None,
1446 };
1447 assert_eq!(
1449 s.pretty(true, None, None).unwrap(),
1450 "a = \u{1b}[1m\u{1b}[97m5\u{1b}[39m\u{1b}[0m\nbb = \u{1b}[35mnull\u{1b}[39m\n".to_string()
1451 );
1452 }
1453
1454 #[test]
1455 fn nested_option_struct() {
1456 struct T1 {
1457 a: u32,
1458 bb: String,
1459 cccc: bool,
1460 }
1461 impl PrettyPrint for T1 {
1462 fn meta(&self) -> Meta {
1463 Meta {
1464 padding: 5,
1465 separator: None,
1466 fields: vec![
1467 MetaField {
1468 profiles: vec![],
1469 field_prefix: FieldPrefix::Label {
1470 label: "a",
1471 label_color: None,
1472 },
1473 color: None,
1474 value: MetaValue::String {
1475 value: &self.a,
1476 formatter: None,
1477 },
1478 },
1479 MetaField {
1480 profiles: vec![],
1481 field_prefix: FieldPrefix::Label {
1482 label: "bb",
1483 label_color: None,
1484 },
1485 color: None,
1486 value: MetaValue::String {
1487 value: &self.bb,
1488 formatter: None,
1489 },
1490 },
1491 MetaField {
1492 profiles: vec![],
1493 field_prefix: FieldPrefix::Label {
1494 label: "cccc",
1495 label_color: None,
1496 },
1497 color: None,
1498 value: MetaValue::String {
1499 value: &self.cccc,
1500 formatter: None,
1501 },
1502 },
1503 ],
1504 }
1505 }
1506 }
1507 struct T2 {
1508 a: u32,
1509 n: Option<T1>,
1510 }
1511 impl PrettyPrint for T2 {
1512 fn meta(&self) -> Meta {
1513 Meta {
1514 padding: 2,
1515 separator: None,
1516 fields: vec![
1517 MetaField {
1518 profiles: vec![],
1519 field_prefix: FieldPrefix::Label {
1520 label: "a",
1521 label_color: None,
1522 },
1523 color: None,
1524 value: MetaValue::String {
1525 value: &self.a,
1526 formatter: None,
1527 },
1528 },
1529 MetaField {
1530 profiles: vec![],
1531 field_prefix: FieldPrefix::Label {
1532 label: "n",
1533 label_color: None,
1534 },
1535 color: None,
1536 value: MetaValue::OptionPretty {
1537 value: self.n.as_ref().map(|x| x as &dyn PrettyPrint),
1538 skip_none: false,
1539 },
1540 },
1541 ],
1542 }
1543 }
1544 }
1545 let s = T2 {
1546 a: 5,
1547 n: Some(T1 {
1548 a: 5,
1549 bb: "string".to_string(),
1550 cccc: false,
1551 }),
1552 };
1553 assert_eq!(
1555 s.pretty(false, None, None).unwrap(),
1556 "a = 5\nn -->\n| a = 5\n| bb = string\n| cccc = false\n".to_string()
1557 );
1558
1559 let s = T2 { a: 5, n: None };
1560 assert_eq!(
1562 s.pretty(false, None, None).unwrap(),
1563 "a = 5\nn = null\n".to_string()
1564 );
1565 }
1566
1567 #[test]
1568 fn skip_none_nested_option_struct() {
1569 struct T1 {
1570 a: u32,
1571 bb: String,
1572 cccc: bool,
1573 }
1574 impl PrettyPrint for T1 {
1575 fn meta(&self) -> Meta {
1576 Meta {
1577 padding: 5,
1578 separator: None,
1579 fields: vec![
1580 MetaField {
1581 profiles: vec![],
1582 field_prefix: FieldPrefix::Label {
1583 label: "a",
1584 label_color: None,
1585 },
1586 color: None,
1587 value: MetaValue::String {
1588 value: &self.a,
1589 formatter: None,
1590 },
1591 },
1592 MetaField {
1593 profiles: vec![],
1594 field_prefix: FieldPrefix::Label {
1595 label: "bb",
1596 label_color: None,
1597 },
1598 color: None,
1599 value: MetaValue::String {
1600 value: &self.bb,
1601 formatter: None,
1602 },
1603 },
1604 MetaField {
1605 profiles: vec![],
1606 field_prefix: FieldPrefix::Label {
1607 label: "cccc",
1608 label_color: None,
1609 },
1610 color: None,
1611 value: MetaValue::String {
1612 value: &self.cccc,
1613 formatter: None,
1614 },
1615 },
1616 ],
1617 }
1618 }
1619 }
1620 struct T2 {
1621 a: u32,
1622 n: Option<T1>,
1623 }
1624 impl PrettyPrint for T2 {
1625 fn meta(&self) -> Meta {
1626 Meta {
1627 padding: 2,
1628 separator: None,
1629 fields: vec![
1630 MetaField {
1631 profiles: vec![],
1632 field_prefix: FieldPrefix::Label {
1633 label: "a",
1634 label_color: None,
1635 },
1636 color: None,
1637 value: MetaValue::String {
1638 value: &self.a,
1639 formatter: None,
1640 },
1641 },
1642 MetaField {
1643 profiles: vec![],
1644 field_prefix: FieldPrefix::Label {
1645 label: "n",
1646 label_color: None,
1647 },
1648 color: None,
1649 value: MetaValue::OptionPretty {
1650 value: self.n.as_ref().map(|x| x as &dyn PrettyPrint),
1651 skip_none: true,
1652 },
1653 },
1654 ],
1655 }
1656 }
1657 }
1658 let s = T2 {
1659 a: 5,
1660 n: Some(T1 {
1661 a: 5,
1662 bb: "string".to_string(),
1663 cccc: false,
1664 }),
1665 };
1666 assert_eq!(
1668 s.pretty(false, None, None).unwrap(),
1669 "a = 5\nn -->\n| a = 5\n| bb = string\n| cccc = false\n".to_string()
1670 );
1671
1672 let s = T2 { a: 5, n: None };
1673 assert_eq!(s.pretty(false, None, None).unwrap(), "a = 5\n".to_string());
1675 }
1676
1677 #[test]
1678 fn vec_struct() {
1679 struct T1 {
1680 a: Vec<u32>,
1681 bb: Vec<String>,
1682 }
1683
1684 impl PrettyPrint for T1 {
1685 fn meta(&self) -> Meta {
1686 Meta {
1687 padding: 5,
1688 separator: None,
1689 fields: vec![
1690 MetaField {
1691 profiles: vec![],
1692 field_prefix: FieldPrefix::Label {
1693 label: "a",
1694 label_color: None,
1695 },
1696 color: None,
1697 value: MetaValue::VecString(self.a.iter().map(|x| x as &dyn ToString).collect()),
1698 },
1699 MetaField {
1700 profiles: vec![],
1701 field_prefix: FieldPrefix::Label {
1702 label: "bb",
1703 label_color: None,
1704 },
1705 color: None,
1706 value: MetaValue::VecString(self.bb.iter().map(|x| x as &dyn ToString).collect()),
1707 },
1708 ],
1709 }
1710 }
1711 }
1712
1713 let s = T1 {
1714 a: vec![5, 3, 1, 4, 2],
1715 bb: vec!["a".to_string(), "string".to_string()],
1716 };
1717 assert_eq!(
1719 s.pretty(false, None, None).unwrap(),
1720 "a :\n - 5\n - 3\n - 1\n - 4\n - 2\nbb :\n - a\n - string\n".to_string()
1721 );
1722 }
1723
1724 #[test]
1725 fn vec_struct_colored() {
1726 struct T1 {
1727 a: Vec<u32>,
1728 bb: Vec<String>,
1729 }
1730
1731 impl PrettyPrint for T1 {
1732 fn meta(&self) -> Meta {
1733 Meta {
1734 padding: 5,
1735 separator: None,
1736 fields: vec![
1737 MetaField {
1738 profiles: vec![],
1739 field_prefix: FieldPrefix::Label {
1740 label: "a",
1741 label_color: None,
1742 },
1743 color: None,
1744 value: MetaValue::VecString(self.a.iter().map(|x| x as &dyn ToString).collect()),
1745 },
1746 MetaField {
1747 profiles: vec![],
1748 field_prefix: FieldPrefix::Label {
1749 label: "bb",
1750 label_color: None,
1751 },
1752 color: None,
1753 value: MetaValue::VecString(self.bb.iter().map(|x| x as &dyn ToString).collect()),
1754 },
1755 ],
1756 }
1757 }
1758 }
1759
1760 let s = T1 {
1761 a: vec![5, 3, 1, 4, 2],
1762 bb: vec!["a".to_string(), "string".to_string()],
1763 };
1764 assert_eq!(
1766 s.pretty(true, None, None).unwrap(),
1767 "a :\n - \u{1b}[1m\u{1b}[97m5\u{1b}[39m\u{1b}[0m\n - \u{1b}[1m\u{1b}[97m3\u{1b}[39m\u{1b}[0m\n - \u{1b}[1m\u{1b}[97m1\u{1b}[39m\u{1b}[0m\n - \u{1b}[1m\u{1b}[97m4\u{1b}[39m\u{1b}[0m\n - \u{1b}[1m\u{1b}[97m2\u{1b}[39m\u{1b}[0m\nbb :\n - \u{1b}[1m\u{1b}[97ma\u{1b}[39m\u{1b}[0m\n - \u{1b}[1m\u{1b}[97mstring\u{1b}[39m\u{1b}[0m\n".to_string()
1768 );
1769 }
1770
1771 #[test]
1772 fn nested_vec_struct() {
1773 struct T1 {
1774 a: u32,
1775 bb: String,
1776 cccc: bool,
1777 }
1778 impl PrettyPrint for T1 {
1779 fn meta(&self) -> Meta {
1780 Meta {
1781 padding: 5,
1782 separator: None,
1783 fields: vec![
1784 MetaField {
1785 profiles: vec![],
1786 field_prefix: FieldPrefix::Label {
1787 label: "a",
1788 label_color: None,
1789 },
1790 color: None,
1791 value: MetaValue::String {
1792 value: &self.a,
1793 formatter: None,
1794 },
1795 },
1796 MetaField {
1797 profiles: vec![],
1798 field_prefix: FieldPrefix::Label {
1799 label: "bb",
1800 label_color: None,
1801 },
1802 color: None,
1803 value: MetaValue::String {
1804 value: &self.bb,
1805 formatter: None,
1806 },
1807 },
1808 MetaField {
1809 profiles: vec![],
1810 field_prefix: FieldPrefix::Label {
1811 label: "cccc",
1812 label_color: None,
1813 },
1814 color: None,
1815 value: MetaValue::String {
1816 value: &self.cccc,
1817 formatter: None,
1818 },
1819 },
1820 ],
1821 }
1822 }
1823 }
1824 struct T2 {
1825 a: u32,
1826 n: Vec<T1>,
1827 }
1828 impl PrettyPrint for T2 {
1829 fn meta(&self) -> Meta {
1830 Meta {
1831 padding: 2,
1832 separator: None,
1833 fields: vec![
1834 MetaField {
1835 profiles: vec![],
1836 field_prefix: FieldPrefix::Label {
1837 label: "a",
1838 label_color: None,
1839 },
1840 color: None,
1841 value: MetaValue::String {
1842 value: &self.a,
1843 formatter: None,
1844 },
1845 },
1846 MetaField {
1847 profiles: vec![],
1848 field_prefix: FieldPrefix::Label {
1849 label: "n",
1850 label_color: None,
1851 },
1852 color: None,
1853 value: MetaValue::VecPretty(self.n.iter().map(|x| x as &dyn PrettyPrint).collect()),
1854 },
1855 ],
1856 }
1857 }
1858 }
1859 let s = T2 {
1860 a: 5,
1861 n: vec![T1 {
1862 a: 5,
1863 bb: "string".to_string(),
1864 cccc: false,
1865 }],
1866 };
1867 assert_eq!(
1869 s.pretty(false, None, None).unwrap(),
1870 "a = 5\nn :\n - a = 5\n bb = string\n cccc = false\n".to_string()
1871 );
1872 }
1873
1874 #[test]
1875 fn option_vec_struct() {
1876 struct T1 {
1877 a: Option<Vec<u32>>,
1878 bb: Option<Vec<String>>,
1879 }
1880
1881 impl PrettyPrint for T1 {
1882 fn meta(&self) -> Meta {
1883 Meta {
1884 padding: 5,
1885 separator: None,
1886 fields: vec![
1887 MetaField {
1888 profiles: vec![],
1889 field_prefix: FieldPrefix::Label {
1890 label: "a",
1891 label_color: None,
1892 },
1893 color: None,
1894 value: MetaValue::OptionVecString {
1895 value: self
1896 .a
1897 .as_ref()
1898 .map(|vec| vec.iter().map(|x| x as &dyn ToString).collect()),
1899 skip_none: false,
1900 },
1901 },
1902 MetaField {
1903 profiles: vec![],
1904 field_prefix: FieldPrefix::Label {
1905 label: "bb",
1906 label_color: None,
1907 },
1908 color: None,
1909 value: MetaValue::OptionVecString {
1910 value: self
1911 .bb
1912 .as_ref()
1913 .map(|vec| vec.iter().map(|x| x as &dyn ToString).collect()),
1914 skip_none: false,
1915 },
1916 },
1917 ],
1918 }
1919 }
1920 }
1921
1922 let s = T1 {
1923 a: Some(vec![5, 3, 1, 4, 2]),
1924 bb: Some(vec!["a".to_string(), "string".to_string()]),
1925 };
1926 assert_eq!(
1928 s.pretty(false, None, None).unwrap(),
1929 "a :\n - 5\n - 3\n - 1\n - 4\n - 2\nbb :\n - a\n - string\n".to_string()
1930 );
1931
1932 let s = T1 { a: None, bb: None };
1933 assert_eq!(
1935 s.pretty(false, None, None).unwrap(),
1936 "a : null\nbb : null\n".to_string()
1937 );
1938 }
1939
1940 #[test]
1941 fn skip_none_option_vec_struct() {
1942 struct T1 {
1943 a: Option<Vec<u32>>,
1944 bb: Option<Vec<String>>,
1945 }
1946
1947 impl PrettyPrint for T1 {
1948 fn meta(&self) -> Meta {
1949 Meta {
1950 padding: 5,
1951 separator: None,
1952 fields: vec![
1953 MetaField {
1954 profiles: vec![],
1955 field_prefix: FieldPrefix::Label {
1956 label: "a",
1957 label_color: None,
1958 },
1959 color: None,
1960 value: MetaValue::OptionVecString {
1961 value: self
1962 .a
1963 .as_ref()
1964 .map(|vec| vec.iter().map(|x| x as &dyn ToString).collect()),
1965 skip_none: true,
1966 },
1967 },
1968 MetaField {
1969 profiles: vec![],
1970 field_prefix: FieldPrefix::Label {
1971 label: "bb",
1972 label_color: None,
1973 },
1974 color: None,
1975 value: MetaValue::OptionVecString {
1976 value: self
1977 .bb
1978 .as_ref()
1979 .map(|vec| vec.iter().map(|x| x as &dyn ToString).collect()),
1980 skip_none: false,
1981 },
1982 },
1983 ],
1984 }
1985 }
1986 }
1987
1988 let s = T1 {
1989 a: Some(vec![5, 3, 1, 4, 2]),
1990 bb: Some(vec!["a".to_string(), "string".to_string()]),
1991 };
1992 assert_eq!(
1994 s.pretty(false, None, None).unwrap(),
1995 "a :\n - 5\n - 3\n - 1\n - 4\n - 2\nbb :\n - a\n - string\n".to_string()
1996 );
1997
1998 let s = T1 { a: None, bb: None };
1999 assert_eq!(
2001 s.pretty(false, None, None).unwrap(),
2002 "bb : null\n".to_string()
2003 );
2004 }
2005
2006 #[test]
2007 fn option_vec_struct_colored() {
2008 struct T1 {
2009 a: Option<Vec<u32>>,
2010 bb: Option<Vec<String>>,
2011 }
2012
2013 impl PrettyPrint for T1 {
2014 fn meta(&self) -> Meta {
2015 Meta {
2016 padding: 5,
2017 separator: None,
2018 fields: vec![
2019 MetaField {
2020 profiles: vec![],
2021 field_prefix: FieldPrefix::Label {
2022 label: "a",
2023 label_color: None,
2024 },
2025 color: None,
2026 value: MetaValue::OptionVecString {
2027 value: self
2028 .a
2029 .as_ref()
2030 .map(|vec| vec.iter().map(|x| x as &dyn ToString).collect()),
2031 skip_none: false,
2032 },
2033 },
2034 MetaField {
2035 profiles: vec![],
2036 field_prefix: FieldPrefix::Label {
2037 label: "bb",
2038 label_color: None,
2039 },
2040 color: None,
2041 value: MetaValue::OptionVecString {
2042 value: self
2043 .bb
2044 .as_ref()
2045 .map(|vec| vec.iter().map(|x| x as &dyn ToString).collect()),
2046 skip_none: false,
2047 },
2048 },
2049 ],
2050 }
2051 }
2052 }
2053
2054 let s = T1 {
2055 a: Some(vec![5, 3, 1, 4, 2]),
2056 bb: Some(vec!["a".to_string(), "string".to_string()]),
2057 };
2058 assert_eq!(
2060 s.pretty(true, None, None).unwrap(),
2061 "a :\n - \u{1b}[1m\u{1b}[97m5\u{1b}[39m\u{1b}[0m\n - \u{1b}[1m\u{1b}[97m3\u{1b}[39m\u{1b}[0m\n - \u{1b}[1m\u{1b}[97m1\u{1b}[39m\u{1b}[0m\n - \u{1b}[1m\u{1b}[97m4\u{1b}[39m\u{1b}[0m\n - \u{1b}[1m\u{1b}[97m2\u{1b}[39m\u{1b}[0m\nbb :\n - \u{1b}[1m\u{1b}[97ma\u{1b}[39m\u{1b}[0m\n - \u{1b}[1m\u{1b}[97mstring\u{1b}[39m\u{1b}[0m\n".to_string()
2062 );
2063
2064 let s = T1 { a: None, bb: None };
2065 assert_eq!(
2067 s.pretty(false, None, None).unwrap(),
2068 "a : null\nbb : null\n".to_string()
2069 );
2070 }
2071
2072 #[test]
2073 fn nested_option_vec_struct() {
2074 struct T1 {
2075 a: u32,
2076 bb: String,
2077 cccc: bool,
2078 }
2079 impl PrettyPrint for T1 {
2080 fn meta(&self) -> Meta {
2081 Meta {
2082 padding: 5,
2083 separator: None,
2084 fields: vec![
2085 MetaField {
2086 profiles: vec![],
2087 field_prefix: FieldPrefix::Label {
2088 label: "a",
2089 label_color: None,
2090 },
2091 color: None,
2092 value: MetaValue::String {
2093 value: &self.a,
2094 formatter: None,
2095 },
2096 },
2097 MetaField {
2098 profiles: vec![],
2099 field_prefix: FieldPrefix::Label {
2100 label: "bb",
2101 label_color: None,
2102 },
2103 color: None,
2104 value: MetaValue::String {
2105 value: &self.bb,
2106 formatter: None,
2107 },
2108 },
2109 MetaField {
2110 profiles: vec![],
2111 field_prefix: FieldPrefix::Label {
2112 label: "cccc",
2113 label_color: None,
2114 },
2115 color: None,
2116 value: MetaValue::String {
2117 value: &self.cccc,
2118 formatter: None,
2119 },
2120 },
2121 ],
2122 }
2123 }
2124 }
2125 struct T2 {
2126 a: u32,
2127 n: Option<Vec<T1>>,
2128 }
2129 impl PrettyPrint for T2 {
2130 fn meta(&self) -> Meta {
2131 Meta {
2132 padding: 2,
2133 separator: None,
2134 fields: vec![
2135 MetaField {
2136 profiles: vec![],
2137 field_prefix: FieldPrefix::Label {
2138 label: "a",
2139 label_color: None,
2140 },
2141 color: None,
2142 value: MetaValue::String {
2143 value: &self.a,
2144 formatter: None,
2145 },
2146 },
2147 MetaField {
2148 profiles: vec![],
2149 field_prefix: FieldPrefix::Label {
2150 label: "n",
2151 label_color: None,
2152 },
2153 color: None,
2154 value: MetaValue::OptionVecPretty {
2155 value: self
2156 .n
2157 .as_ref()
2158 .map(|vec| vec.iter().map(|x| x as &dyn PrettyPrint).collect()),
2159 skip_none: false,
2160 },
2161 },
2162 ],
2163 }
2164 }
2165 }
2166 let s = T2 {
2167 a: 5,
2168 n: Some(vec![T1 {
2169 a: 5,
2170 bb: "string".to_string(),
2171 cccc: false,
2172 }]),
2173 };
2174 assert_eq!(
2176 s.pretty(false, None, None).unwrap(),
2177 "a = 5\nn :\n - a = 5\n bb = string\n cccc = false\n".to_string()
2178 );
2179
2180 let s = T2 { a: 5, n: None };
2181 assert_eq!(
2183 s.pretty(false, None, None).unwrap(),
2184 "a = 5\nn : null\n".to_string()
2185 );
2186
2187 let s = T2 { a: 5, n: None };
2188 assert_eq!(
2190 s.pretty(true, None, None).unwrap(),
2191 "a = \u{1b}[1m\u{1b}[97m5\u{1b}[39m\u{1b}[0m\nn :\u{1b}[35m null\n\u{1b}[39m".to_string()
2192 );
2193 }
2194
2195 #[test]
2196 fn skip_none_nested_option_vec_struct() {
2197 struct T1 {
2198 a: u32,
2199 bb: String,
2200 cccc: bool,
2201 }
2202 impl PrettyPrint for T1 {
2203 fn meta(&self) -> Meta {
2204 Meta {
2205 padding: 5,
2206 separator: None,
2207 fields: vec![
2208 MetaField {
2209 profiles: vec![],
2210 field_prefix: FieldPrefix::Label {
2211 label: "a",
2212 label_color: None,
2213 },
2214 color: None,
2215 value: MetaValue::String {
2216 value: &self.a,
2217 formatter: None,
2218 },
2219 },
2220 MetaField {
2221 profiles: vec![],
2222 field_prefix: FieldPrefix::Label {
2223 label: "bb",
2224 label_color: None,
2225 },
2226 color: None,
2227 value: MetaValue::String {
2228 value: &self.bb,
2229 formatter: None,
2230 },
2231 },
2232 MetaField {
2233 profiles: vec![],
2234 field_prefix: FieldPrefix::Label {
2235 label: "cccc",
2236 label_color: None,
2237 },
2238 color: None,
2239 value: MetaValue::String {
2240 value: &self.cccc,
2241 formatter: None,
2242 },
2243 },
2244 ],
2245 }
2246 }
2247 }
2248 struct T2 {
2249 a: u32,
2250 n: Option<Vec<T1>>,
2251 }
2252 impl PrettyPrint for T2 {
2253 fn meta(&self) -> Meta {
2254 Meta {
2255 padding: 2,
2256 separator: None,
2257 fields: vec![
2258 MetaField {
2259 profiles: vec![],
2260 field_prefix: FieldPrefix::Label {
2261 label: "a",
2262 label_color: None,
2263 },
2264 color: None,
2265 value: MetaValue::String {
2266 value: &self.a,
2267 formatter: None,
2268 },
2269 },
2270 MetaField {
2271 profiles: vec![],
2272 field_prefix: FieldPrefix::Label {
2273 label: "n",
2274 label_color: None,
2275 },
2276 color: None,
2277 value: MetaValue::OptionVecPretty {
2278 value: self
2279 .n
2280 .as_ref()
2281 .map(|vec| vec.iter().map(|x| x as &dyn PrettyPrint).collect()),
2282 skip_none: true,
2283 },
2284 },
2285 ],
2286 }
2287 }
2288 }
2289 let s = T2 {
2290 a: 5,
2291 n: Some(vec![T1 {
2292 a: 5,
2293 bb: "string".to_string(),
2294 cccc: false,
2295 }]),
2296 };
2297 assert_eq!(
2299 s.pretty(false, None, None).unwrap(),
2300 "a = 5\nn :\n - a = 5\n bb = string\n cccc = false\n".to_string()
2301 );
2302
2303 let s = T2 { a: 5, n: None };
2304 assert_eq!(s.pretty(false, None, None).unwrap(), "a = 5\n".to_string());
2306
2307 let s = T2 { a: 5, n: None };
2308 assert_eq!(
2310 s.pretty(true, None, None).unwrap(),
2311 "a = \u{1b}[1m\u{1b}[97m5\u{1b}[39m\u{1b}[0m\n".to_string()
2312 );
2313 }
2314
2315 #[test]
2316 fn coloring_none() {
2317 assert_eq!(
2318 coloring("string".to_string(), &None),
2319 "\u{1b}[1m\u{1b}[97mstring\u{1b}[39m\u{1b}[0m".to_string()
2320 );
2321 }
2322
2323 #[test]
2324 fn coloring_some_red() {
2325 assert_eq!(
2326 coloring("string".to_string(), &Some(Color::Red)),
2327 "\u{1b}[1m\u{1b}[31mstring\u{1b}[39m\u{1b}[0m".to_string()
2328 );
2329 }
2330
2331 #[test]
2332 fn simple_enum() {
2333 enum E1 {
2334 Aa,
2335 Bb,
2336 }
2337
2338 impl PrettyPrint for E1 {
2339 fn meta(&self) -> Meta {
2340 Meta {
2341 padding: 5,
2342 separator: None,
2343 fields: vec![MetaField {
2344 profiles: vec![],
2345 field_prefix: FieldPrefix::None,
2346 color: None,
2347 value: MetaValue::Variant {
2348 value: match self {
2349 E1::Aa => &"Aa",
2350 E1::Bb => &"Bb",
2351 },
2352 formatter: None,
2353 },
2354 }],
2355 }
2356 }
2357 }
2358
2359 let s = E1::Aa;
2360 assert_eq!(s.pretty(false, None, None).unwrap(), "Aa\n".to_string());
2361 let s = E1::Bb;
2362 assert_eq!(s.pretty(false, None, None).unwrap(), "Bb\n".to_string());
2363 }
2364
2365 #[test]
2366 fn enum_with_struct() {
2367 #[derive(Debug)]
2368 struct T1 {
2369 a: u32,
2370 bb: String,
2371 cccc: bool,
2372 }
2373
2374 impl PrettyPrint for T1 {
2375 fn meta(&self) -> Meta {
2376 Meta {
2377 padding: 5,
2378 separator: None,
2379 fields: vec![
2380 MetaField {
2381 profiles: vec![],
2382 field_prefix: FieldPrefix::Label {
2383 label: "a",
2384 label_color: None,
2385 },
2386 color: None,
2387 value: MetaValue::String {
2388 value: &self.a,
2389 formatter: None,
2390 },
2391 },
2392 MetaField {
2393 profiles: vec![],
2394 field_prefix: FieldPrefix::Label {
2395 label: "bb",
2396 label_color: None,
2397 },
2398 color: None,
2399 value: MetaValue::String {
2400 value: &self.bb,
2401 formatter: None,
2402 },
2403 },
2404 MetaField {
2405 profiles: vec![],
2406 field_prefix: FieldPrefix::Label {
2407 label: "cccc",
2408 label_color: None,
2409 },
2410 color: None,
2411 value: MetaValue::String {
2412 value: &self.cccc,
2413 formatter: None,
2414 },
2415 },
2416 ],
2417 }
2418 }
2419 }
2420
2421 enum E1 {
2422 Aa(T1),
2423 Bb,
2424 }
2425
2426 impl PrettyPrint for E1 {
2427 fn meta(&self) -> Meta {
2428 Meta {
2429 padding: 5,
2430 separator: None,
2431 fields: vec![MetaField {
2432 profiles: vec![],
2433 field_prefix: FieldPrefix::Label {
2434 label: "a",
2435 label_color: None,
2436 },
2437 color: None,
2438 value: match self {
2439 E1::Aa(t) => MetaValue::Pretty(t),
2440 E1::Bb => MetaValue::Variant {
2441 value: &"Bb",
2442 formatter: None,
2443 },
2444 },
2445 }],
2446 }
2447 }
2448 }
2449
2450 let s = E1::Aa(T1 {
2451 a: 14,
2452 bb: "aaa".to_string(),
2453 cccc: true,
2454 });
2455 assert_eq!(
2456 s.pretty(false, None, None).unwrap(),
2457 "a -->\n| a = 14\n| bb = aaa\n| cccc = true\n".to_string()
2458 );
2459 let s = E1::Bb;
2460 assert_eq!(
2461 s.pretty(false, None, None).unwrap(),
2462 "a = Bb\n".to_string()
2463 );
2464 }
2465}