1use std::borrow::Cow;
2
3use nu_engine::command_prelude::*;
4use nu_protocol::{DeprecationEntry, DeprecationType, ReportMode, Signals, ast::PathMember};
5
6#[derive(Clone)]
7pub struct Get;
8
9impl Command for Get {
10 fn name(&self) -> &str {
11 "get"
12 }
13
14 fn description(&self) -> &str {
15 "Extract data using a cell path."
16 }
17
18 fn extra_description(&self) -> &str {
19 r#"This is equivalent to using the cell path access syntax: `$env.OS` is the same as `$env | get OS`.
20
21If multiple cell paths are given, this will produce a list of values."#
22 }
23
24 fn signature(&self) -> nu_protocol::Signature {
25 Signature::build("get")
26 .input_output_types(vec![
27 (
28 Type::List(Box::new(Type::Any)),
31 Type::Any,
32 ),
33 (Type::table(), Type::Any),
34 (Type::record(), Type::Any),
35 (Type::Nothing, Type::Nothing),
36 ])
37 .required(
38 "cell_path",
39 SyntaxShape::CellPath,
40 "The cell path to the data.",
41 )
42 .rest("rest", SyntaxShape::CellPath, "Additional cell paths.")
43 .switch(
44 "optional",
45 "make all cell path members optional (returns `null` for missing values)",
46 Some('o'),
47 )
48 .switch(
49 "ignore-errors",
50 "ignore missing data (make all cell path members optional) (deprecated)",
51 Some('i'),
52 )
53 .switch(
54 "sensitive",
55 "get path in a case sensitive manner (deprecated)",
56 Some('s'),
57 )
58 .allow_variants_without_examples(true)
59 .category(Category::Filters)
60 }
61
62 fn examples(&self) -> Vec<Example> {
63 vec![
64 Example {
65 description: "Get an item from a list",
66 example: "[0 1 2] | get 1",
67 result: Some(Value::test_int(1)),
68 },
69 Example {
70 description: "Get a column from a table",
71 example: "[{A: A0}] | get A",
72 result: Some(Value::list(
73 vec![Value::test_string("A0")],
74 Span::test_data(),
75 )),
76 },
77 Example {
78 description: "Get a cell from a table",
79 example: "[{A: A0}] | get 0.A",
80 result: Some(Value::test_string("A0")),
81 },
82 Example {
83 description: "Extract the name of the 3rd record in a list (same as `ls | $in.name.2`)",
84 example: "ls | get name.2",
85 result: None,
86 },
87 Example {
88 description: "Extract the name of the 3rd record in a list",
89 example: "ls | get 2.name",
90 result: None,
91 },
92 Example {
93 description: "Getting Path/PATH in a case insensitive way",
94 example: "$env | get paTH!",
95 result: None,
96 },
97 Example {
98 description: "Getting Path in a case sensitive way, won't work for 'PATH'",
99 example: "$env | get Path",
100 result: None,
101 },
102 ]
103 }
104
105 fn is_const(&self) -> bool {
106 true
107 }
108
109 fn run_const(
110 &self,
111 working_set: &StateWorkingSet,
112 call: &Call,
113 input: PipelineData,
114 ) -> Result<PipelineData, ShellError> {
115 let cell_path: CellPath = call.req_const(working_set, 0)?;
116 let rest: Vec<CellPath> = call.rest_const(working_set, 1)?;
117 let ignore_errors = call.has_flag_const(working_set, "ignore-errors")?;
118 let metadata = input.metadata();
119 action(
120 input,
121 cell_path,
122 rest,
123 ignore_errors,
124 working_set.permanent().signals().clone(),
125 call.head,
126 )
127 .map(|x| x.set_metadata(metadata))
128 }
129
130 fn run(
131 &self,
132 engine_state: &EngineState,
133 stack: &mut Stack,
134 call: &Call,
135 input: PipelineData,
136 ) -> Result<PipelineData, ShellError> {
137 let cell_path: CellPath = call.req(engine_state, stack, 0)?;
138 let rest: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
139 let optional = call.has_flag(engine_state, stack, "optional")?
140 || call.has_flag(engine_state, stack, "ignore-errors")?;
141 let metadata = input.metadata();
142 action(
143 input,
144 cell_path,
145 rest,
146 optional,
147 engine_state.signals().clone(),
148 call.head,
149 )
150 .map(|x| x.set_metadata(metadata))
151 }
152
153 fn deprecation_info(&self) -> Vec<DeprecationEntry> {
154 vec![
155 DeprecationEntry {
156 ty: DeprecationType::Flag("sensitive".into()),
157 report_mode: ReportMode::FirstUse,
158 since: Some("0.105.0".into()),
159 expected_removal: None,
160 help: Some("Cell-paths are now case-sensitive by default.\nTo access fields case-insensitively, add `!` after the relevant path member.".into())
161 },
162 DeprecationEntry {
163 ty: DeprecationType::Flag("ignore-errors".into()),
164 report_mode: ReportMode::FirstUse,
165 since: Some("0.106.0".into()),
166 expected_removal: None,
167 help: Some("This flag has been renamed to `--optional (-o)` to better reflect its behavior.".into())
168 }
169 ]
170 }
171}
172
173fn action(
174 input: PipelineData,
175 mut cell_path: CellPath,
176 mut rest: Vec<CellPath>,
177 optional: bool,
178 signals: Signals,
179 span: Span,
180) -> Result<PipelineData, ShellError> {
181 if optional {
182 cell_path.make_optional();
183 for path in &mut rest {
184 path.make_optional();
185 }
186 }
187
188 if let PipelineData::Empty = input {
189 return Err(ShellError::PipelineEmpty { dst_span: span });
190 }
191
192 if rest.is_empty() {
193 follow_cell_path_into_stream(input, signals, cell_path.members, span)
194 } else {
195 let mut output = vec![];
196
197 let paths = std::iter::once(cell_path).chain(rest);
198
199 let input = input.into_value(span)?;
200
201 for path in paths {
202 output.push(input.follow_cell_path(&path.members)?.into_owned());
203 }
204
205 Ok(output.into_iter().into_pipeline_data(span, signals))
206 }
207}
208
209pub fn follow_cell_path_into_stream(
217 data: PipelineData,
218 signals: Signals,
219 cell_path: Vec<PathMember>,
220 head: Span,
221) -> Result<PipelineData, ShellError> {
222 let has_int_member = cell_path
225 .iter()
226 .any(|it| matches!(it, PathMember::Int { .. }));
227 match data {
228 PipelineData::ListStream(stream, ..) if !has_int_member => {
229 let result = stream
230 .into_iter()
231 .map(move |value| {
232 let span = value.span();
233
234 value
235 .follow_cell_path(&cell_path)
236 .map(Cow::into_owned)
237 .unwrap_or_else(|error| Value::error(error, span))
238 })
239 .into_pipeline_data(head, signals);
240
241 Ok(result)
242 }
243
244 _ => data
245 .follow_cell_path(&cell_path, head)
246 .map(|x| x.into_pipeline_data()),
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 #[test]
255 fn test_examples() {
256 use crate::test_examples;
257
258 test_examples(Get)
259 }
260}