1use nu_engine::command_prelude::*;
2use nu_protocol::{ListStream, Signals};
3
4#[derive(Clone)]
5pub struct Default;
6
7impl Command for Default {
8 fn name(&self) -> &str {
9 "default"
10 }
11
12 fn signature(&self) -> Signature {
13 Signature::build("default")
14 .input_output_types(vec![(Type::Any, Type::Any)])
17 .required(
18 "default value",
19 SyntaxShape::Any,
20 "The value to use as a default.",
21 )
22 .optional(
23 "column name",
24 SyntaxShape::String,
25 "The name of the column.",
26 )
27 .switch(
28 "empty",
29 "also replace empty items like \"\", {}, and []",
30 Some('e'),
31 )
32 .category(Category::Filters)
33 }
34
35 fn description(&self) -> &str {
36 "Sets a default value if a row's column is missing or null."
37 }
38
39 fn run(
40 &self,
41 engine_state: &EngineState,
42 stack: &mut Stack,
43 call: &Call,
44 input: PipelineData,
45 ) -> Result<PipelineData, ShellError> {
46 let empty = call.has_flag(engine_state, stack, "empty")?;
47 default(engine_state, stack, call, input, empty)
48 }
49
50 fn examples(&self) -> Vec<Example> {
51 vec![
52 Example {
53 description: "Give a default 'target' column to all file entries",
54 example: "ls -la | default 'nothing' target ",
55 result: None,
56 },
57 Example {
58 description:
59 "Get the env value of `MY_ENV` with a default value 'abc' if not present",
60 example: "$env | get --ignore-errors MY_ENV | default 'abc'",
61 result: Some(Value::test_string("abc")),
62 },
63 Example {
64 description: "Replace the `null` value in a list",
65 example: "[1, 2, null, 4] | each { default 3 }",
66 result: Some(Value::list(
67 vec![
68 Value::test_int(1),
69 Value::test_int(2),
70 Value::test_int(3),
71 Value::test_int(4),
72 ],
73 Span::test_data(),
74 )),
75 },
76 Example {
77 description: r#"Replace the missing value in the "a" column of a list"#,
78 example: "[{a:1 b:2} {b:1}] | default 'N/A' a",
79 result: Some(Value::test_list(vec![
80 Value::test_record(record! {
81 "a" => Value::test_int(1),
82 "b" => Value::test_int(2),
83 }),
84 Value::test_record(record! {
85 "a" => Value::test_string("N/A"),
86 "b" => Value::test_int(1),
87 }),
88 ])),
89 },
90 Example {
91 description: r#"Replace the empty string in the "a" column of a list"#,
92 example: "[{a:1 b:2} {a:'' b:1}] | default -e 'N/A' a",
93 result: Some(Value::test_list(vec![
94 Value::test_record(record! {
95 "a" => Value::test_int(1),
96 "b" => Value::test_int(2),
97 }),
98 Value::test_record(record! {
99 "a" => Value::test_string("N/A"),
100 "b" => Value::test_int(1),
101 }),
102 ])),
103 },
104 ]
105 }
106}
107
108fn default(
109 engine_state: &EngineState,
110 stack: &mut Stack,
111 call: &Call,
112 input: PipelineData,
113 default_when_empty: bool,
114) -> Result<PipelineData, ShellError> {
115 let metadata = input.metadata();
116 let value: Value = call.req(engine_state, stack, 0)?;
117 let column: Option<Spanned<String>> = call.opt(engine_state, stack, 1)?;
118
119 if let Some(column) = column {
120 input
121 .map(
122 move |mut item| match item {
123 Value::Record {
124 val: ref mut record,
125 ..
126 } => {
127 let record = record.to_mut();
128 if let Some(val) = record.get_mut(&column.item) {
129 if matches!(val, Value::Nothing { .. })
130 || (default_when_empty && val.is_empty())
131 {
132 *val = value.clone();
133 }
134 } else {
135 record.push(column.item.clone(), value.clone());
136 }
137
138 item
139 }
140 _ => item,
141 },
142 engine_state.signals(),
143 )
144 .map(|x| x.set_metadata(metadata))
145 } else if input.is_nothing()
146 || (default_when_empty
147 && matches!(input, PipelineData::Value(ref value, _) if value.is_empty()))
148 {
149 Ok(value.into_pipeline_data())
150 } else if default_when_empty && matches!(input, PipelineData::ListStream(..)) {
151 let PipelineData::ListStream(ls, metadata) = input else {
152 unreachable!()
153 };
154 let span = ls.span();
155 let mut stream = ls.into_inner().peekable();
156 if stream.peek().is_none() {
157 return Ok(value.into_pipeline_data());
158 }
159
160 let ls = ListStream::new(stream, span, Signals::empty());
163 Ok(PipelineData::ListStream(ls, metadata))
164 } else {
165 Ok(input)
166 }
167}
168
169#[cfg(test)]
170mod test {
171 use super::*;
172
173 #[test]
174 fn test_examples() {
175 use crate::test_examples;
176
177 test_examples(Default {})
178 }
179}