crud-pretty-struct 0.1.7

Pretty display for struct
Documentation
use crate::{coloring, Meta, PrettyPrint};
use miette::Result;
use owo_colors::OwoColorize;
use pad::PadStr;
use serde_json::Value;
use std::fmt::Write;
use unicode_width::UnicodeWidthStr;

impl PrettyPrint for Value {
  fn meta(&self) -> Meta {
    Meta {
      padding: 0,
      separator: None,
      fields: vec![],
    }
  }
  fn pretty(&self, colored: bool, prefix: Option<String>, _profile: Option<&str>) -> Result<String> {
    let Meta { separator, .. } = self.meta();

    let separator = separator.unwrap_or("= ");

    let prefix_ = if let Some(prefix) = &prefix {
      if colored {
        prefix.truecolor(80, 80, 80).to_string()
      } else {
        prefix.to_owned()
      }
    } else {
      "".into()
    };
    let prefix = &prefix.unwrap_or_default();

    let (v, should_color, should_prefix) = match self {
      Value::Null => ("null".to_string(), true, true),
      Value::Bool(b) => (b.to_string(), true, true),
      Value::Number(n) => (n.to_string(), true, true),
      Value::String(s) => (s.to_owned(), true, true),
      Value::Array(vv) => (
        vv.iter().fold(String::new(), |mut output, item| {
          let end = match item {
            Value::Array(_) | Value::Object(_) => "",
            _ => "\n",
          };
          let _ = write!(
            output,
            "{prefix_}{}{end}",
            item
              .pretty(colored, Some(prefix.clone() + "   "), _profile)
              .unwrap_or_default()
              .replacen(&(prefix.clone() + "   "), " - ", 1)
          );
          output
        }),
        false,
        false,
      ),
      Value::Object(o) => (
        {
          let padding = 1 + o.keys().map(|k| k.width()).max().unwrap_or_default();

          o.iter()
            .enumerate()
            .map(|(_i, (k, v))| {
              let separator = match v {
                Value::Array(_) | Value::Object(_) => "-->\n",
                _ => separator,
              };
              let end = match v {
                Value::Array(_) | Value::Object(_) => "",
                _ => "\n",
              };
              let v = v
                .pretty(
                  colored,
                  match v {
                    Value::Array(_) | Value::Object(_) => Some(prefix.clone() + "| "),
                    _ => None,
                  },
                  _profile,
                )
                .unwrap_or_default();
              if colored {
                let v = coloring(v, &None);
                format!("{prefix_}{}{separator}{v}{end}", k.pad_to_width(padding))
              } else {
                format!("{prefix}{}{separator}{v}{end}", k.pad_to_width(padding))
              }
            })
            .collect()
        },
        false,
        false,
      ),
    };

    let prefix_ = if should_prefix { prefix_ } else { "".into() };

    let pretty_hashmap = if colored && should_color {
      let v = coloring(v, &None);
      format!("{prefix_}{v}")
    } else {
      format!("{prefix_}{v}")
    };

    Ok(pretty_hashmap)
  }
}

#[cfg(test)]
mod tests {
  use crate::PrettyPrint;
  use serde_json::{json, Value};

  #[test]
  fn value_object() {
    let v: Value = json!({});
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(result, "".to_string());

    let v: Value = json!({"a":1});
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(result, "a = 1\n".to_string());

    let v: Value = json!({"number":1,"bool":true,"string":"string"});
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(
      result,
      "bool   = true\nnumber = 1\nstring = string\n".to_string()
    );
    let v: Value = json!({"array":[1, 3, 4]});
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(result, "array -->\n|  - 1\n|  - 3\n|  - 4\n".to_string());

    let v: Value = json!({"object":{"aaa":1,"b":2}});
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(result, "object -->\n| aaa = 1\n| b   = 2\n".to_string());

    let v: Value = json!({"object":{"aaa":1,"b":{"a":"aaaa","bbb":["a","bb","ccc"]}}});
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(result, "object -->\n| aaa = 1\n| b   -->\n| | a   = aaaa\n| | bbb -->\n| | |  - a\n| | |  - bb\n| | |  - ccc\n".to_string());

    let v: Value = json!({"array":[{"a":1,"bb":2},{"b":2}]});
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(
      result,
      "array -->\n|  - a  = 1\n|    bb = 2\n|  - b = 2\n".to_string()
    );
  }

