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 anyhow::{anyhow, Error};
use clap::ValueEnum;
use serde::{Deserialize, Serialize};
use std::{fmt::Display, str::FromStr};
use tui::{style::Modifier, widgets::BorderType};

macro_rules! impl_display {
    ($enum_name:ident) => {
        impl std::fmt::Display for $enum_name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                f.write_str(&super::parsers::enum_debug_display_parser(&format!(
                    "{:?}",
                    self
                )))?;
                Ok(())
            }
        }
    };
}

#[derive(Default, Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize, ValueEnum)]
pub enum PasteBehavior {
    AsKeys,
    #[default]
    Insert,
    None,
}

impl_display!(PasteBehavior);

#[derive(Serialize, Deserialize, ValueEnum, Clone, Debug, PartialEq, Eq, Default)]
pub enum SetFinalDateType {
    Override,
    #[default]
    OnlyMissing,
    Never,
}

impl_display!(SetFinalDateType);

/// Represents the possible sorting options for tasks.
#[derive(Clone, Copy, Serialize, Deserialize, ValueEnum, Debug, PartialEq, Eq)]
pub enum TaskSort {
    Reverse,
    Priority,
    Alphanumeric,
    AlphanumericReverse,
    CreateDateAsc,
    CreateDateDesc,
    FinishDateAsc,
    FinishDateDesc,
    DueDateAsc,
    DueDateDesc,
}

impl_display!(TaskSort);

#[derive(Clone, Copy, Serialize, Deserialize, Default, ValueEnum, Debug, PartialEq, Eq)]
pub enum SavePolicy {
    ManualOnly,
    AutoSave,
    #[default]
    OnChange,
}

impl_display!(SavePolicy);

/// Serialization and deserialization support for the TUI text modifier type.
///
/// This enum is used to serialize and deserialize TUI `Modifier` objects.
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
pub enum TextModifier {
    Bold,
    Italic,
    Underlined,
}

impl FromStr for TextModifier {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "bold" => Ok(Self::Bold),
            "italic" => Ok(Self::Italic),
            "underline" => Ok(Self::Underlined),
            _ => Err(anyhow!("Modifier '{s}' is invalid.")),
        }
    }
}

impl From<TextModifier> for Modifier {
    fn from(val: TextModifier) -> Self {
        use TextModifier::*;
        match val {
            Bold => Modifier::BOLD,
            Italic => Modifier::ITALIC,
            Underlined => Modifier::UNDERLINED,
        }
    }
}

#[derive(Serialize, Deserialize, ValueEnum, Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum WidgetBorderType {
    Plain,
    #[default]
    Rounded,
    Double,
    Thick,
}

impl Display for WidgetBorderType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&super::parsers::enum_debug_display_parser(&format!(
            "{:?}",
            self
        )))?;
        Ok(())
    }
}

impl From<WidgetBorderType> for BorderType {
    fn from(value: WidgetBorderType) -> Self {
        match value {
            WidgetBorderType::Plain => Self::Plain,
            WidgetBorderType::Rounded => Self::Rounded,
            WidgetBorderType::Double => Self::Double,
            WidgetBorderType::Thick => Self::Thick,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use anyhow::Result;

    #[test]
    fn set_final_date_type_display() {
        assert_eq!(format!("{}", SetFinalDateType::Override), "override");
        assert_eq!(format!("{}", SetFinalDateType::OnlyMissing), "only-missing");
        assert_eq!(format!("{}", SetFinalDateType::Never), "never");
    }

    #[test]
    fn task_sort_display() {
        assert_eq!(format!("{}", TaskSort::Reverse), "reverse");
        assert_eq!(format!("{}", TaskSort::Priority), "priority");
        assert_eq!(format!("{}", TaskSort::Alphanumeric), "alphanumeric");
        assert_eq!(
            format!("{}", TaskSort::AlphanumericReverse),
            "alphanumeric-reverse"
        );
        assert_eq!(format!("{}", TaskSort::CreateDateAsc), "create-date-asc");
        assert_eq!(format!("{}", TaskSort::CreateDateDesc), "create-date-desc");
        assert_eq!(format!("{}", TaskSort::FinishDateAsc), "finish-date-asc");
        assert_eq!(format!("{}", TaskSort::FinishDateDesc), "finish-date-desc");
        assert_eq!(format!("{}", TaskSort::DueDateAsc), "due-date-asc");
        assert_eq!(format!("{}", TaskSort::DueDateDesc), "due-date-desc");
    }

    #[test]
    fn fom_str_text_modifier() -> Result<()> {
        assert_eq!(TextModifier::from_str("bold")?, TextModifier::Bold);
        assert_eq!(TextModifier::from_str("iTALic")?, TextModifier::Italic);
        assert!(TextModifier::from_str("Some random data").is_err());

        Ok(())
    }

    #[test]
    fn to_text_modifier() {
        let bold = TextModifier::Bold;
        assert_eq!(Modifier::from(bold), Modifier::BOLD);

        let italic = TextModifier::Italic;
        assert_eq!(Modifier::from(italic), Modifier::ITALIC);

        let underline = TextModifier::Underlined;
        assert_eq!(Modifier::from(underline), Modifier::UNDERLINED);
    }

    #[test]
    fn widget_border_type_display() {
        assert_eq!(format!("{}", WidgetBorderType::Plain), "plain");
        assert_eq!(format!("{}", WidgetBorderType::Rounded), "rounded");
        assert_eq!(format!("{}", WidgetBorderType::Double), "double");
        assert_eq!(format!("{}", WidgetBorderType::Thick), "thick");
    }

    #[test]
    fn widget_border_type_into_border_type() {
        assert_eq!(BorderType::from(WidgetBorderType::Plain), BorderType::Plain);
        assert_eq!(
            BorderType::from(WidgetBorderType::Rounded),
            BorderType::Rounded
        );
        assert_eq!(
            BorderType::from(WidgetBorderType::Double),
            BorderType::Double
        );
        assert_eq!(BorderType::from(WidgetBorderType::Thick), BorderType::Thick);
    }
}