use nu_protocol::ast::{Call, PathMember}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature, Value, }; #[derive(Clone)] pub struct ToJson; impl Command for ToJson { fn name(&self) -> &str { "to json" } fn signature(&self) -> Signature { Signature::build("to json") // .named( // "pretty", // SyntaxShape::Int, // "Formats the JSON text with the provided indentation setting", // Some('p'), // ) } fn usage(&self) -> &str { "Converts table data into JSON text." } fn run( &self, engine_state: &EngineState, _stack: &mut Stack, call: &Call, input: PipelineData, ) -> Result { to_json(engine_state, call, input) } 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]")), }] } } pub fn value_to_json_value(v: &Value) -> Result { Ok(match v { Value::Bool { val, .. } => nu_json::Value::Bool(*val), Value::Filesize { val, .. } => nu_json::Value::I64(*val), Value::Duration { val, .. } => nu_json::Value::I64(*val), Value::Date { val, .. } => nu_json::Value::String(val.to_string()), Value::Float { val, .. } => nu_json::Value::F64(*val), Value::Int { val, .. } => nu_json::Value::I64(*val), Value::Nothing { .. } => nu_json::Value::Null, Value::String { val, .. } => nu_json::Value::String(val.to_string()), Value::CellPath { val, .. } => nu_json::Value::Array( val.members .iter() .map(|x| match &x { PathMember::String { val, .. } => Ok(nu_json::Value::String(val.clone())), PathMember::Int { val, .. } => Ok(nu_json::Value::U64(*val as u64)), }) .collect::, ShellError>>()?, ), Value::List { vals, .. } => nu_json::Value::Array(json_list(vals)?), Value::Error { error } => return Err(error.clone()), Value::Block { .. } | Value::Range { .. } => nu_json::Value::Null, #[cfg(feature = "dataframe")] UntaggedValue::DataFrame(_) | UntaggedValue::FrameStruct(_) => serde_json::Value::Null, Value::Binary { val, .. } => { nu_json::Value::Array(val.iter().map(|x| nu_json::Value::U64(*x as u64)).collect()) } Value::Record { cols, vals, .. } => { let mut m = nu_json::Map::new(); for (k, v) in cols.iter().zip(vals) { m.insert(k.clone(), value_to_json_value(v)?); } nu_json::Value::Object(m) } }) } fn json_list(input: &[Value]) -> Result, ShellError> { let mut out = vec![]; for value in input { out.push(value_to_json_value(value)?); } Ok(out) } fn to_json( engine_state: &EngineState, call: &Call, input: PipelineData, ) -> Result { let name_span = call.head; // let pretty: Option = args.get_flag("pretty")?; //let input: Vec = input.collect(); // let to_process_input = match input.len() { // x if x > 1 => { // let tag = input[0].tag.clone(); // vec![Value:: { // value: UntaggedValue::Table(input), // tag, // }] // } // 1 => input, // _ => vec![], // }; match input { PipelineData::Value(value) => { let json_value = value_to_json_value(&value)?; match nu_json::to_string(&json_value) { Ok(serde_json_string) => Ok(Value::String { val: serde_json_string, span: name_span, } .into_pipeline_data()), _ => Ok(Value::Error { error: ShellError::CantConvert("JSON".into(), name_span), } .into_pipeline_data()), } } PipelineData::Stream(stream) => Ok(stream .map(move |value| { if let Ok(json_value) = value_to_json_value(&value) { match nu_json::to_string(&json_value) { Ok(serde_json_string) => Value::String { val: serde_json_string, span: name_span, }, _ => Value::Error { error: ShellError::CantConvert("JSON".into(), name_span), }, } } else { Value::Error { error: ShellError::CantConvert("JSON".into(), name_span), } } }) .into_pipeline_data(engine_state.ctrlc.clone())), } // input // // .into_iter() // .map( // move |value| { // let value_span = value.span().expect("non-error"); // match value_to_json_value(&value) { // Ok(json_value) => { // match nu_json::to_string(&json_value) { // Ok(serde_json_string) => { // // if let Some(pretty_value) = &pretty { // // let mut pretty_format_failed = true; // // if let Ok(pretty_u64) = pretty_value.as_u64() { // // if let Ok(serde_json_value) = // // serde_json::from_str::(&serde_json_string) // // { // // let indentation_string = " ".repeat(pretty_u64 as usize); // // let serde_formatter = // // serde_json::ser::PrettyFormatter::with_indent( // // indentation_string.as_bytes(), // // ); // // let serde_buffer = Vec::new(); // // let mut serde_serializer = // // serde_json::Serializer::with_formatter( // // serde_buffer, // // serde_formatter, // // ); // // let serde_json_object = json!(serde_json_value); // // if let Ok(()) = // // serde_json_object.serialize(&mut serde_serializer) // // { // // if let Ok(ser_json_string) = // // String::from_utf8(serde_serializer.into_inner()) // // { // // pretty_format_failed = false; // // serde_json_string = ser_json_string // // } // // } // // } // // } // // if pretty_format_failed { // // return Value::error(ShellError::labeled_error( // // "Pretty formatting failed", // // "failed", // // pretty_value.tag(), // // )); // // } // // } // Value::String { // val: serde_json_string, // span: value_span, // } // } // _ => Value::Error { // error: ShellError::CantConvert("JSON".into(), value_span), // }, // } // } // _ => Value::Error { // error: ShellError::CantConvert("JSON".into(), value_span), // }, // } // }, // engine_state.ctrlc.clone(), // ) } #[cfg(test)] mod test { use super::*; #[test] fn test_examples() { use crate::test_examples; test_examples(ToJson {}) } }