From d0733b1a7cb63b9dca188a727b21e24f31838fd2 Mon Sep 17 00:00:00 2001 From: Nilstrieb Date: Sat, 17 Jul 2021 20:42:02 +0200 Subject: [PATCH] works --- .gitignore | 5 +- README.md | 7 +++ src/lib.rs | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 23 +++++++- 4 files changed, 181 insertions(+), 3 deletions(-) create mode 100644 README.md create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore index 5b82e25..02866f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ /target .idea -*.iml \ No newline at end of file +*.iml + +# test data +*.json \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..dad71ef --- /dev/null +++ b/README.md @@ -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 \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..9f12788 --- /dev/null +++ b/src/lib.rs @@ -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)); + } +} diff --git a/src/main.rs b/src/main.rs index e7a11a9..bbbf69b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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(()) }