sac13 0.1.1

The reference implementation for the SAC13 calendar system.
Documentation
/*!

SAC13 is a 13-month solar calendar with fixed four-week months,
starting each year with the March Equinox.

<a href="https://sac13.net"><img alt="Static Badge" src="https://img.shields.io/badge/web-SAC13-yellow"></a>
<a href="https://codeberg.org/SAC13/sac13.rs"><img alt="Repository link icon" src="https://img.shields.io/badge/repo-sac13.rs-blue"></a>
<a href="https://crates.io/crates/sac13"><img alt="Crates.io Version" src="https://img.shields.io/crates/v/sac13"></a>
<a href="https://docs.rs/sac13"><img alt="docs.rs" src="https://img.shields.io/docsrs/sac13"></a>
<a href="https://codeberg.org/SAC13/sac13.rs/src/branch/main/LICENSE"><img alt="Crates.io License" src="https://img.shields.io/crates/l/sac13"></a>

This library is the Rust reference implementation for SAC13 and maintains data types and functions
to convert, among others, from and to

  - [Gregorian Calendar dates](crate::date_gregorian::GregorianDate)
  - [JulianDays](crate::day_counts::JulianDay)
  - [UnixDays](crate::day_counts::UnixDay)

*/

#![no_std]

#[cfg(test)]
#[macro_use]
extern crate std;

/// Creates a [SAC13 year](Year) with a statically and compile time checked value.
///
/// # Example
///
/// ```
/// use sac13::year;
///
/// let year = year!(M020);
///
/// let year_zero = year!(A000);
/// let last_year = year!(Z999);
///
/// // The following lines are invalid years (or format) and would fail during compilation:
/// // let year = year!(m020);
/// // let year = year!(20020);
/// // let year = year!(-100);
/// // let year = year!(20);
/// ```
#[macro_export]
macro_rules! year {
    ($year:ident) => {
        const {
            $crate::Year::try_from_str(core::stringify!($year))
                .expect(concat!("Invalid SAC13 year: ", stringify!($year)))
        }
    };
}

/// Creates a [Gregorian Calendar date](GregorianDate) with a statically known and compile time checked value.
///
/// # Example
///
/// ```
/// use sac13::prelude::*;
///
/// let date = date_greg!(2020 - 04 - 17);
/// let date = date_greg!(2020 - 02 - 29);  // leap year
///
/// // the following line would not compile (because 2021 wasn't a leap year)
/// // let date = date_greg!(2021 - 02 - 29);
/// ```
#[macro_export]
macro_rules! date_greg {
    ($year:literal - $month:literal - $day:literal) => {
        const {
            #[allow(clippy::zero_prefixed_literal)]
            let y = $year;

            #[allow(clippy::zero_prefixed_literal)]
            let m = $month;

            #[allow(clippy::zero_prefixed_literal)]
            let d = $day;

            $crate::GregorianDate::from_ymd(y, m, d)
                .expect("The given input was not a valid Gregorian Calendar date")
        }
    };
}

/// Creates a [SAC13 date](Date) with a statically known and compile time checked value.
///
/// # Example
///
/// ```
/// use sac13::prelude::*;
///
/// let date = date!(M020 - 04 - 14); // "regular" day
/// let date = date!(M020 - 13 - 29); // year day
/// let date = date!(M021 - 06 - 29); // leap day
///
/// // the following lines would not compile
///
/// // date = date!(M022 - 06 - 29); // M022 is not a leap year
/// // date = date!(M022 - 04 - 29); // No month except August on leap years and Addenduary have more than 28 days
///
/// ```
#[macro_export]
macro_rules! date {
    ($year:ident - $month:literal - $day:literal) => {
        const {
            let y = $crate::year!($year);

            #[allow(clippy::zero_prefixed_literal)]
            let m = $month;

            #[allow(clippy::zero_prefixed_literal)]
            let d = $day;

            let m = $crate::Month::new(m).expect(concat!(
                "Month must be a value from 1 - 13. Given: ",
                $month
            ));

            $crate::Date::from_ymd(y, m, d).expect("The given input was not a valid SAC13 date")
        }
    };
}

macro_rules! ok {
    ($opt:expr) => {
        match $opt {
            ::core::option::Option::None => return ::core::option::Option::None,
            ::core::option::Option::Some(x) => x,
        }
    };
}

/// The type of the year.
///
/// A [`YearType::Common`] year has 365 days and a [`YearType::Leap`] year has 366 days.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
#[allow(missing_docs)]
pub enum YearType {
    Common,
    Leap,
}

pub mod prelude;

/// Primitive types for linear day counts like the [Julian Day Number](crate::scalars::JulianDay).
pub mod day_counts {
    pub use crate::date_sac13::raw_date::YearOrdinal;
    pub use crate::scalars::{CycleEpochDay, JulianDay, Sac13Day, UnixDay};
}

pub use date_gregorian::GregorianDate;
pub use date_sac13::Date;
pub use month::Month;
pub use parse::*;
pub use sac_or_greg::SacOrGreg;
pub use scalars::Year;
pub use traits::CalendarDate;

pub(crate) mod parse;

mod date_gregorian;
mod date_sac13;
mod iterhelp;
mod month;
mod sac_or_greg;
mod scalars;
mod traits;

#[cfg(test)]
mod tests;

#[cfg(any(test, doctest))]
pub mod readme_test {
    // The README.md of the crate is actually different from the
    // root level documentation in docs.rs, so during testing we
    // add an empty module that has the crate README.md linked
    // to make sure the code examples are tested.

    #![doc = include_str!("../README.md")]
}