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