Skip to main content

nu_command/filesystem/idx/
search.rs

1use super::state::stream_grep;
2use fff_search::GrepMode;
3use nu_engine::command_prelude::*;
4
5#[derive(Clone)]
6pub struct IdxSearch;
7
8impl Command for IdxSearch {
9    fn name(&self) -> &str {
10        "idx search"
11    }
12
13    fn signature(&self) -> Signature {
14        Signature::build(self.name())
15            .rest(
16                "pattern",
17                SyntaxShape::String,
18                "One or more search patterns.",
19            )
20            .switch("regex", "Use regular-expression matching mode.", Some('r'))
21            .switch("fuzzy", "Use fuzzy line-matching mode.", Some('f'))
22            .named(
23                "limit",
24                SyntaxShape::Int,
25                "Maximum number of matches to collect.",
26                Some('l'),
27            )
28            .input_output_types(vec![(Type::Nothing, Type::List(Box::new(Type::record())))])
29            .category(Category::FileSystem)
30    }
31
32    fn description(&self) -> &str {
33        "Search indexed file contents."
34    }
35
36    fn extra_description(&self) -> &str {
37        "Mode selection: plain text is the default and treats each pattern literally, `--regex` evaluates the patterns as regular expressions, and `--fuzzy` performs approximate line matching."
38    }
39
40    fn examples(&self) -> Vec<Example<'_>> {
41        vec![
42            Example {
43                description: "Search indexed file contents for a plain text pattern",
44                example: "idx search hello",
45                result: None,
46            },
47            Example {
48                description: "Search using a regular expression",
49                example: "idx search --regex 'fn \\w+'",
50                result: None,
51            },
52            Example {
53                description: "Search with multiple patterns simultaneously",
54                example: "idx search TODO FIXME HACK",
55                result: None,
56            },
57        ]
58    }
59
60    fn run(
61        &self,
62        engine_state: &EngineState,
63        stack: &mut Stack,
64        call: &Call,
65        _input: PipelineData,
66    ) -> Result<PipelineData, ShellError> {
67        let patterns: Vec<String> = call.rest(engine_state, stack, 0)?;
68        if patterns.is_empty() {
69            return Err(ShellError::MissingParameter {
70                param_name: "pattern".to_string(),
71                span: call.head,
72            });
73        }
74
75        let regex = call.has_flag(engine_state, stack, "regex")?;
76        let fuzzy = call.has_flag(engine_state, stack, "fuzzy")?;
77
78        if regex && fuzzy {
79            return Err(ShellError::IncompatibleParameters {
80                left_message: "--regex cannot be used with --fuzzy".to_string(),
81                left_span: call.get_flag_span(stack, "regex").unwrap_or(call.head),
82                right_message: "--fuzzy cannot be used with --regex".to_string(),
83                right_span: call.get_flag_span(stack, "fuzzy").unwrap_or(call.head),
84            });
85        }
86
87        let limit = call
88            .get_flag::<i64>(engine_state, stack, "limit")?
89            .and_then(|v| usize::try_from(v).ok())
90            .unwrap_or(50);
91
92        let mode = if fuzzy {
93            GrepMode::Fuzzy
94        } else if regex {
95            GrepMode::Regex
96        } else {
97            GrepMode::PlainText
98        };
99
100        let signals = engine_state.signals();
101        stream_grep(&patterns, mode, limit, call.head, signals)
102    }
103}