nu_command/filters/
where_.rs

1use 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}