Documentation
/*
==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--==--

Dia-Time

Copyright (C) 2018-2022, 2024  Anonymous

There are several releases over multiple years,
they are listed as ranges, such as: "2018-2022".

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.

::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--::--
*/

//! # Weekday

use {
    core::{
        fmt::{self, Debug, Display, Formatter},
        ops::Deref,
        str::FromStr,
    },
    crate::{Error, Result as CrateResult},
};

#[cfg(test)]
mod tests;

/// # Weekday
///
/// ## Notes
///
/// -   First day of the week is Monday (see [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)).
/// -   The days' names are English only. That applies to implementations of [`FromStr`][trait:core/str/FromStr],
///     [`Deref<Target=str>`][trait:core/ops/Deref]...
/// -   Implementation of `Deref<Target=str>` is same as [`Display`][trait:core/fmt/Display]'s, but it's faster because it provides references
///     to static strings.
///
/// [trait:core/fmt/Display]: https://doc.rust-lang.org/core/fmt/trait.Display.html
/// [trait:core/ops/Deref]: https://doc.rust-lang.org/core/ops/trait.Deref.html
/// [trait:core/str/FromStr]: https://doc.rust-lang.org/core/str/trait.FromStr.html
#[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Copy)]
pub enum Weekday {

    /// # Monday,
    Monday,

    /// # Tuesday,
    Tuesday,

    /// # Wednesday,
    Wednesday,

    /// # Thursday,
    Thursday,

    /// # Friday
    Friday,

    /// # Saturday
    Saturday,

    /// # Sunday
    Sunday,

}

impl Weekday {

    /// # Gets next day
    pub const fn next(&self) -> Option<Self> {
        match self {
            Weekday::Monday => Some(Weekday::Tuesday),
            Weekday::Tuesday => Some(Weekday::Wednesday),
            Weekday::Wednesday => Some(Weekday::Thursday),
            Weekday::Thursday => Some(Weekday::Friday),
            Weekday::Friday => Some(Weekday::Saturday),
            Weekday::Saturday => Some(Weekday::Sunday),
            Weekday::Sunday => None,
        }
    }

    /// # Gets next day
    ///
    /// Next of Sunday will be Monday.
    pub const fn wrapping_next(&self) -> Self {
        match self {
            Weekday::Monday => Weekday::Tuesday,
            Weekday::Tuesday => Weekday::Wednesday,
            Weekday::Wednesday => Weekday::Thursday,
            Weekday::Thursday => Weekday::Friday,
            Weekday::Friday => Weekday::Saturday,
            Weekday::Saturday => Weekday::Sunday,
            Weekday::Sunday => Weekday::Monday,
        }
    }

    /// # Gets last day
    pub const fn last(&self) -> Option<Self> {
        match self {
            Weekday::Monday => None,
            Weekday::Tuesday => Some(Weekday::Monday),
            Weekday::Wednesday => Some(Weekday::Tuesday),
            Weekday::Thursday => Some(Weekday::Wednesday),
            Weekday::Friday => Some(Weekday::Thursday),
            Weekday::Saturday => Some(Weekday::Friday),
            Weekday::Sunday => Some(Weekday::Saturday),
        }
    }

    /// # Gets last day
    ///
    /// Last of Monday will be Sunday.
    pub const fn wrapping_last(&self) -> Self {
        match self {
            Weekday::Monday => Weekday::Sunday,
            Weekday::Tuesday => Weekday::Monday,
            Weekday::Wednesday => Weekday::Tuesday,
            Weekday::Thursday => Weekday::Wednesday,
            Weekday::Friday => Weekday::Thursday,
            Weekday::Saturday => Weekday::Friday,
            Weekday::Sunday => Weekday::Saturday,
        }
    }

    /// # Tries to convert a Unix value into self
    pub (crate) fn try_from_unix(weekday: i64) -> CrateResult<Self> {
        match weekday {
            0 => Ok(Weekday::Sunday),
            1 => Ok(Weekday::Monday),
            2 => Ok(Weekday::Tuesday),
            3 => Ok(Weekday::Wednesday),
            4 => Ok(Weekday::Thursday),
            5 => Ok(Weekday::Friday),
            6 => Ok(Weekday::Saturday),
            _ => Err(err!("Invalid Unix weekday: {weekday}", weekday=weekday)),
        }
    }

}

impl Deref for Weekday {

    type Target = str;

    fn deref(&self) -> &Self::Target {
        match self {
            Weekday::Monday => "Monday",
            Weekday::Tuesday => "Tuesday",
            Weekday::Wednesday => "Wednesday",
            Weekday::Thursday => "Thursday",
            Weekday::Friday => "Friday",
            Weekday::Saturday => "Saturday",
            Weekday::Sunday => "Sunday",
        }
    }

}

impl Display for Weekday {

    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
        f.write_str(self)
    }

}

impl FromStr for Weekday {

    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.eq_ignore_ascii_case(&*Weekday::Monday) {
            Ok(Weekday::Monday)
        } else if s.eq_ignore_ascii_case(&*Weekday::Tuesday) {
            Ok(Weekday::Tuesday)
        } else if s.eq_ignore_ascii_case(&*Weekday::Wednesday) {
            Ok(Weekday::Wednesday)
        } else if s.eq_ignore_ascii_case(&*Weekday::Thursday) {
            Ok(Weekday::Thursday)
        } else if s.eq_ignore_ascii_case(&*Weekday::Friday) {
            Ok(Weekday::Friday)
        } else if s.eq_ignore_ascii_case(&*Weekday::Saturday) {
            Ok(Weekday::Saturday)
        } else if s.eq_ignore_ascii_case(&*Weekday::Sunday) {
            Ok(Weekday::Sunday)
        } else {
            Err(err!("Unknown weekday: {:?}", s))
        }
    }

}