nu_command/formats/to/
nuon.rs

1use nu_engine::command_prelude::*;
2
3#[derive(Clone)]
4pub struct ToNuon;
5
6impl Command for ToNuon {
7    fn name(&self) -> &str {
8        "to nuon"
9    }
10
11    fn signature(&self) -> Signature {
12        Signature::build("to nuon")
13            .input_output_types(vec![(Type::Any, Type::String)])
14            .switch(
15                "raw",
16                "remove all of the whitespace (overwrites -i and -t)",
17                Some('r'),
18            )
19            .named(
20                "indent",
21                SyntaxShape::Number,
22                "specify indentation width",
23                Some('i'),
24            )
25            .named(
26                "tabs",
27                SyntaxShape::Number,
28                "specify indentation tab quantity",
29                Some('t'),
30            )
31            .switch(
32                "serialize",
33                "serialize nushell types that cannot be deserialized",
34                Some('s'),
35            )
36            .category(Category::Formats)
37    }
38
39    fn description(&self) -> &str {
40        "Converts table data into Nuon (Nushell Object Notation) text."
41    }
42
43    fn run(
44        &self,
45        engine_state: &EngineState,
46        stack: &mut Stack,
47        call: &Call,
48        input: PipelineData,
49    ) -> Result<PipelineData, ShellError> {
50        let metadata = input
51            .metadata()
52            .unwrap_or_default()
53            .with_content_type(Some("application/x-nuon".into()));
54
55        let serialize_types = call.has_flag(engine_state, stack, "serialize")?;
56        let style = if call.has_flag(engine_state, stack, "raw")? {
57            nuon::ToStyle::Raw
58        } else if let Some(t) = call.get_flag(engine_state, stack, "tabs")? {
59            nuon::ToStyle::Tabs(t)
60        } else if let Some(i) = call.get_flag(engine_state, stack, "indent")? {
61            nuon::ToStyle::Spaces(i)
62        } else {
63            nuon::ToStyle::Default
64        };
65
66        let span = call.head;
67        let value = input.into_value(span)?;
68
69        match nuon::to_nuon(engine_state, &value, style, Some(span), serialize_types) {
70            Ok(serde_nuon_string) => Ok(Value::string(serde_nuon_string, span)
71                .into_pipeline_data_with_metadata(Some(metadata))),
72            Err(error) => {
73                Ok(Value::error(error, span).into_pipeline_data_with_metadata(Some(metadata)))
74            }
75        }
76    }
77
78    fn examples(&self) -> Vec<Example<'_>> {
79        vec![
80            Example {
81                description: "Outputs a NUON string representing the contents of this list, compact by default",
82                example: "[1 2 3] | to nuon",
83                result: Some(Value::test_string("[1, 2, 3]")),
84            },
85            Example {
86                description: "Outputs a NUON array of ints, with pretty indentation",
87                example: "[1 2 3] | to nuon --indent 2",
88                result: Some(Value::test_string("[\n  1,\n  2,\n  3\n]")),
89            },
90            Example {
91                description: "Overwrite any set option with --raw",
92                example: "[1 2 3] | to nuon --indent 2 --raw",
93                result: Some(Value::test_string("[1,2,3]")),
94            },
95            Example {
96                description: "A more complex record with multiple data types",
97                example: "{date: 2000-01-01, data: [1 [2 3] 4.56]} | to nuon --indent 2",
98                result: Some(Value::test_string(
99                    "{\n  date: 2000-01-01T00:00:00+00:00,\n  data: [\n    1,\n    [\n      2,\n      3\n    ],\n    4.56\n  ]\n}",
100                )),
101            },
102            Example {
103                description: "A more complex record with --raw",
104                example: "{date: 2000-01-01, data: [1 [2 3] 4.56]} | to nuon --raw",
105                result: Some(Value::test_string(
106                    "{date:2000-01-01T00:00:00+00:00,data:[1,[2,3],4.56]}",
107                )),
108            },
109        ]
110    }
111}
112
113#[cfg(test)]
114mod test {
115    use super::*;
116    use nu_cmd_lang::eval_pipeline_without_terminal_expression;
117
118    use crate::{Get, Metadata};
119
120    #[test]
121    fn test_examples() {
122        use super::ToNuon;
123        use crate::test_examples;
124        test_examples(ToNuon {})
125    }
126
127    #[test]
128    fn test_content_type_metadata() {
129        let mut engine_state = Box::new(EngineState::new());
130        let delta = {
131            // Base functions that are needed for testing
132            // Try to keep this working set small to keep tests running as fast as possible
133            let mut working_set = StateWorkingSet::new(&engine_state);
134
135            working_set.add_decl(Box::new(ToNuon {}));
136            working_set.add_decl(Box::new(Metadata {}));
137            working_set.add_decl(Box::new(Get {}));
138
139            working_set.render()
140        };
141
142        engine_state
143            .merge_delta(delta)
144            .expect("Error merging delta");
145
146        let cmd = "{a: 1 b: 2} | to nuon | metadata | get content_type | $in";
147        let result = eval_pipeline_without_terminal_expression(
148            cmd,
149            std::env::temp_dir().as_ref(),
150            &mut engine_state,
151        );
152        assert_eq!(
153            Value::test_string("application/x-nuon"),
154            result.expect("There should be a result")
155        );
156    }
157}