1mod config;
2mod util;
3
4#[cfg(test)]
5mod tests;
6
7pub use config::{ChromaConfig, Color, InlineThreshold, IntegerFormat};
8
9use core::fmt;
10use std::fmt::{Debug, Write};
11
12use pest::{iterators::Pair, Parser};
13use pest_derive::Parser;
14use util::IndentedWriter;
15
16#[derive(Parser)]
17#[grammar = "dbg.pest"]
18struct DbgParser;
19
20pub trait ChromaDebug: Debug {
21 fn dbg_chroma(&self) -> String;
22}
23
24impl<T: Debug> ChromaDebug for T {
25 fn dbg_chroma(&self) -> String {
26 ChromaConfig::DEFAULT.format(self)
27 }
28}
29
30impl ChromaConfig {
31 pub fn format(&self, value: &impl Debug) -> String {
32 self.try_format(value)
33 .unwrap_or_else(|_| format!("{:#?}", value))
34 }
35
36 pub fn try_format(&self, value: &impl Debug) -> Result<String, pest::error::Error<Rule>> {
37 let original = format!("{:?}", value);
38 self.try_format_string(&original)
39 }
40
41 pub fn try_format_string(&self, value: &str) -> Result<String, pest::error::Error<Rule>> {
42 let pairs = DbgParser::parse(Rule::main, value)?;
43 let mut output = String::new();
44 let mut writer = IndentedWriter::new(&mut output);
45 for pair in pairs {
46 self.emit_value(&mut writer, pair);
47 }
48 drop(writer);
49 Ok(output)
50 }
51
52 fn emit_value<W: fmt::Write>(&self, w: &mut IndentedWriter<W>, pair: Pair<'_, Rule>) {
53 match pair.as_rule() {
54 Rule::r#struct => {
55 let mut pairs = pair.into_inner();
56 let name = pairs.next().unwrap().as_str();
57 Self::emit_colored(w, name, self.identifier_color);
58 Self::emit_plain(w, " { ");
59 let inline = self.inline_struct.should_inline(pairs.as_str().len());
60 if !inline {
61 Self::emit_plain(w, "\n");
62 }
63 let fields = pairs.next().unwrap().into_inner();
64 let field_count = fields.len();
65 for (i, field) in fields.enumerate() {
66 let mut field = field.into_inner();
67 let name = field.next().unwrap().as_str();
68 if !inline {
69 w.push_indent();
70 }
71 Self::emit_colored(w, name, self.field_color);
72 Self::emit_plain(w, ": ");
73 self.emit_value(w, field.next().unwrap());
74 if i < field_count - 1 || !inline {
75 Self::emit_plain(w, ", ");
76 } else {
77 Self::emit_plain(w, " ");
78 }
79
80 if !inline {
81 Self::emit_plain(w, "\n");
82 w.pop_indent();
83 }
84 }
85 Self::emit_plain(w, "}");
86 }
87 Rule::map_jsonlike => {
88 let pairs = pair.into_inner();
89 Self::emit_plain(w, "{");
90 let fields = pairs.clone().next().map(|p| p.into_inner());
91 if let Some(fields) = fields {
92 Self::emit_plain(w, " ");
93 let inline = self.inline_struct.should_inline(pairs.as_str().len());
94 if !inline {
95 Self::emit_plain(w, "\n");
96 }
97 let field_count = fields.len();
98 for (i, field) in fields.enumerate() {
99 let mut field = field.into_inner();
100 let name = field.next().unwrap().as_str();
101 if !inline {
102 w.push_indent();
103 }
104 Self::emit_colored(w, name, self.string_color);
105 Self::emit_plain(w, ": ");
106 self.emit_value(w, field.next().unwrap());
107 if i < field_count - 1 || !inline {
108 Self::emit_plain(w, ", ");
109 } else {
110 Self::emit_plain(w, " ");
111 }
112
113 if !inline {
114 Self::emit_plain(w, "\n");
115 w.pop_indent();
116 }
117 }
118 }
119 Self::emit_plain(w, "}");
120 }
121 Rule::tuple_struct => {
122 let mut pairs = pair.into_inner();
123 let name = pairs.next().unwrap().as_str();
124 Self::emit_colored(w, name, self.identifier_color);
125 Self::emit_plain(w, "(");
126 let fields = pairs.next().unwrap().into_inner();
127 let field_count = fields.len();
128 for (i, field) in fields.enumerate() {
129 self.emit_value(w, field);
130 if i < field_count - 1 {
131 Self::emit_plain(w, ", ");
132 }
133 }
134 Self::emit_plain(w, ")");
135 }
136 Rule::bitflags_struct => {
137 let mut pairs = pair.into_inner();
138 let name = pairs.next().unwrap().as_str();
139 Self::emit_colored(w, name, self.identifier_color);
140 Self::emit_plain(w, "(");
141 let fields = pairs.next().unwrap().into_inner();
142 let field_count = fields.len();
143 for (i, field) in fields.enumerate() {
144 self.emit_value(w, field);
145 if i < field_count - 1 {
146 Self::emit_plain(w, " | ");
147 }
148 }
149 Self::emit_plain(w, ")");
150 }
151 Rule::number => {
152 let num = pair.into_inner().next().unwrap();
153 match num.as_rule() {
154 Rule::integer => {
155 if let Ok(num) = num.as_str().parse::<u64>() {
157 Self::emit_colored(
158 w,
159 self.integer_format.format(num).as_str(),
160 self.numerical_color,
161 );
162 } else {
163 Self::emit_colored(w, num.as_str(), self.numerical_color);
164 }
165 }
166 _ => {
168 Self::emit_colored(w, num.as_str(), self.numerical_color);
169 }
170 }
171 }
172 Rule::string => {
173 Self::emit_colored(w, "\"", self.string_color);
174 let chars = pair.into_inner();
175 for char in chars {
176 let char = char.into_inner().next().unwrap();
177 match char.as_rule() {
178 Rule::escape_sequence => {
179 Self::emit_colored(w, char.as_str(), self.string_escape_color);
180 }
181 _ => {
182 Self::emit_colored(w, char.as_str(), self.string_color);
183 }
184 }
185 }
186 Self::emit_colored(w, "\"", self.string_color);
187 }
188 Rule::char_literal => {
189 Self::emit_colored(w, "'", self.string_color);
190 let char = pair.into_inner().next().unwrap();
191 println!("{:?}", char.as_rule());
192 if char.as_rule() == Rule::char {
193 let char = char.into_inner().next().unwrap();
194 match char.as_rule() {
195 Rule::escape_sequence => {
196 Self::emit_colored(w, char.as_str(), self.string_escape_color);
197 }
198 _ => {
199 Self::emit_colored(w, char.as_str(), self.string_color);
200 }
201 }
202 }
203 Self::emit_colored(w, "'", self.string_color);
204 }
205 Rule::enum_variant => {
206 Self::emit_colored(w, pair.as_str(), self.identifier_color);
207 }
208 Rule::array => {
209 Self::emit_plain(w, "[");
210 let inline = self.inline_array.should_inline(pair.as_str().len());
211 let elements = pair.into_inner();
212 let element_count = elements.len();
213 for (i, field) in elements.enumerate() {
214 if i == 0 {
215 if inline {
216 Self::emit_plain(w, " ");
217 } else {
218 Self::emit_plain(w, "\n");
219 }
220 }
221 if !inline {
222 w.push_indent();
223 }
224 self.emit_value(w, field);
225 if i < element_count - 1 || !inline {
226 Self::emit_plain(w, ", ");
227 } else {
228 Self::emit_plain(w, " ");
229 }
230
231 if !inline {
232 Self::emit_plain(w, "\n");
233 w.pop_indent();
234 }
235 }
236 Self::emit_plain(w, "]");
237 }
238 Rule::boolean => {
239 Self::emit_colored(w, pair.as_str(), self.numerical_color);
240 }
241 _ => {
242 let value = pair.as_str();
243 Self::emit_plain(w, value);
244 }
245 }
246 }
247
248 fn emit_plain<W: fmt::Write>(w: &mut IndentedWriter<W>, s: &str) {
249 w.write_str(s).ok();
250 }
251
252 fn emit_colored<W: fmt::Write>(w: &mut IndentedWriter<W>, s: &str, color: Color) {
253 let style = anstyle::Style::new().fg_color(Some(anstyle::Color::Rgb(color.into())));
254 let reset = anstyle::Reset;
255 write!(w, "{style}{s}{reset}").ok();
256 }
257}