  #[test]
  fn value_object_colored() {
    let v: Value = json!({});
    let result = v.pretty(true, None, None).unwrap_or_default();
    assert_eq!(result, "".to_string());

    let v: Value = json!({"a":1});
    let result = v.pretty(true, None, None).unwrap_or_default();
    assert_eq!(
      result,
      "a = \u{1b}[1m\u{1b}[97m\u{1b}[1m\u{1b}[97m1\u{1b}[39m\u{1b}[0m\u{1b}[39m\u{1b}[0m\n"
        .to_string()
    );

    let v: Value = json!({"number":1,"bool":true,"string":"string"});
    let result = v.pretty(true, None, None).unwrap_or_default();
    assert_eq!(
      result,
     "bool   = \u{1b}[1m\u{1b}[97m\u{1b}[1m\u{1b}[97mtrue\u{1b}[39m\u{1b}[0m\u{1b}[39m\u{1b}[0m\nnumber = \u{1b}[1m\u{1b}[97m\u{1b}[1m\u{1b}[97m1\u{1b}[39m\u{1b}[0m\u{1b}[39m\u{1b}[0m\nstring = \u{1b}[1m\u{1b}[97m\u{1b}[1m\u{1b}[97mstring\u{1b}[39m\u{1b}[0m\u{1b}[39m\u{1b}[0m\n".to_string()
    );
    let v: Value = json!({"array":[1, 3, 4]});
    let result = v.pretty(true, None, None).unwrap_or_default();
    assert_eq!(result,  "array -->\n\u{1b}[1m\u{1b}[97m\u{1b}[38;2;80;80;80m| \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97m1\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m| \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97m3\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m| \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97m4\u{1b}[39m\u{1b}[0m\n\u{1b}[39m\u{1b}[0m".to_string());

    let v: Value = json!({"object":{"aaa":1,"b":2}});
    let result = v.pretty(true, None, None).unwrap_or_default();
    assert_eq!(result, "object -->\n\u{1b}[1m\u{1b}[97m\u{1b}[38;2;80;80;80m| \u{1b}[39maaa = \u{1b}[1m\u{1b}[97m\u{1b}[1m\u{1b}[97m1\u{1b}[39m\u{1b}[0m\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m| \u{1b}[39mb   = \u{1b}[1m\u{1b}[97m\u{1b}[1m\u{1b}[97m2\u{1b}[39m\u{1b}[0m\u{1b}[39m\u{1b}[0m\n\u{1b}[39m\u{1b}[0m".to_string());

    let v: Value = json!({"object":{"aaa":1,"b":{"a":"aaaa","bbb":["a",{"bb":3,"a":6,"ccc":[1,2,3]},["ccc","ddddd"]]}}});
    let result = v.pretty(true, None, None).unwrap_or_default();
    assert_eq!(result, "object -->\n\u{1b}[1m\u{1b}[97m\u{1b}[38;2;80;80;80m| \u{1b}[39maaa = \u{1b}[1m\u{1b}[97m\u{1b}[1m\u{1b}[97m1\u{1b}[39m\u{1b}[0m\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m| \u{1b}[39mb   -->\n\u{1b}[1m\u{1b}[97m\u{1b}[38;2;80;80;80m| | \u{1b}[39ma   = \u{1b}[1m\u{1b}[97m\u{1b}[1m\u{1b}[97maaaa\u{1b}[39m\u{1b}[0m\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m| | \u{1b}[39mbbb -->\n\u{1b}[1m\u{1b}[97m\u{1b}[38;2;80;80;80m| | | \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97ma\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m| | | \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39ma   = \u{1b}[1m\u{1b}[97m\u{1b}[1m\u{1b}[97m6\u{1b}[39m\u{1b}[0m\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m| | |    \u{1b}[39mbb  = \u{1b}[1m\u{1b}[97m\u{1b}[1m\u{1b}[97m3\u{1b}[39m\u{1b}[0m\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m| | |    \u{1b}[39mccc -->\n\u{1b}[1m\u{1b}[97m\u{1b}[38;2;80;80;80m| | |    | \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97m1\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m| | |    | \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97m2\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m| | |    | \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97m3\u{1b}[39m\u{1b}[0m\n\u{1b}[39m\u{1b}[0m\u{1b}[38;2;80;80;80m| | | \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97mccc\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m| | |    \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97mddddd\u{1b}[39m\u{1b}[0m\n\u{1b}[39m\u{1b}[0m\u{1b}[39m\u{1b}[0m\u{1b}[39m\u{1b}[0m".to_string());
  }

  #[test]
  fn value_null() {
    let v: Value = json!(null);
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(result, "null".to_string());
  }

  #[test]
  fn value_bool() {
    let v: Value = json!(false);
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(result, "false".to_string());

    let v: Value = json!(true);
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(result, "true".to_string());
  }

  #[test]
  fn value_string() {
    let v: Value = json!("string");
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(result, "string".to_string());
  }

  #[test]
  fn value_number() {
    let v: Value = json!(5);
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(result, "5".to_string());

    let v: Value = json!(-3.014);
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(result, "-3.014".to_string());
  }

  #[test]
  fn value_number_colored() {
    let v: Value = json!(-874);
    let result = v.pretty(true, None, None).unwrap_or_default();
    assert_eq!(
      result,
      "\u{1b}[1m\u{1b}[97m-874\u{1b}[39m\u{1b}[0m".to_string()
    );
  }

  #[test]
  fn value_number_porefix() {
    let v: Value = json!(842.147854);
    let result = v
      .pretty(false, Some("==> ".to_string()), None)
      .unwrap_or_default();
    assert_eq!(result, "==> 842.147854".to_string());
  }

  #[test]
  fn value_array() {
    let v: Value = json!([1, 2, 3]);
    let result = v.pretty(false, None, None).unwrap_or_default();
    assert_eq!(result, " - 1\n - 2\n - 3\n".to_string());
  }

  #[test]
  fn value_array_color() {
    let v: Value = json!([1, 2, 3]);
    let result = v.pretty(true, None, None).unwrap_or_default();
    assert_eq!(result,"\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97m1\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97m2\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97m3\u{1b}[39m\u{1b}[0m\n".to_string());
  }

  #[test]
  fn value_array_prefix() {
    let v: Value = json!([1, 2, 3]);
    let result = v
      .pretty(false, Some(">>> ".to_string()), None)
      .unwrap_or_default();
    assert_eq!(result, ">>>  - 1\n>>>  - 2\n>>>  - 3\n".to_string());
  }

  #[test]
  fn value_array_prefix_colored() {
    let v: Value = json!([1, 2, 3]);
    let result = v
      .pretty(true, Some(">>> ".to_string()), None)
      .unwrap_or_default();
    assert_eq!(result, "\u{1b}[38;2;80;80;80m>>> \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97m1\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m>>> \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97m2\u{1b}[39m\u{1b}[0m\n\u{1b}[38;2;80;80;80m>>> \u{1b}[39m\u{1b}[38;2;80;80;80m - \u{1b}[39m\u{1b}[1m\u{1b}[97m3\u{1b}[39m\u{1b}[0m\n".to_string());
  }
}