ttz/src/model.rs

102 lines
2.8 KiB
Rust
Raw Normal View History

2026-06-08 05:53:41 +00:00
use chrono::NaiveDateTime;
use color_eyre::Result;
use ratatui::widgets::TableState;
use ratatui_textarea::TextArea;
use unidecode::unidecode;
use crate::{
config::{Config, Location},
message::Message,
update::update,
};
/// App-wide Elm model.
#[derive(Debug)]
pub(crate) struct Model<'a> {
pub(crate) config: Config,
pub(crate) table_state: TableState,
pub(crate) table_rows: Vec<Vec<CellData>>,
pub(crate) running_state: RunningState,
pub(crate) focus_state: FocusState,
pub(crate) filter_textarea: TextArea<'a>,
}
impl<'a> Model<'a> {
/// Create new model from parsed runtime config.
pub(crate) fn from_config(config: Config) -> Self {
Self {
config,
table_state: Default::default(),
table_rows: Default::default(),
running_state: Default::default(),
focus_state: Default::default(),
filter_textarea: Default::default(),
}
}
/// Run Elm updates until they produce no further recursive messages.
pub(crate) fn update_loop(&mut self, msg: Option<Message>) -> Result<()> {
let mut maybe_msg = msg;
while let Some(msg) = maybe_msg {
maybe_msg = update(self, msg)?;
}
Ok(())
}
/// Compute list of visible locations based on filter value.
pub(crate) fn get_visible_locations(&self) -> Vec<Location> {
if self.focus_state == FocusState::Filter {
let filter_terms = self.get_filter_terms();
self.config
.locations
.iter()
.enumerate()
.filter(|(i, location)| {
*i == 0
|| filter_terms.iter().any(|filter_term| {
unidecode(&location.name.to_ascii_lowercase())
.contains(&unidecode(&filter_term.to_ascii_lowercase()))
})
})
.map(|(_, value)| value.clone())
.collect()
} else {
self.config.locations.to_vec()
}
}
fn get_filter_terms(&self) -> Vec<String> {
self.filter_textarea
.clone()
.into_lines()
.into_iter()
.next()
.unwrap_or_default()
.split('|')
.map(|value| value.trim().to_owned())
.collect()
}
}
#[derive(Debug, Default, Eq, PartialEq)]
pub(crate) enum RunningState {
#[default]
Running,
Done,
}
#[derive(Debug, Default, Eq, PartialEq)]
pub(crate) enum FocusState {
#[default]
Table,
Filter,
}
/// Computed data required to render a timetable cell.
#[derive(Debug)]
pub(crate) struct CellData {
pub(crate) is_daylight: bool,
pub(crate) is_current_time: bool,
pub(crate) datetime: NaiveDateTime,
}