tatuin-core 0.2.0

Core lib of the Tatuin project
Documentation
// SPDX-License-Identifier: MIT

use super::{filter, project::Project as ProjectTrait, task_patch::DuePatchItem};
use chrono::{DateTime, prelude::*};
use colored::Colorize;
use serde::{Deserialize, Serialize};
use std::{
    any::Any,
    cmp::Ordering,
    fmt::{self, Write},
};

pub type DateTimeUtc = DateTime<Utc>;

#[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum State {
    Unknown(char),
    #[default]
    Uncompleted,
    Completed,
    InProgress,
}

impl fmt::Display for State {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            State::Completed => write!(f, ""),
            State::Uncompleted => write!(f, " "),
            State::InProgress => write!(f, ""),
            State::Unknown(x) => f.write_char(*x),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)]
pub enum Priority {
    Lowest,
    Low,
    #[default]
    Normal,
    Medium,
    High,
    Highest,
}

impl Priority {
    pub fn values() -> Vec<Priority> {
        vec![
            Priority::Lowest,
            Priority::Low,
            Priority::Normal,
            Priority::Medium,
            Priority::High,
            Priority::Highest,
        ]
    }
}

impl fmt::Display for Priority {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{self:?}")
    }
}

#[derive(Default, Debug, Clone)]
pub struct PatchPolicy {
    pub is_editable: bool,
    pub available_states: Vec<State>,
    pub available_priorities: Vec<Priority>,
    pub available_due_items: Vec<DuePatchItem>,
}

#[allow(dead_code)]
pub trait Task: Send + Sync {
    fn id(&self) -> String;
    fn text(&self) -> String;

    fn description(&self) -> Option<String> {
        None
    }

    fn priority(&self) -> Priority {
        Priority::Normal
    }

    fn state(&self) -> State;
    fn created_at(&self) -> Option<DateTimeUtc> {
        None
    }
    fn updated_at(&self) -> Option<DateTimeUtc> {
        None
    }
    fn completed_at(&self) -> Option<DateTimeUtc> {
        None
    }
    fn due(&self) -> Option<DateTimeUtc> {
        None
    }
    fn place(&self) -> String {
        String::new()
    }

    fn url(&self) -> String {
        String::new()
    }

    fn labels(&self) -> Vec<String> {
        Vec::new()
    }

    fn provider(&self) -> String;

    fn project(&self) -> Option<Box<dyn ProjectTrait>>;

    fn as_any(&self) -> &dyn Any;

    fn clone_boxed(&self) -> Box<dyn Task>;

    fn const_patch_policy(&self) -> PatchPolicy {
        PatchPolicy {
            is_editable: false,
            available_states: vec![State::Uncompleted, State::Completed, State::InProgress],
            available_priorities: Priority::values(),
            available_due_items: DuePatchItem::values(),
        }
    }

    fn patch_policy(&self) -> PatchPolicy {
        let mut pp = self.const_patch_policy();

        let s = self.state();
        pp.available_states.retain(|e| e != &s);
        let p = self.priority();
        pp.available_priorities.retain(|e| e != &p);
        pp
    }
}

pub fn datetime_to_str<Tz: TimeZone>(t: Option<DateTimeUtc>, tz: &Tz) -> String
where
    <Tz as TimeZone>::Offset: std::fmt::Display,
{
    if let Some(d) = t {
        if d.time() == chrono::NaiveTime::default() {
            return d.format("%Y-%m-%d").to_string();
        }

        return d.with_timezone(tz).format("%Y-%m-%d %H:%M:%S %Z").to_string();
    }

    String::from("-")
}

pub fn format(t: &dyn Task) -> String {
    format!(
        "- [{}] {} ({}) ({})",
        t.state(),
        t.text(),
        format!("due: {}", datetime_to_str(t.due(), &Local::now().timezone())).blue(),
        t.place().green()
    )
}

pub fn due_group(due: &Option<DateTimeUtc>) -> filter::Due {
    match due {
        Some(d) => {
            let now = chrono::Utc::now().date_naive();
            match d.date_naive().cmp(&now) {
                Ordering::Less => filter::Due::Overdue,
                Ordering::Equal => filter::Due::Today,
                Ordering::Greater => filter::Due::Future,
            }
        }
        None => filter::Due::NoDate,
    }
}