tabiew 0.13.1

A lightweight TUI application to view and query tabular data files, such as CSV, TSV, and parquet.
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use polars::frame::DataFrame;
use ratatui::widgets::Widget;

use crate::{
    handler::message::Message,
    misc::search::{self, Contain, Skim},
    tui::{component::Component, widgets::block::Block},
};

use super::widgets::input::Input;

#[derive(Debug)]
pub struct SearchBar {
    input: Input,
    searcher: Searcher,
    rollback_df: DataFrame,
}

impl SearchBar {
    pub fn exact(dataframe: DataFrame) -> Self {
        SearchBar {
            input: Default::default(),
            searcher: Searcher::Exact(search::Search::new(dataframe.clone(), Default::default())),
            rollback_df: dataframe,
        }
    }

    pub fn fuzzy(dataframe: DataFrame) -> Self {
        SearchBar {
            input: Default::default(),
            searcher: Searcher::Fuzzy(search::Search::new(dataframe.clone(), Default::default())),
            rollback_df: dataframe,
        }
    }

    pub fn searcher(&self) -> &Searcher {
        &self.searcher
    }

    pub fn into_rollback_df(self) -> DataFrame {
        self.rollback_df
    }

    pub fn value(&self) -> &str {
        self.input.value()
    }

    fn update_search(&mut self) {
        if self.input.value() != self.searcher.pattern() {
            match self.searcher {
                Searcher::Fuzzy(_) => {
                    self.searcher = Searcher::Fuzzy(search::Search::new(
                        self.rollback_df.clone(),
                        self.input.value().to_owned(),
                    ))
                }
                Searcher::Exact(_) => {
                    self.searcher = Searcher::Exact(search::Search::new(
                        self.rollback_df.clone(),
                        self.input.value().to_owned(),
                    ))
                }
            }
        }
    }
}

impl Component for SearchBar {
    fn render(
        &mut self,
        area: ratatui::prelude::Rect,
        buf: &mut ratatui::prelude::Buffer,
        focus_state: super::component::FocusState,
    ) {
        let title = match &self.searcher {
            Searcher::Fuzzy(_) => "Fuzzy Search",
            Searcher::Exact(_) => "Search",
        };
        let area = {
            let block = Block::default().title(title);
            let inner = block.inner(area);
            block.render(area, buf);
            inner
        };
        self.input.render(area, buf, focus_state);
    }

    fn handle(&mut self, event: KeyEvent) -> bool {
        if self.input.handle(event) {
            self.update_search();
            true
        } else {
            match (event.code, event.modifiers) {
                (KeyCode::Esc, KeyModifiers::NONE) => {
                    Message::PaneDismissModal.enqueue();
                    Message::PanePopDataFrame.enqueue();
                    true
                }
                (KeyCode::Enter, KeyModifiers::NONE) => {
                    Message::PaneDismissModal.enqueue();
                    true
                }
                _ => false,
            }
        }
    }
}

#[derive(Debug)]
pub enum Searcher {
    Fuzzy(search::Search<Skim>),
    Exact(search::Search<Contain>),
}

impl Searcher {
    pub fn pattern(&self) -> &str {
        match self {
            Searcher::Fuzzy(search) => search.pattern(),
            Searcher::Exact(search) => search.pattern(),
        }
    }

    pub fn latest(&self) -> Option<DataFrame> {
        match self {
            Searcher::Fuzzy(search) => search.latest(),
            Searcher::Exact(search) => search.latest(),
        }
    }
}