nu_command/filters/
where_.rs1use nu_engine::{command_prelude::*, ClosureEval};
2use nu_protocol::engine::{Closure, CommandType};
3
4#[derive(Clone)]
5pub struct Where;
6
7impl Command for Where {
8 fn name(&self) -> &str {
9 "where"
10 }
11
12 fn description(&self) -> &str {
13 "Filter values based on a row condition."
14 }
15
16 fn extra_description(&self) -> &str {
17 r#"This command works similar to 'filter' but allows extra shorthands for working with
18tables, known as "row conditions". On the other hand, reading the condition from a variable is
19not supported."#
20 }
21
22 fn command_type(&self) -> CommandType {
23 CommandType::Keyword
24 }
25
26 fn signature(&self) -> nu_protocol::Signature {
27 Signature::build("where")
28 .input_output_types(vec![
29 (
30 Type::List(Box::new(Type::Any)),
31 Type::List(Box::new(Type::Any)),
32 ),
33 (Type::table(), Type::table()),
34 (Type::Range, Type::Any),
35 ])
36 .required(
37 "row_condition",
38 SyntaxShape::RowCondition,
39 "Filter condition.",
40 )
41 .allow_variants_without_examples(true)
42 .category(Category::Filters)
43 }
44
45 fn search_terms(&self) -> Vec<&str> {
46 vec!["filter", "find", "search", "condition"]
47 }
48
49 fn run(
50 &self,
51 engine_state: &EngineState,
52 stack: &mut Stack,
53 call: &Call,
54 input: PipelineData,
55 ) -> Result<PipelineData, ShellError> {
56 let head = call.head;
57 let closure: Closure = call.req(engine_state, stack, 0)?;
58
59 let mut closure = ClosureEval::new(engine_state, stack, closure);
60
61 let metadata = input.metadata();
62 Ok(input
63 .into_iter_strict(head)?
64 .filter_map(move |value| {
65 match closure
66 .run_with_value(value.clone())
67 .and_then(|data| data.into_value(head))
68 {
69 Ok(cond) => cond.is_true().then_some(value),
70 Err(err) => Some(Value::error(err, head)),
71 }
72 })
73 .into_pipeline_data_with_metadata(head, engine_state.signals().clone(), metadata))
74 }
75
76 fn examples(&self) -> Vec<Example> {
77 vec![
78 Example {
79 description: "Filter rows of a table according to a condition",
80 example: "[{a: 1} {a: 2}] | where a > 1",
81 result: Some(Value::test_list(
82 vec![Value::test_record(record! {
83 "a" => Value::test_int(2),
84 })],
85 )),
86 },
87 Example {
88 description: "Filter items of a list according to a condition",
89 example: "[1 2] | where {|x| $x > 1}",
90 result: Some(Value::test_list(
91 vec![Value::test_int(2)],
92 )),
93 },
94 Example {
95 description: "List all files in the current directory with sizes greater than 2kb",
96 example: "ls | where size > 2kb",
97 result: None,
98 },
99 Example {
100 description: "List only the files in the current directory",
101 example: "ls | where type == file",
102 result: None,
103 },
104 Example {
105 description: "List all files with names that contain \"Car\"",
106 example: "ls | where name =~ \"Car\"",
107 result: None,
108 },
109 Example {
110 description: "List all files that were modified in the last two weeks",
111 example: "ls | where modified >= (date now) - 2wk",
112 result: None,
113 },
114 Example {
115 description: "Find files whose filenames don't begin with the correct sequential number",
116 example: "ls | where type == file | sort-by name --natural | enumerate | where {|e| $e.item.name !~ $'^($e.index + 1)' } | each {|| get item }",
117 result: None,
118 },
119 Example {
120 description: r#"Find case-insensitively files called "readme", without an explicit closure"#,
121 example: "ls | where ($it.name | str downcase) =~ readme",
122 result: None,
123 },
124 Example {
125 description: "same as above but with regex only",
126 example: "ls | where name =~ '(?i)readme'",
127 result: None,
128 }
129
130
131 ]
132 }
133}
134
135#[cfg(test)]
136mod test {
137 use super::*;
138
139 #[test]
140 fn test_examples() {
141 use crate::test_examples;
142
143 test_examples(Where {})
144 }
145}