lib-todoist 0.1.1

(Not affiliated with Todoist) Todoist lib; bringing Todoist to the terminal.
Documentation
use std::{
    convert::TryFrom,
    fmt::{self, Display, Formatter},
};

use chrono_elapsed::*;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};

use super::DueDateTime;

#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Task {
    pub id: usize,
    pub project_id: usize,
    pub content: String,
    pub description: Option<String>,
    pub completed: State,
    pub label_ids: Option<Vec<usize>>,
    pub parent_id: Option<usize>,
    pub order: usize,
    pub priority: Priority,
    #[serde(rename = "due")] /* Renamed to `due` on (de&)serialisation. */
    pub due_datetime_obj: Option<DueDateTime>,
    pub url: String,
    // IGNORED: section_id; comment_count; assignee; assigner
}

impl Display for Task {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{completed} {content} {due_in} [{id}]",
            completed = self.completed,
            content = self.content,
            due_in = "", // TODO
            id = self.id
        )
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum Priority {
    Low = 1,
    Medium = 2,
    High = 3,
    Urgent = 4,
}

impl Default for Priority {
    fn default() -> Self {
        Self::Low
    }
}

impl From<Priority> for u8 {
    fn from(prio: Priority) -> Self {
        match prio {
            Priority::Low => 1,
            Priority::Medium => 2,
            Priority::High => 3,
            Priority::Urgent => 4,
        }
    }
}

impl TryFrom<u8> for Priority {
    type Error = &'static str;
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            1 => Ok(Self::Low),
            2 => Ok(Self::Medium),
            3 => Ok(Self::High),
            4 => Ok(Self::Urgent),
            _ => Err("`task::Priority` can only be represented by numbers 1-4 (low to urgent)"),
        }
    }
}

impl From<Priority> for String {
    fn from(prio: Priority) -> Self {
        match prio {
            Priority::Low => Self::from("low"),
            Priority::Medium => Self::from("medium"),
            Priority::High => Self::from("high"),
            Priority::Urgent => Self::from("urgent"),
        }
    }
}

impl TryFrom<&str> for Priority {
    type Error = &'static str;
    fn try_from(value: &str) -> Result<Self, Self::Error> {
        match value.trim() {
            "low" => Ok(Self::Low),
            "medium" => Ok(Self::Medium),
            "high" => Ok(Self::High),
            "urgent" => Ok(Self::Urgent),
            _ => Err("`task::Priority` valid strings are: 'low', 'medium', 'high' and 'urgent'"),
        }
    }
}

impl Display for Priority {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Priority::Low => write!(f, "{}", String::from(Priority::Low)),
            Priority::Medium => write!(f, "{}", String::from(Priority::Medium)),
            Priority::High => write!(f, "{}", String::from(Priority::High)),
            Priority::Urgent => write!(f, "{}", String::from(Priority::Urgent)),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize_repr, Deserialize_repr)]
#[repr(u8)]
pub enum State {
    Incomplete = 0, // Closed
    Complete = 1,   // Open
}

impl State {
    pub fn as_char(&self) -> char {
        char::from(*self)
    }
    pub fn as_bool(&self) -> bool {
        bool::from(*self)
    }
}

impl Default for State {
    fn default() -> Self {
        Self::Incomplete
    }
}

impl From<State> for bool {
    fn from(state: State) -> Self {
        match state {
            State::Complete => true,
            State::Incomplete => false,
        }
    }
}

impl From<bool> for State {
    fn from(b: bool) -> Self {
        match b {
            true => Self::Complete,
            false => Self::Incomplete,
        }
    }
}

impl From<State> for char {
    fn from(state: State) -> Self {
        match state {
            State::Complete => '',
            State::Incomplete => '',
        }
    }
}

impl TryFrom<char> for State {
    type Error = &'static str;
    fn try_from(value: char) -> Result<Self, Self::Error> {
        match value {
            '' => Ok(Self::Complete),
            '' => Ok(Self::Incomplete),
            _ => Err("invalid char for `task::State`"),
        }
    }
}

impl Display for State {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            Self::Complete => write!(f, "{}", char::from(Self::Complete)),
            Self::Incomplete => write!(f, "{}", char::from(Self::Incomplete)),
        }
    }
}