nu_command/filters/
columns.rs1use nu_engine::{column::get_columns, command_prelude::*};
2
3#[derive(Clone)]
4pub struct Columns;
5
6impl Command for Columns {
7 fn name(&self) -> &str {
8 "columns"
9 }
10
11 fn signature(&self) -> Signature {
12 Signature::build(self.name())
13 .input_output_types(vec![
14 (Type::table(), Type::List(Box::new(Type::String))),
15 (Type::record(), Type::List(Box::new(Type::String))),
16 ])
17 .category(Category::Filters)
18 }
19
20 fn description(&self) -> &str {
21 "Given a record or table, produce a list of its columns' names."
22 }
23
24 fn extra_description(&self) -> &str {
25 "This is a counterpart to `values`, which produces a list of columns' values."
26 }
27
28 fn examples(&self) -> Vec<Example<'_>> {
29 vec![
30 Example {
31 example: "{ acronym:PWD, meaning:'Print Working Directory' } | columns",
32 description: "Get the columns from the record",
33 result: Some(Value::list(
34 vec![Value::test_string("acronym"), Value::test_string("meaning")],
35 Span::test_data(),
36 )),
37 },
38 Example {
39 example: "[[name,age,grade]; [bill,20,a]] | columns",
40 description: "Get the columns from the table",
41 result: Some(Value::list(
42 vec![
43 Value::test_string("name"),
44 Value::test_string("age"),
45 Value::test_string("grade"),
46 ],
47 Span::test_data(),
48 )),
49 },
50 Example {
51 example: "[[name,age,grade]; [bill,20,a]] | columns | first",
52 description: "Get the first column from the table",
53 result: None,
54 },
55 Example {
56 example: "[[name,age,grade]; [bill,20,a]] | columns | select 1",
57 description: "Get the second column from the table",
58 result: None,
59 },
60 ]
61 }
62
63 fn run(
64 &self,
65 _engine_state: &EngineState,
66 _stack: &mut Stack,
67 call: &Call,
68 input: PipelineData,
69 ) -> Result<PipelineData, ShellError> {
70 getcol(call.head, input)
71 }
72}
73
74fn getcol(head: Span, input: PipelineData) -> Result<PipelineData, ShellError> {
75 let metadata = input.metadata();
76 match input {
77 PipelineData::Empty => Ok(PipelineData::empty()),
78 PipelineData::Value(v, ..) => {
79 let span = v.span();
80 let cols = match v {
81 Value::List {
82 vals: input_vals, ..
83 } => get_columns(&input_vals)
84 .into_iter()
85 .map(move |x| Value::string(x, span))
86 .collect(),
87 Value::Custom { val, .. } => {
88 let input_as_base_value = val.to_base_value(span)?;
91 get_columns(&[input_as_base_value])
92 .into_iter()
93 .map(move |x| Value::string(x, span))
94 .collect()
95 }
96 Value::Record { val, .. } => val
97 .into_owned()
98 .into_iter()
99 .map(move |(x, _)| Value::string(x, head))
100 .collect(),
101 Value::Error { error, .. } => return Err(*error),
103 other => {
104 return Err(ShellError::OnlySupportsThisInputType {
105 exp_input_type: "record or table".into(),
106 wrong_type: other.get_type().to_string(),
107 dst_span: head,
108 src_span: other.span(),
109 });
110 }
111 };
112
113 Ok(Value::list(cols, head)
114 .into_pipeline_data()
115 .set_metadata(metadata))
116 }
117 PipelineData::ListStream(stream, ..) => {
118 let values = stream.into_iter().collect::<Vec<_>>();
119 let cols = get_columns(&values)
120 .into_iter()
121 .map(|s| Value::string(s, head))
122 .collect();
123
124 Ok(Value::list(cols, head)
125 .into_pipeline_data()
126 .set_metadata(metadata))
127 }
128 PipelineData::ByteStream(stream, ..) => Err(ShellError::OnlySupportsThisInputType {
129 exp_input_type: "record or table".into(),
130 wrong_type: "byte stream".into(),
131 dst_span: head,
132 src_span: stream.span(),
133 }),
134 }
135}
136
137#[cfg(test)]
138mod test {
139 use super::*;
140
141 #[test]
142 fn test_examples() {
143 use crate::test_examples;
144
145 test_examples(Columns {})
146 }
147}