todo-txt 3.0.1

Parser for the todo.txt format
Documentation
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[cfg_attr(
    feature = "serde-support",
    derive(serde_derive::Serialize, serde_derive::Deserialize)
)]
pub struct Priority(u8);

impl Priority {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn from(priority: u8) -> Self {
        Self(priority)
    }

    pub fn lowest() -> Self {
        Self::default()
    }

    pub fn is_lowest(&self) -> bool {
        self == &Self::lowest()
    }
}

impl Default for Priority {
    fn default() -> Self {
        26.into()
    }
}

impl From<Priority> for char {
    fn from(priority: Priority) -> Self {
        (priority.0 + b'A') as char
    }
}

impl TryFrom<char> for Priority {
    type Error = crate::Error;

    fn try_from(value: char) -> Result<Self, Self::Error> {
        let c = value.to_ascii_uppercase() as u8;

        if (b'A'..=b'Z').contains(&c) {
            Ok(Self(c - b'A'))
        } else {
            Err(crate::Error::InvalidPriority(value))
        }
    }
}

impl std::cmp::PartialEq<char> for Priority {
    fn eq(&self, other: &char) -> bool {
        let c = char::from(self.clone());

        c.eq_ignore_ascii_case(other)
    }
}

impl From<Priority> for u8 {
    fn from(priority: Priority) -> Self {
        priority.0
    }
}

impl From<u8> for Priority {
    fn from(c: u8) -> Self {
        Self::from(c)
    }
}

impl std::cmp::PartialEq<u8> for Priority {
    fn eq(&self, other: &u8) -> bool {
        self.0 == *other
    }
}

impl std::fmt::Display for Priority {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.is_lowest() {
            Ok(())
        } else {
            write!(f, "{}", (self.0 + b'A') as char)
        }
    }
}

impl std::cmp::PartialOrd for Priority {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl std::cmp::Ord for Priority {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.0.cmp(&other.0).reverse()
    }
}

#[cfg(test)]
mod test {
    #[test]
    fn from_char() {
        assert_eq!(crate::Priority::from(1), char::try_from('B').unwrap());
    }

    #[test]
    fn from_u8() {
        assert_eq!(crate::Priority::from(1), u8::from(1));
    }

    #[test]
    fn lowest() {
        let priority = crate::Priority::default();

        assert!(priority.is_lowest());
        assert_eq!(priority, 26);
    }

    #[test]
    fn display() {
        let priority = crate::Priority::from(1);
        assert_eq!(priority.to_string(), "B");

        let priority = crate::Priority::default();
        assert_eq!(priority.to_string(), "");
    }

    #[test]
    fn ord() {
        let a = crate::Priority::from(1);
        let b = crate::Priority::from(2);

        assert!(a > b);
    }

    #[test]
    fn eq() {
        let a = crate::Priority::from(0);

        assert_eq!(a, 'a');
        assert_eq!(a, 'A');
    }
}