nu-command 0.115.1

Nushell's built-in commands
Documentation
use super::state::{WatchStreamOptions, stream_watch};
use nu_engine::command_prelude::*;
use std::time::Duration;

#[derive(Clone)]
pub struct IdxWatch;

impl Command for IdxWatch {
    fn name(&self) -> &str {
        "idx watch"
    }

    fn signature(&self) -> Signature {
        Signature::build(self.name())
            .optional(
                "pattern",
                SyntaxShape::String,
                "Base-relative glob, path, or directory to watch. Omit or pass empty to watch the whole indexed tree.",
            )
            .named(
                "ignore",
                SyntaxShape::List(Box::new(SyntaxShape::String)),
                "List of globs or path-prefixes to exclude.",
                Some('i'),
            )
            .named(
                "timeout",
                SyntaxShape::Duration,
                "Stop streaming after this duration.",
                Some('t'),
            )
            .named(
                "max-events",
                SyntaxShape::Int,
                "Stop after emitting this many events.",
                Some('n'),
            )
            .input_output_types(vec![(
                Type::Nothing,
                Type::Table(
                    vec![
                        ("kind".into(), Type::String),
                        ("path".into(), Type::String),
                    ]
                    .into(),
                ),
            )])
            .category(Category::FileSystem)
    }

    fn description(&self) -> &str {
        "Stream filesystem change events from the live idx index."
    }

    fn extra_description(&self) -> &str {
        "Requires a live runtime initialized without `--no-watch`. \
Events are debounced by fff-search and emitted as records with `kind` (`created`, `modified`, `removed`, `rescan`) \
and absolute `path`. Gitignored and other index-ignored files do not produce events. \
Patterns must be inside the indexed base path. Use plain `watch` for ad-hoc path watching without an index."
    }

    fn search_terms(&self) -> Vec<&str> {
        vec!["watcher", "filesystem", "notify", "events"]
    }

    fn examples(&self) -> Vec<Example<'_>> {
        vec![
            Example {
                description: "Watch the whole indexed tree after initializing idx.",
                example: "idx init .; idx watch",
                result: None,
            },
            Example {
                description: "Watch only Rust files, ignoring a vendor-style path prefix.",
                example: r#"idx watch "**/*.rs" --ignore [target]"#,
                result: None,
            },
            Example {
                description: "Take action on modified files in a pipeline.",
                example: r#"idx watch | where kind == "modified" | each { |e| print $"changed: ($e.path)" }"#,
                result: None,
            },
            Example {
                description: "Stop after a single event (useful in scripts).",
                example: "idx watch --max-events 1",
                result: None,
            },
            Example {
                description: "Stop after a duration if no more events are needed.",
                example: "idx watch --timeout 5sec",
                result: None,
            },
        ]
    }

    fn run(
        &self,
        engine_state: &EngineState,
        stack: &mut Stack,
        call: &Call,
        _input: PipelineData,
    ) -> Result<PipelineData, ShellError> {
        let pattern: Option<String> = call.opt(engine_state, stack, 0)?;
        let ignore = call
            .get_flag::<Vec<String>>(engine_state, stack, "ignore")?
            .unwrap_or_default();
        let timeout: Option<Duration> = call.get_flag(engine_state, stack, "timeout")?;
        let max_events: Option<i64> = call.get_flag(engine_state, stack, "max-events")?;

        let max_events = max_events
            .map(|value| {
                usize::try_from(value)
                    .map_err(|_| ShellError::NeedsPositiveValue { span: call.head })
            })
            .transpose()?;

        stream_watch(WatchStreamOptions {
            pattern: pattern.unwrap_or_default(),
            ignore,
            timeout,
            max_events,
            span: call.head,
            signals: engine_state.signals().clone(),
        })
    }
}