1use serde_json::Value;
4use crate::console::{ConsoleOptions, RenderResult, Renderable};
5use crate::segment::Segment;
6use crate::style::Style;
7use crate::theme::Theme;
8
9pub fn render_json(value: &Value) -> JsonRender {
11 JsonRender {
12 value: value.clone(),
13 indent: 2,
14 theme: None,
15 highlight: true,
16 }
17}
18
19#[derive(Debug, Clone)]
20pub struct JsonRender {
21 value: Value,
22 indent: usize,
23 theme: Option<Theme>,
24 highlight: bool,
25}
26
27impl JsonRender {
28 pub fn indent(mut self, n: usize) -> Self { self.indent = n; self }
29 pub fn theme(mut self, t: Theme) -> Self { self.theme = Some(t); self }
30
31 fn style_for(&self, name: &str) -> Style {
32 if let Some(ref t) = self.theme {
33 t.get(name).cloned().unwrap_or(Style::new())
34 } else {
35
36 let theme = crate::theme::default_theme();
37 theme.get(name).cloned().unwrap_or(Style::new())
38 }
39 }
40}
41
42impl Renderable for JsonRender {
43 fn render(&self, _options: &ConsoleOptions) -> RenderResult {
44 let formatted = format_json_value(
45 &self.value,
46 self.indent,
47 0,
48 self.highlight,
49 &|name| self.style_for(name),
50 );
51 let lines: Vec<Vec<Segment>> = formatted
52 .lines()
53 .map(|line| vec![Segment::new(line)])
54 .collect();
55 RenderResult { lines, items: Vec::new() }
56 }
57}
58
59fn format_json_value(
60 value: &Value,
61 indent: usize,
62 level: usize,
63 highlight: bool,
64 get_style: &dyn Fn(&str) -> Style,
65) -> String {
66 if !highlight {
67 return serde_json::to_string_pretty(value).unwrap_or_else(|_| format!("{value:?}"));
68 }
69
70 match value {
71 Value::Null => {
72 let s = get_style("json.null");
73 format!("{}{}null{}", s.to_ansi(), s.reset_ansi(), "")
74 }
75 Value::Bool(b) => {
76 let s = get_style("json.bool");
77 format!("{}{b}{}", s.to_ansi(), s.reset_ansi())
78 }
79 Value::Number(n) => {
80 let s = get_style("json.number");
81 format!("{}{n}{}", s.to_ansi(), s.reset_ansi())
82 }
83 Value::String(s) => {
84 let st = get_style("json.str");
85 format!("{}\"{}\"{}", st.to_ansi(), s.escape_default(), st.reset_ansi())
86 }
87 Value::Array(arr) => {
88 if arr.is_empty() {
89 return "[]".to_string();
90 }
91 let brace = get_style("json.brace");
92 let mut out = format!("{}[{}", brace.to_ansi(), brace.reset_ansi());
93 let pad = " ".repeat(indent * (level + 1));
94 let close_pad = " ".repeat(indent * level);
95
96 for (i, item) in arr.iter().enumerate() {
97 let item_str = format_json_value(item, indent, level + 1, highlight, get_style);
98 out.push('\n');
99 out.push_str(&pad);
100 out.push_str(&item_str);
101 if i < arr.len() - 1 {
102 out.push(',');
103 }
104 }
105 out.push('\n');
106 out.push_str(&close_pad);
107 out.push_str(&format!("{}]{}", brace.to_ansi(), brace.reset_ansi()));
108 out
109 }
110 Value::Object(obj) => {
111 if obj.is_empty() {
112 return "{}".to_string();
113 }
114 let brace = get_style("json.brace");
115 let key_style = get_style("json.key");
116 let mut out = format!("{}{{{}", brace.to_ansi(), brace.reset_ansi());
117 let pad = " ".repeat(indent * (level + 1));
118 let close_pad = " ".repeat(indent * level);
119 let keys: Vec<&String> = obj.keys().collect();
120
121 for (i, key) in keys.iter().enumerate() {
122 let val = &obj[*key];
123 let val_str = format_json_value(val, indent, level + 1, highlight, get_style);
124 out.push('\n');
125 out.push_str(&pad);
126 out.push_str(&format!(
127 "{}\"{}\"{}: ",
128 key_style.to_ansi(),
129 key.escape_default(),
130 key_style.reset_ansi()
131 ));
132 out.push_str(&val_str);
133 if i < keys.len() - 1 {
134 out.push(',');
135 }
136 }
137 out.push('\n');
138 out.push_str(&close_pad);
139 out.push_str(&format!("{}}}{}", brace.to_ansi(), brace.reset_ansi()));
140 out
141 }
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn test_format_json() {
151 let v: Value = serde_json::from_str(r#"{"name": "Alice", "age": 30}"#).unwrap();
152 let rendered = render_json(&v);
153 let result = rendered.render(&ConsoleOptions::default());
154 assert!(result.to_ansi().contains("Alice"));
155 }
156}