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            .switch(
37                "raw-strings",
38                "use raw string syntax (r#'...'#) for strings with quotes or backslashes",
39                None,
40            )
41            .category(Category::Formats)
42    }
43
44    fn description(&self) -> &str {
45        "Converts table data into Nuon (Nushell Object Notation) text."
46    }
47
48    fn run(
49        &self,
50        engine_state: &EngineState,
51        stack: &mut Stack,
52        call: &Call,
53        input: PipelineData,
54    ) -> Result<PipelineData, ShellError> {
55        let metadata = input
56            .metadata()
57            .unwrap_or_default()
58            .with_content_type(Some("application/x-nuon".into()));
59
60        let serialize_types = call.has_flag(engine_state, stack, "serialize")?;
61        let raw_strings = call.has_flag(engine_state, stack, "raw-strings")?;
62        let style = if call.has_flag(engine_state, stack, "raw")? {
63            nuon::ToStyle::Raw
64        } else if let Some(t) = call.get_flag(engine_state, stack, "tabs")? {
65            nuon::ToStyle::Tabs(t)
66        } else if let Some(i) = call.get_flag(engine_state, stack, "indent")? {
67            nuon::ToStyle::Spaces(i)
68        } else {
69            nuon::ToStyle::Default
70        };
71
72        let span = call.head;
73        let value = input.into_value(span)?;
74
75        let config = nuon::ToNuonConfig::default()
76            .style(style)
77            .span(Some(span))
78            .serialize_types(serialize_types)
79            .raw_strings(raw_strings);
80
81        match nuon::to_nuon(engine_state, &value, config) {
82            Ok(serde_nuon_string) => Ok(Value::string(serde_nuon_string, span)
83                .into_pipeline_data_with_metadata(Some(metadata))),
84            Err(error) => {
85                Ok(Value::error(error, span).into_pipeline_data_with_metadata(Some(metadata)))
86            }
87        }
88    }
89
90    fn examples(&self) -> Vec<Example<'_>> {
91        vec![
92            Example {
93                description: "Outputs a NUON string representing the contents of this list, compact by default",
94                example: "[1 2 3] | to nuon",
95                result: Some(Value::test_string("[1, 2, 3]")),
96            },
97            Example {
98                description: "Outputs a NUON array of ints, with pretty indentation",
99                example: "[1 2 3] | to nuon --indent 2",
100                result: Some(Value::test_string("[\n  1,\n  2,\n  3\n]")),
101            },
102            Example {
103                description: "Overwrite any set option with --raw",
104                example: "[1 2 3] | to nuon --indent 2 --raw",
105                result: Some(Value::test_string("[1,2,3]")),
106            },
107            Example {
108                description: "A more complex record with multiple data types",
109                example: "{date: 2000-01-01, data: [1 [2 3] 4.56]} | to nuon --indent 2",
110                result: Some(Value::test_string(
111                    "{\n  date: 2000-01-01T00:00:00+00:00,\n  data: [\n    1,\n    [\n      2,\n      3\n    ],\n    4.56\n  ]\n}",
112                )),
113            },
114            Example {
115                description: "A more complex record with --raw",
116                example: "{date: 2000-01-01, data: [1 [2 3] 4.56]} | to nuon --raw",
117                result: Some(Value::test_string(
118                    "{date:2000-01-01T00:00:00+00:00,data:[1,[2,3],4.56]}",
119                )),
120            },
121            Example {
122                description: "Use raw string syntax for strings with quotes or backslashes",
123                example: r#"'hello "world"' | to nuon --raw-strings"#,
124                result: Some(Value::test_string(r#"r#'hello "world"'#"#)),
125            },
126        ]
127    }
128}
129
130#[cfg(test)]
131mod test {
132    use super::*;
133    use nu_cmd_lang::eval_pipeline_without_terminal_expression;
134
135    use crate::{Get, Metadata};
136
137    #[test]
138    fn test_examples() {
139        use super::ToNuon;
140        use crate::test_examples;
141        test_examples(ToNuon {})
142    }
143
144    #[test]
145    fn test_content_type_metadata() {
146        let mut engine_state = Box::new(EngineState::new());
147        let delta = {
148            // Base functions that are needed for testing
149            // Try to keep this working set small to keep tests running as fast as possible
150            let mut working_set = StateWorkingSet::new(&engine_state);
151
152            working_set.add_decl(Box::new(ToNuon {}));
153            working_set.add_decl(Box::new(Metadata {}));
154            working_set.add_decl(Box::new(Get {}));
155
156            working_set.render()
157        };
158
159        engine_state
160            .merge_delta(delta)
161            .expect("Error merging delta");
162
163        let cmd = "{a: 1 b: 2} | to nuon | metadata | get content_type | $in";
164        let result = eval_pipeline_without_terminal_expression(
165            cmd,
166            std::env::temp_dir().as_ref(),
167            &mut engine_state,
168        );
169        assert_eq!(
170            Value::test_string("application/x-nuon"),
171            result.expect("There should be a result")
172        );
173    }
174}