nu_command/formats/from/
yaml.rs1use nu_engine::command_prelude::*;
2use nu_heavy_utils::yaml::ParseOptions;
3use nu_protocol::{ast::PathMember, casing::Casing};
4
5#[derive(Clone)]
6pub struct FromYamlLike(&'static str);
7pub const FROM_YAML: FromYamlLike = FromYamlLike("from yaml");
8pub const FROM_YML: FromYamlLike = FromYamlLike("from yml");
9
10impl Command for FromYamlLike {
11 fn name(&self) -> &str {
12 self.0
13 }
14
15 fn signature(&self) -> Signature {
16 Signature::build(self.name())
17 .input_output_types(vec![(Type::String, Type::Any)])
18 .category(Category::Formats)
19 .param(
20 Flag::new("spec")
21 .arg(SyntaxShape::String)
22 .desc("YAML spec version ('1.1' or '1.2' (default)).")
23 .completion(Completion::new_list(&["1.1", "1.2"])),
24 )
25 .param(
26 Flag::new("multiple")
27 .arg(SyntaxShape::String)
28 .desc("Handle multiple documents ('auto', 'list', 'single').")
29 .completion(Completion::new_list(&["auto", "list", "single"])),
30 )
31 .switch("ignore-tags", "Ignore any tags", None)
32 .param(
33 Flag::new("key-resolution")
34 .arg(SyntaxShape::String)
35 .desc("Handle plain scalar keys ('strict' (default), 'verbatim').")
36 .completion(Completion::new_list(&["strict", "verbatim"])),
37 )
38 }
39
40 fn description(&self) -> &str {
41 "Parse text as .yaml/.yml and create table."
42 }
43
44 fn examples(&self) -> Vec<Example<'_>> {
45 vec![
46 Example {
47 example: match self.name() {
48 "from yaml" => "'a: 1' | from yaml",
49 "from yml" => "'a: 1' | from yml",
50 _ => unreachable!("only implemented for `yaml` and `yml`"),
51 },
52 description: "Converts YAML formatted string to table.",
53 result: Some(test_record! {
54 "a" => 1
55 }),
56 },
57 Example {
58 example: match self.name() {
59 "from yaml" => "'[ a: 1, b: [1, 2] ]' | from yaml",
60 "from yml" => "'[ a: 1, b: [1, 2] ]' | from yml",
61 _ => unreachable!("only implemented for `yaml` and `yml`"),
62 },
63 description: "Converts YAML formatted string to table.",
64 result: Some(Value::test_list(vec![
65 test_record! { "a" => 1 },
66 test_record! { "b" => [1, 2] },
67 ])),
68 },
69 Example {
70 example: match self.name() {
71 "from yaml" => "'!cell-path $.1.abc?.def!' | from yaml",
72 "from yml" => "'!cell-path $.1.abc?.def!' | from yml",
73 _ => unreachable!("only implemented for `yaml` and `yml`"),
74 },
75 description: "Convert nushell values from YAML.",
76 result: Some(Value::test_cell_path(CellPath {
77 members: vec![
78 PathMember::test_int(1, false),
79 PathMember::test_string("abc", true, Casing::Sensitive),
80 PathMember::test_string("def", false, Casing::Insensitive),
81 ],
82 })),
83 },
84 ]
85 }
86
87 fn run(
88 &self,
89 engine_state: &EngineState,
90 stack: &mut Stack,
91 call: &Call,
92 mut input: PipelineData,
93 ) -> Result<PipelineData, ShellError> {
94 let metadata = input
95 .take_metadata()
96 .map(|meta| meta.with_content_type(None));
97 let (yaml, yaml_span, ..) = input.collect_string_strict(call.head)?;
98 let yaml = yaml.as_str().into_spanned(yaml_span);
99 let spec = call.get_flag(engine_state, stack, "spec")?;
100 let multiple = call.get_flag(engine_state, stack, "multiple")?;
101 let ignore_tags = call.has_flag(engine_state, stack, "ignore-tags")?;
102 let plain_scalar_key_mode = call.get_flag(engine_state, stack, "key-resolution")?;
103 let options = ParseOptions::default()
104 .with_spec(spec.unwrap_or_default())
105 .with_multiple(multiple.unwrap_or_default())
106 .with_ignore_tags(ignore_tags)
107 .with_key_resolution(plain_scalar_key_mode.unwrap_or_default());
108 nu_heavy_utils::yaml::parse(yaml, call.head, options)
109 .map(|val| PipelineData::value(val, metadata))
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use nu_test_support::prelude::{Result, *};
117
118 #[test]
119 fn test_examples() -> Result {
120 test().examples(FROM_YAML)?;
121 test().examples(FROM_YML)?;
122 Ok(())
123 }
124
125 #[test]
126 fn test_content_type_metadata() -> Result {
127 let code = r#"
128 "a: 1\nb: 2"
129 | metadata set --content-type 'application/yaml' --path-columns [name]
130 | from yaml
131 | metadata
132 | reject span
133 "#;
134
135 test().run(code).expect_value_eq(test_record! {
136 "path_columns" => ["name"]
137 })
138 }
139}