jtool-grep 0.3.0

notebook-specific grep tool for jtool
Documentation
//! Core types for grep functionality

use serde::{Deserialize, Serialize};

/// A single match found in a notebook
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Match {
    /// Cell index (0-based)
    pub cell_index: usize,

    /// Cell number (1-based, human-friendly)
    pub cell_number: usize,

    /// Execution count (may be None for unexecuted cells)
    pub execution_count: Option<i32>,

    /// Type of match (input or output)
    pub match_type: MatchType,

    /// Line number within the cell (0-based)
    pub line_index: usize,

    /// Line number (1-based, human-friendly)
    pub line_number: usize,

    /// The full line content
    pub line_content: String,

    /// The matched portion of the line
    pub matched_text: String,

    /// Context lines before the match
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub context_before: Vec<String>,

    /// Context lines after the match
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub context_after: Vec<String>,
}

impl Match {
    /// Create a new Match with 1-based numbers calculated
    pub fn new(
        cell_index: usize,
        execution_count: Option<i32>,
        match_type: MatchType,
        line_index: usize,
        line_content: String,
        matched_text: String,
    ) -> Self {
        Self {
            cell_index,
            cell_number: cell_index + 1,
            execution_count,
            match_type,
            line_index,
            line_number: line_index + 1,
            line_content,
            matched_text,
            context_before: Vec::new(),
            context_after: Vec::new(),
        }
    }
}

/// Type of match (input or output)
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum MatchType {
    /// Match in cell input (source code)
    Input,
    /// Match in cell output
    Output,
}

impl std::fmt::Display for MatchType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            MatchType::Input => write!(f, "input"),
            MatchType::Output => write!(f, "output"),
        }
    }
}

/// Options for grep search
#[derive(Debug, Clone)]
pub struct GrepOptions {
    /// The pattern to search for
    pub pattern: String,

    /// Case insensitive search
    pub case_insensitive: bool,

    /// Search in cell inputs
    pub search_inputs: bool,

    /// Search in cell outputs
    pub search_outputs: bool,

    /// Number of context lines (for -C flag)
    pub context_lines: Option<usize>,

    /// Number of lines before match (for -B flag)
    pub context_before: Option<usize>,

    /// Number of lines after match (for -A flag)
    pub context_after: Option<usize>,

    /// Match only whole words
    pub word_regexp: bool,

    /// Treat pattern as fixed string (not regex)
    pub fixed_strings: bool,

    /// Show only the matching part of lines
    pub only_matching: bool,

    /// Invert match (show non-matching lines)
    pub invert_match: bool,

    /// Maximum number of matches per notebook
    pub max_count: Option<usize>,

    /// Filter: only search code cells
    pub code_cells_only: bool,

    /// Filter: only search markdown cells
    pub markdown_cells_only: bool,

    /// Filter: only search raw cells
    pub raw_cells_only: bool,

    /// Filter: only search executed cells (have execution_count)
    pub executed_only: bool,

    /// Filter: only search non-executed cells
    pub not_executed_only: bool,

    /// Filter: only search stream outputs
    pub stream_output_only: bool,

    /// Filter: only search error outputs
    pub error_output_only: bool,

    /// Filter: only search result outputs
    pub result_output_only: bool,

    /// Only search notebooks matching glob pattern
    pub glob_pattern: Option<String>,

    /// Exclude notebooks matching pattern
    pub exclude_pattern: Option<String>,
}

impl Default for GrepOptions {
    fn default() -> Self {
        Self {
            pattern: String::new(),
            case_insensitive: false,
            search_inputs: true,
            search_outputs: true,
            context_lines: None,
            context_before: None,
            context_after: None,
            word_regexp: false,
            fixed_strings: false,
            only_matching: false,
            invert_match: false,
            max_count: None,
            code_cells_only: false,
            markdown_cells_only: false,
            raw_cells_only: false,
            executed_only: false,
            not_executed_only: false,
            stream_output_only: false,
            error_output_only: false,
            result_output_only: false,
            glob_pattern: None,
            exclude_pattern: None,
        }
    }
}

/// Result of a grep search
#[derive(Debug, Clone, Serialize)]
pub struct GrepResult {
    /// Path to the notebook
    pub notebook: String,

    /// All matches found
    pub matches: Vec<Match>,
}

impl GrepResult {
    pub fn new(notebook: String) -> Self {
        Self {
            notebook,
            matches: Vec::new(),
        }
    }

    pub fn is_empty(&self) -> bool {
        self.matches.is_empty()
    }

    pub fn match_count(&self) -> usize {
        self.matches.len()
    }
}