blkar_lib/
json_printer.rs

1use 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}