This commit is contained in:
nora 2021-07-17 20:42:02 +02:00
parent bf1155cd00
commit d0733b1a7c
4 changed files with 181 additions and 3 deletions

3
.gitignore vendored
View file

@ -1,3 +1,6 @@
/target
.idea
*.iml
# test data
*.json

7
README.md Normal file
View file

@ -0,0 +1,7 @@
Formats json.
Will maybe even be fast in the future, idk
would be amazing if it even works.
note: does not actually parse the json to a parse tree or something, it just formats it

149
src/lib.rs Normal file
View file

@ -0,0 +1,149 @@
pub fn format_json(json: &str, indent_str: &str) -> String {
let mut out = String::with_capacity(json.len()); // at least as big as the input
let mut escaped = false;
let mut in_string = false;
let mut indent_level = 0;
let mut newline_requested = false; // invalidated if next character is ] or }
for char in json.chars() {
if in_string {
let mut escape_here = false;
match char {
'"' => {
if !escaped {
in_string = false;
}
}
'\\' => {
if !escaped {
escape_here = true;
}
}
_ => {}
}
out.push(char);
escaped = escape_here;
} else {
let mut auto_push = true;
let mut request_newline = false;
let old_level = indent_level;
match char {
'"' => in_string = true,
' ' | '\n' | '\t' => continue,
'[' => {
indent_level += 1;
request_newline = true;
}
'{' => {
indent_level += 1;
request_newline = true;
}
'}' | ']' => {
indent_level -= 1;
if !newline_requested {
// see comment above
out.push('\n');
indent(&mut out, indent_level, indent_str);
}
}
':' => {
auto_push = false;
out.push(char);
out.push(' ');
}
',' => {
request_newline = true;
}
_ => {}
}
if newline_requested && char != ']' && char != '}' {
// newline only happens after { [ and ,
out.push('\n');
indent(&mut out, old_level, indent_str);
}
if auto_push {
out.push(char);
}
newline_requested = request_newline;
}
}
out
}
fn indent(buf: &mut String, level: usize, indent_str: &str) {
for _ in 0..level {
buf.push_str(indent_str);
}
}
#[cfg(test)]
mod test {
const INDENT: &str = " ";
use super::*;
#[test]
fn echoes_primitive() {
let json = "1.35";
assert_eq!(json, format_json(json, INDENT));
}
#[test]
fn ignore_whitespace_in_string() {
let json = "\" hallo \"";
assert_eq!(json, format_json(json, INDENT));
}
#[test]
fn remove_leading_whitespace() {
let json = " 0";
let expected = "0";
assert_eq!(expected, format_json(json, INDENT));
}
#[test]
fn handle_escaped_strings() {
let json = " \" hallo \\\" \" ";
let expected = "\" hallo \\\" \"";
assert_eq!(expected, format_json(json, INDENT));
}
#[test]
fn simple_object() {
let json = "{\"a\":0}";
let expected = "{
\"a\": 0
}";
assert_eq!(expected, format_json(json, INDENT));
}
#[test]
fn simple_array() {
let json = "[1,2,null]";
let expected = "[
1,
2,
null
]";
assert_eq!(expected, format_json(json, INDENT));
}
#[test]
fn array_of_object() {
let json = "[{\"a\": 0}, {}, {\"a\": null}]";
let expected = "[
{
\"a\": 0
},
{},
{
\"a\": null
}
]";
assert_eq!(expected, format_json(json, INDENT));
}
}

View file

@ -1,3 +1,22 @@
fn main() {
println!("Hello, world!");
use jsonformat::format_json;
use std::fs;
use std::io;
use std::io::Read;
fn main() -> Result<(), io::Error> {
let filename = std::env::args().skip(1).next();
let str = match filename {
Some(path) => fs::read_to_string(path)?,
None => {
let mut buf = String::new();
let stdin = std::io::stdin();
stdin.lock().read_to_string(&mut buf)?;
buf
}
};
println!("{}", format_json(&str, " "));
Ok(())
}