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