crud_pretty_struct/
lib.rs

1//! # Pretty Struct
2//!
3//! Displays (json) structures and enums in a pretty way.
4//!
5//! This crate is linked to the crud library. If I have time and motivation to generalize it, it can be an indenpendant crate.
6//!
7//! ## Example
8//!
9//! ```rust
10//! use crud_pretty_struct::PrettyPrint;
11//! # #[derive(PrettyPrint)]
12//! # struct OtherPrettyStruct {val:u32}
13//! #[derive(PrettyPrint)]
14//! struct Foo {
15//!     #[pretty(color="green")]
16//!     a: u32,
17//!     #[pretty(skip_none)]
18//!     b: Option<String>,
19//!     #[pretty(formatter=crud_pretty_struct::formatters::bool_check_formatter)]
20//!     c: bool,
21//!     #[pretty(is_pretty)]
22//!     d: OtherPrettyStruct
23//! }
24//! # let var = Foo{a:0,b:None,c:false,d:OtherPrettyStruct{val:0}};
25//! // Instanciate a `var` of type  `Foo`
26//! println!("{}",var.pretty(true,None,None).expect("Can prettify var"));
27//! ```
28//!
29//! ## Field Options
30//!
31//! ##### `is_pretty`
32//!
33//! the nested struct implements `PrettyPrint` and should be printed using it.
34//!
35//! ```rust
36//! use crud_pretty_struct_derive::PrettyPrint;
37//! #[derive(PrettyPrint)]
38//! struct OtherPrettyStruct {}
39//! #[derive(PrettyPrint)]
40//! struct Foo {
41//!     #[pretty(is_pretty)]
42//!     field: OtherPrettyStruct
43//! }
44//! ```
45//!
46//! ##### `label`
47//!
48//! custom label for this field
49//! ```rust
50//! # use crud_pretty_struct_derive::PrettyPrint;
51//! #[derive(PrettyPrint)]
52//! struct Foo {
53//!     #[pretty(label="☀️ my field")]
54//!     field: u32
55//! }
56//! ```
57//! ##### `color`
58//!
59//! custom color for the value of this field. The avaiable colors are [Color].
60//! ```rust
61//! # use crud_pretty_struct_derive::PrettyPrint;
62//! #[derive(PrettyPrint)]
63//! struct Foo {
64//!     #[pretty(color="red")]
65//!     field: u32
66//! }
67//! ```
68//! ##### `label_color`
69//!
70//! custom color for the label of dthis field. The avaiable colors are [Color].
71//! ```rust
72//! # use crud_pretty_struct_derive::PrettyPrint;
73//! #[derive(PrettyPrint)]
74//! struct Foo {
75//!     #[pretty(label_color="red")]
76//!     field: u32
77//! }
78//! ```
79//! ##### `skip`
80//!
81//! skip the field. It won't be display.
82//! ```rust
83//! # use crud_pretty_struct_derive::PrettyPrint;
84//! #[derive(PrettyPrint)]
85//! struct Foo {
86//!     #[pretty(skip)]
87//!     field: u32
88//! }
89//! ```
90//! ##### `skip_none`
91//!
92//! skip the field  if the value is `None`. The type of the field should be an `Option<T>`.
93//! ```rust
94//! # use crud_pretty_struct_derive::PrettyPrint;
95//! #[derive(PrettyPrint)]
96//! struct Foo {
97//!     #[pretty(skip_none)]
98//!     field: Option<u32>
99//! }
100//! ```
101//! ##### `profile`
102//! the field is displayed only when this field profiles matched the profile declare when calling the `pretty` function.
103//!
104//! ```rust
105//! # use crud_pretty_struct::PrettyPrint;
106//! #[derive(PrettyPrint)]
107//! struct Foo {
108//!   #[pretty(profiles = "a")]
109//!   field1: u32,
110//!   #[pretty(profiles = "a,b")]
111//!   field2: bool,
112//! }
113//!
114//! let foo = Foo{field1:0, field2:false};
115//! foo.pretty(false, None, Some("b")).unwrap(); //  print only `field2`
116//! ```
117//! ##### `formatter`
118//!
119//! Custom value formatter for this field.
120//!
121//! Some [formatters] are shipped in this crate.
122//! ```rust
123//! # use crud_pretty_struct_derive::PrettyPrint;
124//! #[derive(PrettyPrint)]
125//! struct Foo {
126//!     #[pretty(formatter=crud_pretty_struct::formatters::bool_check_formatter)]
127//!     field: bool
128//! }
129//! ```
130//!
131//! Formatters should follow this signature:
132//! ```rust
133//! type Formatter = dyn Fn(/*value:*/ &dyn ToString, /*colored:*/ bool) -> miette::Result<(String, bool)>;
134//! ```
135//! Parameters:
136//! - `value`: the value to format
137//! - `colored`: when `true` the formatted value can be colored
138//!
139//! Result:
140//! - String: the formatted value
141//! - bool: returns `true` if the value have colors. returns `false` if the value don't have colors then default color will be applied.
142//!
143//! ```rust
144//! # use crud_pretty_struct_derive::PrettyPrint;
145//! #[derive(PrettyPrint)]
146//! struct Foo {
147//!     #[pretty(formatter=|x, _| Ok((format!("{} kg", x.to_string()), false)))]
148//!     field: f32
149//! }
150//! ```
151//!
152//! ## Enum Option
153//!
154//! Limitations on enums:
155//! - unit variants are supported
156//! - tuple variants with only 1 argument are supported
157//!
158//! ##### `color`
159//!
160//! custom color for this variant avaiable colors are [Color].
161//! ```rust
162//! # use crud_pretty_struct_derive::PrettyPrint;
163//! #[derive(PrettyPrint)]
164//! enum Foo {
165//!     #[pretty(color="red")]
166//!     Variant
167//! }
168//! ```
169//!
170//! ##### `label`
171//!
172//! custom label for this variant
173//! ```rust
174//! # use crud_pretty_struct_derive::PrettyPrint;
175//! #[derive(PrettyPrint)]
176//! enum Foo {
177//!     #[pretty(label="☀️ my field")]
178//!     Variant
179//! }
180//! ```
181//!
182//!
183pub mod formatters;
184pub mod impls;
185
186use crate::formatters::identity_formatter;
187pub use crud_pretty_struct_derive::*;
188//pub use impls::*;
189use 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() // TODO: coloring
389                        } 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() // TODO: coloring
409                        } 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(), // TODO: coloring
459                        }
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() // TODO: coloring
494                          } 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() // TODO: coloring
576                        } 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() // TODO: coloring
607                        } 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(), // TODO: coloring
657                        }
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() // TODO: coloring
692                          } 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    //    print!("{}", s.pretty(false, None,&None).unwrap());
790    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    //    print!("{}", s.pretty(false, None,&None).unwrap());
968    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    //    print!("{}", s.pretty(false, None,&None).unwrap());
1034    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    //    print!("{}", s.pretty(true, None));
1100    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    //    print!("{}", s.pretty(true, None));
1166    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    //    print!("{}", s.pretty(true, None));
1232    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    //    print!("{}", s.pretty(false, None, None).unwrap());
1287    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    //    print!("{}", s.pretty(false, None, None).unwrap());
1341    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    //    print!("{}", s.pretty(false, None, None).unwrap());
1393    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    //    print!("{}", s.pretty(true, None));
1448    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    //    print!("{}", s.pretty(false, None, None).unwrap());
1554    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    //    print!("{}", s.pretty(false, None, None).unwrap());
1561    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    //    print!("{}", s.pretty(false, None, None).unwrap());
1667    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    //    print!("{}", s.pretty(false, None, None).unwrap());
1674    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    //    print!("{}", s.pretty(false, None, None).unwrap());
1718    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    //    print!("{}", s.pretty(true, None, None).unwrap());
1765    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    //    print!("{}", s.pretty(false, None, None).unwrap());
1868    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    //    print!("{}", s.pretty(false, None, None).unwrap());
1927    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    //    print!("{}", s.pretty(false, None, None).unwrap());
1934    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    //    print!("{}", s.pretty(false, None, None).unwrap());
1993    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    //    print!("{}", s.pretty(false, None, None).unwrap());
2000    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    //    print!("{}", s.pretty(true, None, None).unwrap());
2059    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    //    print!("{}", s.pretty(false, None, None).unwrap());
2066    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    //    print!("{}", s.pretty(false, None, None).unwrap());
2175    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    //    print!("{}", s.pretty(false, None, None).unwrap());
2182    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    //    print!("{}", s.pretty(true, None, None).unwrap());
2189    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    //    print!("{}", s.pretty(false, None, None).unwrap());
2298    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    //    print!("{}", s.pretty(false, None, None).unwrap());
2305    assert_eq!(s.pretty(false, None, None).unwrap(), "a = 5\n".to_string());
2306
2307    let s = T2 { a: 5, n: None };
2308    //    print!("{}", s.pretty(true, None, None).unwrap());
2309    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}