rtlibs-tui 0.1.5

rtools library: ratatui widgets
Documentation
use ratatui::layout::Position;
use rtlibs_utils::time::date;
use time::Date;
use time::Month;

use crate::widgets::InputNumberState;

#[derive(Debug)]
pub(crate) enum InputDateFocus
{
    Day,
    Month,
    Year,
}

#[derive(Debug)]
pub struct InputDateState
{
    pub(crate) label: Option<String>,
    pub(crate) input_mode: InputDateFocus,
    pub(crate) input_day: InputNumberState,
    pub(crate) input_month: InputNumberState,
    pub(crate) input_year: InputNumberState,
    pub(crate) min_value: Option<Date>,
    pub(crate) max_value: Option<Date>,
}

impl std::default::Default for InputDateState
{
    fn default() -> Self
    {
        Self::new()
    }
}

impl InputDateState
{
    pub fn new() -> Self
    {
        Self {
            label: None,
            input_mode: InputDateFocus::Day,
            input_day: InputNumberState::new()
                .with_max_length(2)
                .min(1)
                .max(31),
            input_month: InputNumberState::new()
                .with_max_length(2)
                .min(1)
                .max(12),
            input_year: InputNumberState::new()
                .with_max_length(4)
                .min(1900)
                .max(2500),
            min_value: None,
            max_value: None,
        }
    }

    pub fn input(&self) -> Option<Date>
    {
        let day: u8 = match self
            .input_day
            .input()
            .filter(|i| *i != 0)
            .and_then(
                |i| {
                    i.try_into()
                        .ok()
                },
            )
        {
            Some(value) => value,
            None => return None,
        };

        let month: u8 = match self
            .input_month
            .input()
            .filter(|i| *i >= 1 && *i <= 12)
            .and_then(
                |i| {
                    i.try_into()
                        .ok()
                },
            )
        {
            Some(value) => value,
            None => return None,
        };

        let month = Month::January.nth_next(month - 1);

        let year: i32 = match self
            .input_year
            .input()
            .and_then(
                |i| {
                    i.try_into()
                        .ok()
                },
            )
        {
            Some(value) => value,
            None => return None,
        };

        let date = match Date::from_calendar_date(
            year, month, day,
        )
        {
            Ok(value) => value,
            Err(_) => return None,
        };

        if let Some(min) = self.min_value
        {
            if date < min
            {
                return None;
            }
        }

        if let Some(max) = self.max_value
        {
            if date > max
            {
                return None;
            }
        }

        Some(date)
    }

    pub fn set_label<S>(
        &mut self,
        label: S,
    ) where
        S: AsRef<str>,
    {
        self.label = Some(
            label
                .as_ref()
                .to_string(),
        );
    }

    pub fn remove_label(&mut self)
    {
        self.label
            .take();
    }

    pub fn with_label<S>(
        mut self,
        label: S,
    ) -> Self
    where
        S: AsRef<str>,
    {
        self.set_label(label);
        self
    }

    pub fn max(
        mut self,
        value: Date,
    ) -> Self
    {
        self.max_value = Some(value);
        self
    }

    pub fn min(
        mut self,
        value: Date,
    ) -> Self
    {
        self.min_value = Some(value);
        self
    }

    pub fn set(
        &mut self,
        value: Date,
    )
    {
        let (day, month, year) = date::to_dmy(value);
        self.input_day
            .set_str(format!("{day:02}"));
        self.input_month
            .set_str(format!("{month:02}"));
        self.input_year
            .set_str(format!("{year:04}"));
        self.input_mode = InputDateFocus::Day;
    }

    pub fn reset_cursor(&mut self)
    {
        self.input_day
            .reset_cursor();
        self.input_month
            .reset_cursor();
        self.input_year
            .reset_cursor();
    }

    pub fn with_default(
        mut self,
        value: Date,
    ) -> Self
    {
        self.set(value);
        self
    }

    pub fn cursor(&self) -> Position
    {
        match self.input_mode
        {
            InputDateFocus::Day => self
                .input_day
                .cursor(),
            InputDateFocus::Month => self
                .input_month
                .cursor(),
            InputDateFocus::Year => self
                .input_year
                .cursor(),
        }
    }

    pub fn clear(&mut self)
    {
        self.input_day
            .clear();
        self.input_month
            .clear();
        self.input_year
            .clear();
    }
}