blkar_lib/
json_printer.rs1use crate::json_utils::split_key_val_pair;
2use crate::misc_utils::to_camelcase;
3use crate::output_channel::OutputChannel;
4use smallvec::SmallVec;
5use std::fmt;
6use std::sync::Mutex;
7
8#[derive(Copy, Clone, PartialEq, Debug)]
9pub enum BracketType {
10 Curly,
11 Square,
12}
13
14#[derive(Clone, Debug)]
15struct JSONContext {
16 first_item: bool,
17 bracket_type: BracketType,
18}
19
20impl JSONContext {
21 pub fn new(bracket_type: BracketType) -> JSONContext {
22 JSONContext {
23 first_item: true,
24 bracket_type,
25 }
26 }
27}
28
29#[derive(Debug)]
30pub struct JSONPrinter {
31 json_enabled: bool,
32 output_channel: OutputChannel,
33 contexts: Mutex<SmallVec<[JSONContext; 8]>>,
34}
35
36impl Clone for JSONPrinter {
37 fn clone(&self) -> Self {
38 JSONPrinter {
39 json_enabled: self.json_enabled,
40 output_channel: self.output_channel,
41 contexts: Mutex::new(self.contexts.lock().unwrap().clone()),
42 }
43 }
44}
45
46fn write_comma_if_not_first(f: &mut fmt::Formatter, context: &mut JSONContext) -> fmt::Result {
47 if !context.first_item {
48 write!(f, ",")?;
49 }
50 context.first_item = false;
51
52 Ok(())
53}
54
55fn bracket_type_to_str_open(bracket_type: BracketType) -> &'static str {
56 match bracket_type {
57 BracketType::Curly => "{",
58 BracketType::Square => "[",
59 }
60}
61
62fn bracket_type_to_str_close(bracket_type: BracketType) -> &'static str {
63 match bracket_type {
64 BracketType::Curly => "}",
65 BracketType::Square => "]",
66 }
67}
68
69impl JSONPrinter {
70 pub fn new(json_enabled: bool, output_channel: OutputChannel) -> JSONPrinter {
71 JSONPrinter {
72 json_enabled,
73 output_channel,
74 contexts: Mutex::new(SmallVec::new()),
75 }
76 }
77
78 pub fn json_enabled(&self) -> bool {
79 self.json_enabled
80 }
81
82 pub fn output_channel(&self) -> OutputChannel {
83 self.output_channel
84 }
85
86 pub fn set_output_channel(&mut self, output_channel: OutputChannel) {
87 self.output_channel = output_channel
88 }
89
90 pub fn first_item(&self) -> bool {
91 self.contexts.lock().unwrap().last().unwrap().first_item
92 }
93
94 fn print_comma_if_not_first(&self, context: &mut JSONContext) {
95 if !context.first_item {
96 print_at_output_channel!(self.output_channel => ",");
97 }
98 context.first_item = false;
99 }
100
101 pub fn print_open_bracket(&self, name: Option<&str>, bracket_type: BracketType) {
102 if !self.json_enabled {
103 return;
104 }
105
106 match self.contexts.lock().unwrap().last_mut() {
107 None => {}
108 Some(x) => self.print_comma_if_not_first(x),
109 }
110
111 match name {
112 None => {}
113 Some(n) => print_at_output_channel!(self.output_channel => "\"{}\": ", to_camelcase(n)),
114 }
115
116 println_at_output_channel!(self.output_channel => "{}", bracket_type_to_str_open(bracket_type));
117
118 self.contexts
119 .lock()
120 .unwrap()
121 .push(JSONContext::new(bracket_type));
122 }
123
124 pub fn write_open_bracket(
125 &self,
126 f: &mut fmt::Formatter,
127 name: Option<&str>,
128 bracket_type: BracketType,
129 ) -> fmt::Result {
130 if !self.json_enabled {
131 return Ok(());
132 }
133
134 match self.contexts.lock().unwrap().last_mut() {
135 None => {}
136 Some(x) => write_comma_if_not_first(f, x)?,
137 }
138
139 match name {
140 None => {}
141 Some(n) => write!(f, "\"{}\": ", to_camelcase(n))?,
142 }
143
144 writeln!(f, "{}", bracket_type_to_str_open(bracket_type))?;
145
146 self.contexts
147 .lock()
148 .unwrap()
149 .push(JSONContext::new(bracket_type));
150
151 Ok(())
152 }
153
154 pub fn print_close_bracket(&self) {
155 if !self.json_enabled {
156 return;
157 }
158
159 let context = self.contexts.lock().unwrap().pop().unwrap();
160
161 println_at_output_channel!(self.output_channel => "{}", bracket_type_to_str_close(context.bracket_type));
162 }
163
164 pub fn write_close_bracket(&self, f: &mut fmt::Formatter) -> fmt::Result {
165 if !self.json_enabled {
166 return Ok(());
167 }
168
169 let context = self.contexts.lock().unwrap().pop().unwrap();
170
171 writeln!(f, "{}", bracket_type_to_str_close(context.bracket_type))
172 }
173
174 pub fn print_maybe_json(&self, force_quotes: bool, msg: &str) {
175 if self.json_enabled {
176 let mut contexts = self.contexts.lock().unwrap();
177 let context = contexts.last_mut().unwrap();
178
179 let (l, r): (&str, &str) = split_key_val_pair(&msg);
180
181 print_json_field!(self.output_channel => l, r, force_quotes, context.first_item);
182
183 context.first_item = false;
184 } else {
185 println_at_output_channel!(self.output_channel => "{}", msg);
186 }
187 }
188
189 pub fn write_maybe_json(
190 &self,
191 f: &mut fmt::Formatter,
192 force_quotes: bool,
193 msg: &str,
194 ) -> fmt::Result {
195 if self.json_enabled {
196 let mut contexts = self.contexts.lock().unwrap();
197 let context = contexts.last_mut().unwrap();
198
199 let (l, r): (&str, &str) = split_key_val_pair(&msg);
200
201 write_json_field!(f, l, r, force_quotes, context.first_item)?;
202
203 context.first_item = false;
204
205 Ok(())
206 } else {
207 writeln!(f, "{}", msg)
208 }
209 }
210}