mirror of
https://github.com/Noratrieb/jsonformat.git
synced 2026-01-14 14:15:03 +01:00
Refactor using a buffer for reading and a buffer for writing
This commit is contained in:
parent
db2be0a36c
commit
f8c49ca661
4 changed files with 79 additions and 34 deletions
18
Cargo.lock
generated
18
Cargo.lock
generated
|
|
@ -1,5 +1,7 @@
|
||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
|
@ -9,6 +11,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
|
|
@ -242,6 +250,7 @@ version = "1.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"criterion",
|
"criterion",
|
||||||
|
"utf8-chars",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -519,6 +528,15 @@ version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8-chars"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c1348d8face79d019be7cbc0198e36bf93e160ddbfaa7bb54c9592627b9ec841"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "vec_map"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ categories = ["command-line-utilities"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version= "2.33.3", optional = true }
|
clap = { version= "2.33.3", optional = true }
|
||||||
|
utf8-chars = "1.0.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
|
|
|
||||||
58
src/lib.rs
58
src/lib.rs
|
|
@ -3,6 +3,10 @@
|
||||||
//!
|
//!
|
||||||
//! It does not do anything more than that, which makes it so fast.
|
//! It does not do anything more than that, which makes it so fast.
|
||||||
|
|
||||||
|
use std::io::{BufReader, BufWriter, Write};
|
||||||
|
use utf8_chars::BufReadCharsExt;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Set the indentation used for the formatting.
|
/// Set the indentation used for the formatting.
|
||||||
///
|
///
|
||||||
|
|
@ -24,16 +28,32 @@ pub enum Indentation<'a> {
|
||||||
/// The default indentation is faster than a custom one
|
/// The default indentation is faster than a custom one
|
||||||
///
|
///
|
||||||
pub fn format_json(json: &str, indentation: Indentation) -> String {
|
pub fn format_json(json: &str, indentation: Indentation) -> String {
|
||||||
// at least as big as the input to avoid resizing
|
let mut reader = BufReader::new(json.as_bytes());
|
||||||
// this might be too big if the input string is formatted in a weird way, but that's not expected, and it will still be efficient
|
let mut writer = BufWriter::new(Vec::new());
|
||||||
let mut out = String::with_capacity(json.len());
|
|
||||||
|
format_json_buffered(&mut reader, &mut writer, indentation).unwrap();
|
||||||
|
String::from_utf8(writer.into_inner().unwrap()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// # Formats a json string
|
||||||
|
///
|
||||||
|
/// The indentation can be set to any value using [Indentation](jsonformat::Indentation)
|
||||||
|
/// The default value is two spaces
|
||||||
|
/// The default indentation is faster than a custom one
|
||||||
|
///
|
||||||
|
pub fn format_json_buffered<R, W>(reader: &mut BufReader<R>, writer: &mut BufWriter<W>, indentation: Indentation) -> Result<(), Box<dyn Error>>
|
||||||
|
where
|
||||||
|
R: std::io::Read,
|
||||||
|
W: std::io::Write {
|
||||||
|
|
||||||
let mut escaped = false;
|
let mut escaped = false;
|
||||||
let mut in_string = false;
|
let mut in_string = false;
|
||||||
let mut indent_level = 0usize;
|
let mut indent_level = 0usize;
|
||||||
let mut newline_requested = false; // invalidated if next character is ] or }
|
let mut newline_requested = false; // invalidated if next character is ] or }
|
||||||
|
|
||||||
for char in json.chars() {
|
for char in reader.chars() {
|
||||||
|
let char = char?;
|
||||||
if in_string {
|
if in_string {
|
||||||
let mut escape_here = false;
|
let mut escape_here = false;
|
||||||
match char {
|
match char {
|
||||||
|
|
@ -49,7 +69,7 @@ pub fn format_json(json: &str, indentation: Indentation) -> String {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
out.push(char);
|
writer.write_all(char.encode_utf8(&mut [0; 4]).as_bytes())?;
|
||||||
escaped = escape_here;
|
escaped = escape_here;
|
||||||
} else {
|
} else {
|
||||||
let mut auto_push = true;
|
let mut auto_push = true;
|
||||||
|
|
@ -71,14 +91,14 @@ pub fn format_json(json: &str, indentation: Indentation) -> String {
|
||||||
indent_level = indent_level.saturating_sub(1);
|
indent_level = indent_level.saturating_sub(1);
|
||||||
if !newline_requested {
|
if !newline_requested {
|
||||||
// see comment below about newline_requested
|
// see comment below about newline_requested
|
||||||
out.push('\n');
|
writeln!(writer)?;
|
||||||
indent(&mut out, indent_level, indentation);
|
indent_buffered(writer, indent_level, indentation)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
':' => {
|
':' => {
|
||||||
auto_push = false;
|
auto_push = false;
|
||||||
out.push(char);
|
writer.write_all(char.encode_utf8(&mut [0; 4]).as_bytes())?;
|
||||||
out.push(' ');
|
writer.write_all(" ".as_bytes())?;
|
||||||
}
|
}
|
||||||
',' => {
|
',' => {
|
||||||
request_newline = true;
|
request_newline = true;
|
||||||
|
|
@ -89,33 +109,37 @@ pub fn format_json(json: &str, indentation: Indentation) -> String {
|
||||||
// newline only happens after { [ and ,
|
// newline only happens after { [ and ,
|
||||||
// this means we can safely assume that it being followed up by } or ]
|
// this means we can safely assume that it being followed up by } or ]
|
||||||
// means an empty object/array
|
// means an empty object/array
|
||||||
out.push('\n');
|
writeln!(writer)?;
|
||||||
indent(&mut out, old_level, indentation);
|
indent_buffered(writer, old_level, indentation)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if auto_push {
|
if auto_push {
|
||||||
out.push(char);
|
writer.write_all(char.encode_utf8(&mut [0; 4]).as_bytes())?;
|
||||||
}
|
}
|
||||||
|
|
||||||
newline_requested = request_newline;
|
newline_requested = request_newline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn indent(buf: &mut String, level: usize, indent_str: Indentation) {
|
fn indent_buffered<W>(writer: &mut BufWriter<W>, level: usize, indent_str: Indentation) -> Result<(), Box<dyn Error>>
|
||||||
|
where
|
||||||
|
W: std::io::Write {
|
||||||
for _ in 0..level {
|
for _ in 0..level {
|
||||||
match indent_str {
|
match indent_str {
|
||||||
Indentation::Default => {
|
Indentation::Default => {
|
||||||
buf.push(' ');
|
writer.write_all(" ".as_bytes())?;
|
||||||
buf.push(' ');
|
writer.write_all(" ".as_bytes())?;
|
||||||
}
|
}
|
||||||
Indentation::Custom(indent) => {
|
Indentation::Custom(indent) => {
|
||||||
buf.push_str(indent);
|
writer.write_all(indent.as_bytes())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
||||||
36
src/main.rs
36
src/main.rs
|
|
@ -1,10 +1,10 @@
|
||||||
use clap::clap_app;
|
use clap::clap_app;
|
||||||
use jsonformat::{format_json, Indentation};
|
use jsonformat::{Indentation, format_json_buffered};
|
||||||
use std::fs;
|
use std::error::Error;
|
||||||
use std::io;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::{BufReader, BufWriter, Read, Write};
|
||||||
|
|
||||||
fn main() -> Result<(), io::Error> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
let matches = clap_app!(jsonformat =>
|
let matches = clap_app!(jsonformat =>
|
||||||
(version: "1.1")
|
(version: "1.1")
|
||||||
(author: "nilstrieb <nilstrieb@gmail.com>")
|
(author: "nilstrieb <nilstrieb@gmail.com>")
|
||||||
|
|
@ -16,13 +16,13 @@ fn main() -> Result<(), io::Error> {
|
||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let str = match matches.value_of("input") {
|
let reader: Box<dyn Read> = match matches.value_of("input") {
|
||||||
Some(path) => fs::read_to_string(path)?,
|
Some(path) => {
|
||||||
|
let f = File::open(path)?;
|
||||||
|
Box::new(BufReader::new(f))
|
||||||
|
},
|
||||||
None => {
|
None => {
|
||||||
let mut buf = String::new();
|
Box::new(std::io::stdin())
|
||||||
let stdin = std::io::stdin();
|
|
||||||
stdin.lock().read_to_string(&mut buf)?;
|
|
||||||
buf
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -41,8 +41,6 @@ fn main() -> Result<(), io::Error> {
|
||||||
None => Indentation::Default,
|
None => Indentation::Default,
|
||||||
};
|
};
|
||||||
|
|
||||||
let formatted = format_json(&str, indent);
|
|
||||||
|
|
||||||
let mut output = matches.value_of("output");
|
let mut output = matches.value_of("output");
|
||||||
let mut windows_output_default_file: Option<String> = None;
|
let mut windows_output_default_file: Option<String> = None;
|
||||||
|
|
||||||
|
|
@ -57,12 +55,16 @@ fn main() -> Result<(), io::Error> {
|
||||||
|
|
||||||
output = windows_output_default_file.as_deref().or(output);
|
output = windows_output_default_file.as_deref().or(output);
|
||||||
|
|
||||||
match output {
|
let writer : Box<dyn Write> = match output {
|
||||||
Some(file) => {
|
Some(file) => {
|
||||||
fs::write(file, formatted)?;
|
Box::new(File::create(file)?)
|
||||||
}
|
}
|
||||||
None => println!("{}", formatted),
|
None => Box::new(std::io::stdout()),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
let mut reader = BufReader::new(reader);
|
||||||
|
let mut writer = BufWriter::new(writer);
|
||||||
|
format_json_buffered(&mut reader, &mut writer, indent)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue