todotxt-tui 0.3.0

Todo.txt TUI is a highly customizable terminal-based application for managing your todo tasks. It follows the todo.txt format and offers a wide range of configuration options to suit your needs.
Documentation
use super::{
    parts::{PartStyle, Parts},
    ToDo,
};
use crate::config::Styles;
use regex::Regex;
use std::sync::LazyLock;
use todo_txt::task::Simple as Task;
use tui::style::Style;

static REGEX: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(?:^|\s)([+@#][^\s]+)").unwrap());

#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq))]
pub struct LineBlock {
    pub parts: Vec<Parts>,
    pub style: PartStyle,
}

impl LineBlock {
    pub fn new(parts: Vec<Parts>, part_style: Option<&PartStyle>) -> Self {
        Self {
            parts,
            style: part_style.cloned().unwrap_or_default(),
        }
    }

    pub fn fill(&self, task: &Task, todo: &ToDo, styles: &Styles) -> Option<Vec<(String, Style)>> {
        let style = self.style.get_style(task, styles).get_style();
        let string = self
            .parts
            .iter()
            .map(|part| part.fill(task, todo))
            .collect::<Option<String>>()?;
        if !self.style.to_colorize {
            return Some(vec![(string, style)]);
        }
        let mut last_index = 0;
        Some(
            REGEX
                .captures_iter(&string)
                .filter_map(|m| m.get(1))
                .flat_map(|m| {
                    let start = last_index;
                    last_index = m.end();
                    let category = string[m.start()..m.end()].to_string();
                    if (self.style.skip_projects && category.starts_with('+'))
                        || (self.style.skip_contexts && category.starts_with('@'))
                        || (self.style.skip_hashtags && category.starts_with('#'))
                    {
                        return vec![(string[start..m.start()].to_string(), style)];
                    }
                    vec![
                        (string[start..m.start()].to_string(), style),
                        if self.style.to_colorize {
                            let category_style = styles.get_category_style(&category).get_style();
                            (category, category_style)
                        } else {
                            (category, style)
                        },
                    ]
                })
                .collect::<Vec<_>>()
                .into_iter()
                .chain([(string[last_index..string.len()].to_string(), style)])
                .collect::<Vec<_>>(),
        )
    }
}