use std::io::prelude::*;
use std::io::BufReader;
use std::io::BufWriter;
use std::io::Error;
const BUF_SIZE: usize = 1024 * 16;
const C_CR: u8 = '\r' as u8;
const C_LF: u8 = '\n' as u8;
const C_TAB: u8 = '\t' as u8;
const C_SPACE: u8 = ' ' as u8;
const C_COMMA: u8 = ',' as u8;
const C_COLON: u8 = ':' as u8;
const C_QUOTE: u8 = '"' as u8;
const C_BACKSLASH: u8 = '\\' as u8;
const C_LEFT_BRACE: u8 = '{' as u8;
const C_LEFT_BRACKET: u8 = '[' as u8;
const C_RIGHT_BRACE: u8 = '}' as u8;
const C_RIGHT_BRACKET: u8 = ']' as u8;
pub struct Formatter {
pub indent: String,
pub line_separator: String,
pub record_separator: String,
pub after_colon: String,
pub trailing_output: String,
depth: usize, in_string: bool, in_backslash: bool, empty: bool, first: bool, }
impl Formatter {
fn default() -> Formatter {
Formatter {
indent: String::from(" "),
line_separator: String::from("\n"),
record_separator: String::from("\n"),
after_colon: String::from(" "),
trailing_output: String::from(""),
depth: 0,
in_string: false,
in_backslash: false,
empty: false,
first: true,
}
}
pub fn pretty_printer() -> Formatter {
return Formatter::default();
}
pub fn minimizer() -> Formatter {
let mut xf = Formatter::default();
xf.indent = String::from("");
xf.line_separator = String::from("");
xf.record_separator = String::from("\n");
xf.after_colon = String::from("");
return xf;
}
pub fn format(&mut self, json_string: &str) -> Result<String, String> {
let mut input = json_string.as_bytes();
let mut output: Vec<u8> = vec![];
match self.format_stream(&mut input, &mut output) {
Ok(_) => {}
Err(f) => {
return Err(f.to_string());
}
};
let output_string = match String::from_utf8(output) {
Ok(s) => s,
Err(f) => {
return Err(f.to_string());
}
};
return Ok(output_string);
}
pub fn format_stream(
&mut self,
input: &mut dyn Read,
output: &mut dyn Write,
) -> Result<(), Error> {
let mut reader = BufReader::new(input);
let mut writer = BufWriter::new(output);
let mut buf = [0 as u8; BUF_SIZE];
loop {
match reader.read(&mut buf) {
Ok(0) => {
break;
}
Ok(n) => {
self.format_buf(&buf[0..n], &mut writer)?;
}
Err(e) => {
return Err(e);
}
}
}
writer.write(self.trailing_output.as_bytes())?;
return Ok(());
}
fn format_buf(&mut self, buf: &[u8], writer: &mut dyn Write) -> Result<(), Error> {
for n in 0..buf.len() {
let b = buf[n];
if self.in_string {
writer.write(&buf[n..n + 1])?;
if self.in_backslash {
self.in_backslash = false;
} else if b == C_QUOTE {
self.in_string = false;
} else if b == C_BACKSLASH {
self.in_backslash = true;
}
} else {
match b {
C_SPACE | C_LF | C_CR | C_TAB => {
}
C_LEFT_BRACKET | C_LEFT_BRACE => {
if self.first {
self.first = false;
writer.write(&buf[n..n + 1])?;
} else if self.empty {
writer.write(self.line_separator.as_bytes())?;
for _ in 0..self.depth {
writer.write(self.indent.as_bytes())?;
}
writer.write(&buf[n..n + 1])?;
} else if self.depth == 0 {
writer.write(self.record_separator.as_bytes())?;
writer.write(&buf[n..n + 1])?;
} else {
writer.write(&buf[n..n + 1])?;
}
self.depth += 1;
self.empty = true;
}
C_RIGHT_BRACKET | C_RIGHT_BRACE => {
self.depth = self.depth.saturating_sub(1);
if self.empty {
self.empty = false;
writer.write(&buf[n..n + 1])?;
} else {
writer.write(self.line_separator.as_bytes())?;
for _ in 0..self.depth {
writer.write(self.indent.as_bytes())?;
}
writer.write(&buf[n..n + 1])?;
}
}
C_COMMA => {
writer.write(&buf[n..n + 1])?;
writer.write(self.line_separator.as_bytes())?;
for _ in 0..self.depth {
writer.write(self.indent.as_bytes())?;
}
}
C_COLON => {
writer.write(&buf[n..n + 1])?;
writer.write(self.after_colon.as_bytes())?;
}
_ => {
if self.empty {
writer.write(self.line_separator.as_bytes())?;
for _ in 0..self.depth {
writer.write(self.indent.as_bytes())?;
}
self.empty = false;
}
if b == C_QUOTE {
self.in_string = true;
}
writer.write(&buf[n..n + 1])?;
}
};
};
}
return Ok(());
}
}
pub fn pretty_print(json_string: &str) -> Result<String, String> {
Formatter::pretty_printer().format(json_string)
}
pub fn pretty_print_stream(input: &mut dyn Read, output: &mut dyn Write) -> Result<(), Error> {
Formatter::pretty_printer().format_stream(input, output)
}
pub fn minimize(json_string: &str) -> Result<String, String> {
Formatter::minimizer().format(json_string)
}
pub fn minimize_stream(input: &mut dyn Read, output: &mut dyn Write) -> Result<(), Error> {
Formatter::minimizer().format_stream(input, output)
}