tabiew 0.13.1

A lightweight TUI application to view and query tabular data files, such as CSV, TSV, and parquet.
use std::fmt::Display;

use polars::{frame::DataFrame, prelude::DataType};

use crate::{
    handler::message::Message,
    tui::{
        pickers::{search_picker::SearchPicker, text_picker::TextPicker},
        popups::multi_step_overlay::{MultiStepOverlay, OverlayStep},
        widgets::input::InputType,
    },
};

const DEFAULT_BUCKET_COUNT: &str = "24";

pub type HistogramBuilder = MultiStepOverlay<State>;

#[derive(Debug)]
pub enum State {
    PickColumn {
        picker: SearchPicker<ColumnNameType>,
    },
    PickBucketCount {
        column: String,
        picker: TextPicker,
    },
}

impl State {
    pub fn new(df: &DataFrame) -> Self {
        let items = df
            .columns()
            .iter()
            .filter(|col| {
                let dtype = col.dtype();
                dtype.is_numeric() || dtype.is_string() || dtype.is_bool()
            })
            .map(|col| ColumnNameType(col.name().to_string(), col.dtype().to_owned()))
            .collect();

        State::PickColumn {
            picker: SearchPicker::new(items).with_title("Column"),
        }
    }
}

impl OverlayStep for State {
    fn next(self) -> Self {
        match self {
            State::PickColumn { picker } => {
                if let Some(ColumnNameType(name, dtype)) = picker.selected_item() {
                    if dtype.is_string() {
                        Message::PaneShowHistogram(name.clone(), 0).enqueue();

                        State::PickColumn { picker }
                    } else {
                        State::PickBucketCount {
                            column: name.to_owned(),
                            picker: TextPicker::default()
                                .with_title("Buckets")
                                .with_input_type(InputType::Numeric)
                                .with_value(DEFAULT_BUCKET_COUNT.to_owned()),
                        }
                    }
                } else {
                    State::PickColumn { picker }
                }
            }
            State::PickBucketCount { column, picker } => {
                let buckets = picker.value().parse().unwrap_or(1).max(1);
                Message::PaneShowHistogram(column.clone(), buckets).enqueue();
                State::PickBucketCount { column, picker }
            }
        }
    }

    fn responder(&mut self) -> &mut dyn crate::tui::component::Component {
        match self {
            State::PickColumn { picker } => picker,
            State::PickBucketCount { column: _, picker } => picker,
        }
    }
}

#[derive(Debug)]
pub struct ColumnNameType(String, DataType);

impl Display for ColumnNameType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Display::fmt(&self.0, f)
    }
}