tabiew 0.16.0

A lightweight TUI application to view and query tabular data files, such as CSV, TSV, and parquet.
use anyhow::anyhow;
use polars::frame::column::Column;

use crate::{
    AppResult,
    parsers::{
        bool_parser::BoolParser, date_parser::DateParser, datetime_parser::DatetimeParser,
        number_parser::NumberParser,
    },
};

#[derive(Default, Clone)]
pub struct AutoParser<'a> {
    bool_parser: Option<BoolParser>,
    date_parser: Option<DateParser>,
    datetime_parser: Option<DatetimeParser<'a>>,
    float_parser: Option<NumberParser>,
    int_parser: Option<NumberParser>,
}

impl<'a> AutoParser<'a> {
    pub fn with_bool(self, enabled: bool) -> Self {
        Self {
            bool_parser: enabled.then(BoolParser::default),
            ..self
        }
    }

    pub fn with_date(self, enabled: bool) -> Self {
        Self {
            date_parser: enabled.then(DateParser::default),
            ..self
        }
    }

    pub fn with_datetime(self, enabled: bool) -> Self {
        Self {
            datetime_parser: enabled.then(DatetimeParser::default),
            ..self
        }
    }

    pub fn with_float(self, enabled: bool) -> Self {
        Self {
            float_parser: enabled.then(NumberParser::float),
            ..self
        }
    }

    pub fn with_int(self, enabled: bool) -> Self {
        Self {
            int_parser: enabled.then(NumberParser::int),
            ..self
        }
    }

    pub fn parse_strict(&mut self, column: &Column) -> AppResult<Column> {
        self.parse_int(column)
            .or_else(|| self.parse_float(column))
            .or_else(|| self.parse_bool(column))
            .or_else(|| self.parse_date(column))
            .or_else(|| self.parse_datetime(column))
            .ok_or_else(|| anyhow!("Column '{}' could not be parsed", column.name()))
    }

    fn parse_datetime(&mut self, column: &Column) -> Option<Column> {
        let parser = self.datetime_parser.as_mut()?;
        parser.set_format(None);
        if let Ok(s) = parser.parse_strict(column) {
            return Some(s);
        }
        for fmt in crate::parsers::datetime_parser::PREDEFINED_FORMATS {
            parser.set_format(fmt);
            if let Ok(s) = parser.parse_strict(column) {
                return Some(s);
            }
        }
        None
    }

    fn parse_date(&mut self, column: &Column) -> Option<Column> {
        let parser = self.date_parser.as_mut()?;
        parser.set_format(None);
        if let Ok(s) = parser.parse_strict(column) {
            return Some(s);
        }
        for fmt in crate::parsers::date_parser::PREDEFINED_FORMATS {
            parser.set_format(fmt);
            if let Ok(s) = parser.parse_strict(column) {
                return Some(s);
            }
        }
        None
    }

    fn parse_bool(&self, column: &Column) -> Option<Column> {
        self.bool_parser.as_ref()?.parse_strict(column).ok()
    }

    fn parse_float(&self, column: &Column) -> Option<Column> {
        self.float_parser.as_ref()?.parse_strict(column).ok()
    }

    fn parse_int(&self, column: &Column) -> Option<Column> {
        self.int_parser.as_ref()?.parse_strict(column).ok()
    }
}