From 5bf2ffeaf58eb0cdda94e67621fa18e79b47ca68 Mon Sep 17 00:00:00 2001 From: "Joseph T. Lyons" Date: Sun, 20 Feb 2022 17:29:19 -0500 Subject: [PATCH] Add indent flag to `to json` (first draft) (#4571) * Add indent flag to `to json` (first draft) * Run cargo fmt * Update examples / tests * Change order of examples --- crates/nu-command/src/formats/to/json.rs | 55 +++++++++++++++++------- crates/nu-json/src/lib.rs | 4 +- crates/nu-json/src/ser.rs | 42 ++++++++++++++++++ 3 files changed, 84 insertions(+), 17 deletions(-) diff --git a/crates/nu-command/src/formats/to/json.rs b/crates/nu-command/src/formats/to/json.rs index 87aa4e650b..5d9b3b8a6e 100644 --- a/crates/nu-command/src/formats/to/json.rs +++ b/crates/nu-command/src/formats/to/json.rs @@ -1,7 +1,8 @@ +use nu_engine::CallExt; use nu_protocol::ast::{Call, PathMember}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Value, + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value, }; #[derive(Clone)] @@ -15,6 +16,12 @@ impl Command for ToJson { fn signature(&self) -> Signature { Signature::build("to json") .switch("raw", "remove all of the whitespace", Some('r')) + .named( + "indent", + SyntaxShape::Number, + "specify indentation width", + Some('i'), + ) .category(Category::Formats) } @@ -24,24 +31,26 @@ impl Command for ToJson { fn run( &self, - _engine_state: &EngineState, - _stack: &mut Stack, + engine_state: &EngineState, + stack: &mut Stack, call: &Call, input: PipelineData, ) -> Result { let raw = call.has_flag("raw"); - let json_function = if raw { - nu_json::to_string_raw - } else { - nu_json::to_string - }; - let span = call.head; let value = input.into_value(span); let json_value = value_to_json_value(&value)?; - match json_function(&json_value) { + let json_result = if raw { + nu_json::to_string_raw(&json_value) + } else { + let indent: usize = call.get_flag(engine_state, stack, "indent")?.unwrap_or(2); + + nu_json::to_string_with_indent(&json_value, indent) + }; + + match json_result { Ok(serde_json_string) => Ok(Value::String { val: serde_json_string, span, @@ -55,12 +64,26 @@ impl Command for ToJson { } fn examples(&self) -> Vec { - vec![Example { - description: - "Outputs an unformatted JSON string representing the contents of this table", - example: "[1 2 3] | to json", - result: Some(Value::test_string("[\n 1,\n 2,\n 3\n]")), - }] + vec![ + Example { + description: + "Outputs a JSON string, with default indentation, representing the contents of this table", + example: "[a b c] | to json", + result: Some(Value::test_string("[\n \"a\",\n \"b\",\n \"c\"\n]")), + }, + Example { + description: + "Outputs a JSON string, with 4-space indentation, representing the contents of this table", + example: "[Joe Bob Sam] | to json -i 4", + result: Some(Value::test_string("[\n \"Joe\",\n \"Bob\",\n \"Sam\"\n]")), + }, + Example { + description: + "Outputs an unformatted JSON string representing the contents of this table", + example: "[1 2 3] | to json -r", + result: Some(Value::test_string("[1,2,3]")), + }, + ] } } diff --git a/crates/nu-json/src/lib.rs b/crates/nu-json/src/lib.rs index 02b792e53b..d05d6c6162 100644 --- a/crates/nu-json/src/lib.rs +++ b/crates/nu-json/src/lib.rs @@ -2,7 +2,9 @@ pub use self::de::{ from_iter, from_reader, from_slice, from_str, Deserializer, StreamDeserializer, }; pub use self::error::{Error, ErrorCode, Result}; -pub use self::ser::{to_string, to_string_raw, to_vec, to_writer, Serializer}; +pub use self::ser::{ + to_string, to_string_raw, to_string_with_indent, to_vec, to_writer, Serializer, +}; pub use self::value::{from_value, to_value, Map, Value}; pub mod builder; diff --git a/crates/nu-json/src/ser.rs b/crates/nu-json/src/ser.rs index e886143d1c..f1a12fa30a 100644 --- a/crates/nu-json/src/ser.rs +++ b/crates/nu-json/src/ser.rs @@ -31,6 +31,11 @@ where pub fn new(writer: W) -> Self { Serializer::with_formatter(writer, HjsonFormatter::new()) } + + #[inline] + pub fn with_indent(writer: W, indent: &'a [u8]) -> Self { + Serializer::with_formatter(writer, HjsonFormatter::with_indent(indent)) + } } impl Serializer @@ -941,6 +946,19 @@ where Ok(()) } +/// Encode the specified struct into a Hjson `[u8]` writer. +#[inline] +pub fn to_writer_with_indent(writer: &mut W, value: &T, indent: usize) -> Result<()> +where + W: io::Write, + T: ser::Serialize, +{ + let indent_string = " ".repeat(indent); + let mut ser = Serializer::with_indent(writer, indent_string.as_bytes()); + value.serialize(&mut ser)?; + Ok(()) +} + /// Encode the specified struct into a Hjson `[u8]` buffer. #[inline] pub fn to_vec(value: &T) -> Result> @@ -954,6 +972,19 @@ where Ok(writer) } +/// Encode the specified struct into a Hjson `[u8]` buffer. +#[inline] +pub fn to_vec_with_indent(value: &T, indent: usize) -> Result> +where + T: ser::Serialize, +{ + // We are writing to a Vec, which doesn't fail. So we can ignore + // the error. + let mut writer = Vec::with_capacity(128); + to_writer_with_indent(&mut writer, value, indent)?; + Ok(writer) +} + /// Encode the specified struct into a Hjson `String` buffer. #[inline] pub fn to_string(value: &T) -> Result @@ -965,6 +996,17 @@ where Ok(string) } +/// Encode the specified struct into a Hjson `String` buffer. +#[inline] +pub fn to_string_with_indent(value: &T, indent: usize) -> Result +where + T: ser::Serialize, +{ + let vec = to_vec_with_indent(value, indent)?; + let string = String::from_utf8(vec)?; + Ok(string) +} + /// Encode the specified struct into a Hjson `String` buffer. /// And remove all whitespace #[inline]