1use crate::{coloring, Meta, PrettyPrint};
2use miette::Result;
3use owo_colors::OwoColorize;
4use pad::PadStr;
5use serde_json::Value;
6use std::fmt::Write;
7use unicode_width::UnicodeWidthStr;
8
9impl PrettyPrint for Value {
10 fn meta(&self) -> Meta {
11 Meta {
12 padding: 0,
13 separator: None,
14 fields: vec![],
15 }
16 }
17 fn pretty(&self, colored: bool, prefix: Option<String>, _profile: Option<&str>) -> Result<String> {
18 let Meta { separator, .. } = self.meta();
19
20 let separator = separator.unwrap_or("= ");
21
22 let prefix_ = if let Some(prefix) = &prefix {
23 if colored {
24 prefix.truecolor(80, 80, 80).to_string()
25 } else {
26 prefix.to_owned()
27 }
28 } else {
29 "".into()
30 };
31 let prefix = &prefix.unwrap_or_default();
32
33 let (v, should_color, should_prefix) = match self {
34 Value::Null => ("null".to_string(), true, true),
35 Value::Bool(b) => (b.to_string(), true, true),
36 Value::Number(n) => (n.to_string(), true, true),
37 Value::String(s) => (s.to_owned(), true, true),
38 Value::Array(vv) => (
39 vv.iter().fold(String::new(), |mut output, item| {
40 let end = match item {
41 Value::Array(_) | Value::Object(_) => "",
42 _ => "\n",
43 };
44 let _ = write!(
45 output,
46 "{prefix_}{}{end}",
47 item
48 .pretty(colored, Some(prefix.clone() + " "), _profile)
49 .unwrap_or_default()
50 .replacen(&(prefix.clone() + " "), " - ", 1)
51 );
52 output
53 }),
54 false,
55 false,
56 ),
57 Value::Object(o) => (
58 {
59 let padding = 1 + o.keys().map(|k| k.width()).max().unwrap_or_default();
60
61 o.iter()
62 .enumerate()
63 .map(|(_i, (k, v))| {
64 let separator = match v {
65 Value::Array(_) | Value::Object(_) => "-->\n",
66 _ => separator,
67 };
68 let end = match v {
69 Value::Array(_) | Value::Object(_) => "",
70 _ => "\n",
71 };
72 let v = v
73 .pretty(
74 colored,
75 match v {
76 Value::Array(_) | Value::Object(_) => Some(prefix.clone() + "| "),
77 _ => None,
78 },
79 _profile,
80 )
81 .unwrap_or_default();
82 if colored {
83 let v = coloring(v, &None);
84 format!("{prefix_}{}{separator}{v}{end}", k.pad_to_width(padding))
85 } else {
86 format!("{prefix}{}{separator}{v}{end}", k.pad_to_width(padding))
87 }
88 })
89 .collect()
90 },
91 false,
92 false,
93 ),
94 };
95
96 let prefix_ = if should_prefix { prefix_ } else { "".into() };
97
98 let pretty_hashmap = if colored && should_color {
99 let v = coloring(v, &None);
100 format!("{prefix_}{v}")
101 } else {
102 format!("{prefix_}{v}")
103 };
104
105 Ok(pretty_hashmap)
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use crate::PrettyPrint;
112 use serde_json::{json, Value};
113
114 #[test]
115 fn value_object() {
116 let v: Value = json!({});
117 let result = v.pretty(false, None, None).unwrap_or_default();
118 assert_eq!(result, "".to_string());
119
120 let v: Value = json!({"a":1});
121 let result = v.pretty(false, None, None).unwrap_or_default();
122 assert_eq!(result, "a = 1\n".to_string());
123
124 let v: Value = json!({"number":1,"bool":true,"string":"string"});
125 let result = v.pretty(false, None, None).unwrap_or_default();
126 assert_eq!(
127 result,
128 "bool = true\nnumber = 1\nstring = string\n".to_string()
129 );
130 let v: Value = json!({"array":[1, 3, 4]});
131 let result = v.pretty(false, None, None).unwrap_or_default();
132 assert_eq!(result, "array -->\n| - 1\n| - 3\n| - 4\n".to_string());
133
134 let v: Value = json!({"object":{"aaa":1,"b":2}});
135 let result = v.pretty(false, None, None).unwrap_or_default();
136 assert_eq!(result, "object -->\n| aaa = 1\n| b = 2\n".to_string());
137
138 let v: Value = json!({"object":{"aaa":1,"b":{"a":"aaaa","bbb":["a","bb","ccc"]}}});
139 let result = v.pretty(false, None, None).unwrap_or_default();
140 assert_eq!(result, "object -->\n| aaa = 1\n| b -->\n| | a = aaaa\n| | bbb -->\n| | | - a\n| | | - bb\n| | | - ccc\n".to_string());
141
142 let v: Value = json!({"array":[{"a":1,"bb":2},{"b":2}]});
143 let result = v.pretty(false, None, None).unwrap_or_default();
144 assert_eq!(
145 result,
146 "array -->\n| - a = 1\n| bb = 2\n| - b = 2\n".to_string()
147 );
148 }
149
150 #[test]
151 fn value_object_colored() {
152 let v: Value = json!({});
153 let result = v.pretty(true, None, None).unwrap_or_default();
154 assert_eq!(result, "".to_string());
155
156 let v: Value = json!({"a":1});
157 let result = v.pretty(true, None, None).unwrap_or_default();
158 assert_eq!(
159 result,
160 "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"
161 .to_string()
162 );
163
164 let v: Value = json!({"number":1,"bool":true,"string":"string"});
165 let result = v.pretty(true, None, None).unwrap_or_default();
166 assert_eq!(
167 result,
168 "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()
169 );
170 let v: Value = json!({"array":[1, 3, 4]});
171 let result = v.pretty(true, None, None).unwrap_or_default();
172 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());
173
174 let v: Value = json!({"object":{"aaa":1,"b":2}});
175 let result = v.pretty(true, None, None).unwrap_or_default();
176 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());
177
178 let v: Value = json!({"object":{"aaa":1,"b":{"a":"aaaa","bbb":["a",{"bb":3,"a":6,"ccc":[1,2,3]},["ccc","ddddd"]]}}});
179 let result = v.pretty(true, None, None).unwrap_or_default();
180 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());
181 }
182
183 #[test]
184 fn value_null() {
185 let v: Value = json!(null);
186 let result = v.pretty(false, None, None).unwrap_or_default();
187 assert_eq!(result, "null".to_string());
188 }
189
190 #[test]
191 fn value_bool() {
192 let v: Value = json!(false);
193 let result = v.pretty(false, None, None).unwrap_or_default();
194 assert_eq!(result, "false".to_string());
195
196 let v: Value = json!(true);
197 let result = v.pretty(false, None, None).unwrap_or_default();
198 assert_eq!(result, "true".to_string());
199 }
200
201 #[test]
202 fn value_string() {
203 let v: Value = json!("string");
204 let result = v.pretty(false, None, None).unwrap_or_default();
205 assert_eq!(result, "string".to_string());
206 }
207
208 #[test]
209 fn value_number() {
210 let v: Value = json!(5);
211 let result = v.pretty(false, None, None).unwrap_or_default();
212 assert_eq!(result, "5".to_string());
213
214 let v: Value = json!(-3.014);
215 let result = v.pretty(false, None, None).unwrap_or_default();
216 assert_eq!(result, "-3.014".to_string());
217 }
218
219 #[test]
220 fn value_number_colored() {
221 let v: Value = json!(-874);
222 let result = v.pretty(true, None, None).unwrap_or_default();
223 assert_eq!(
224 result,
225 "\u{1b}[1m\u{1b}[97m-874\u{1b}[39m\u{1b}[0m".to_string()
226 );
227 }
228
229 #[test]
230 fn value_number_porefix() {
231 let v: Value = json!(842.147854);
232 let result = v
233 .pretty(false, Some("==> ".to_string()), None)
234 .unwrap_or_default();
235 assert_eq!(result, "==> 842.147854".to_string());
236 }
237
238 #[test]
239 fn value_array() {
240 let v: Value = json!([1, 2, 3]);
241 let result = v.pretty(false, None, None).unwrap_or_default();
242 assert_eq!(result, " - 1\n - 2\n - 3\n".to_string());
243 }
244
245 #[test]
246 fn value_array_color() {
247 let v: Value = json!([1, 2, 3]);
248 let result = v.pretty(true, None, None).unwrap_or_default();
249 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());
250 }
251
252 #[test]
253 fn value_array_prefix() {
254 let v: Value = json!([1, 2, 3]);
255 let result = v
256 .pretty(false, Some(">>> ".to_string()), None)
257 .unwrap_or_default();
258 assert_eq!(result, ">>> - 1\n>>> - 2\n>>> - 3\n".to_string());
259 }
260
261 #[test]
262 fn value_array_prefix_colored() {
263 let v: Value = json!([1, 2, 3]);
264 let result = v
265 .pretty(true, Some(">>> ".to_string()), None)
266 .unwrap_or_default();
267 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());
268 }
269}