sac13/
lib.rs

1/*!
2
3SAC13 is a 13-month solar calendar with fixed four-week months,
4starting each year with the March Equinox.
5
6<a href="https://sac13.net"><img alt="Static Badge" src="https://img.shields.io/badge/web-SAC13-yellow"></a>
7<a href="https://codeberg.org/SAC13/sac13.rs"><img alt="Repository link icon" src="https://img.shields.io/badge/repo-sac13.rs-blue"></a>
8<a href="https://crates.io/crates/sac13"><img alt="Crates.io Version" src="https://img.shields.io/crates/v/sac13"></a>
9<a href="https://docs.rs/sac13"><img alt="docs.rs" src="https://img.shields.io/docsrs/sac13"></a>
10<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>
11
12This library is the Rust reference implementation for SAC13 and maintains data types and functions
13to convert, among others, from and to
14
15  - [Gregorian Calendar dates](crate::date_gregorian::GregorianDate)
16  - [JulianDays](crate::day_counts::JulianDay)
17  - [UnixDays](crate::day_counts::UnixDay) (days since 1970-01-01)
18
19*/
20
21#![no_std]
22
23#[cfg(test)]
24#[macro_use]
25extern crate std;
26
27/// Creates a [SAC13 year](Year) with a statically and compile time checked value.
28///
29/// # Example
30///
31/// ```
32/// use sac13::year;
33///
34/// let year = year!(M020);
35///
36/// let year_zero = year!(A000);
37/// let last_year = year!(Z999);
38///
39/// // The following lines are invalid years (or format) and would fail during compilation:
40/// // let year = year!(m020);
41/// // let year = year!(20020);
42/// // let year = year!(-100);
43/// // let year = year!(20);
44/// ```
45#[macro_export]
46macro_rules! year {
47    ($year:ident) => {
48        const {
49            $crate::Year::try_from_str(core::stringify!($year))
50                .expect(concat!("Invalid SAC13 year: ", stringify!($year)))
51        }
52    };
53}
54
55/// Creates a [Gregorian Calendar date](GregorianDate) with a statically known and compile time checked value.
56///
57/// # Example
58///
59/// ```
60/// use sac13::prelude::*;
61///
62/// let date = date_greg!(2020 - 04 - 17);
63/// let date = date_greg!(2020 - 02 - 29);  // leap year
64///
65/// // the following line would not compile (because 2021 wasn't a leap year)
66/// // let date = date_greg!(2021 - 02 - 29);
67/// ```
68#[macro_export]
69macro_rules! date_greg {
70    ($year:literal - $month:literal - $day:literal) => {
71        const {
72            #[allow(clippy::zero_prefixed_literal)]
73            let y = $year;
74
75            #[allow(clippy::zero_prefixed_literal)]
76            let m = $month;
77
78            #[allow(clippy::zero_prefixed_literal)]
79            let d = $day;
80
81            $crate::GregorianDate::from_ymd(y, m, d)
82                .expect("The given input was not a valid Gregorian Calendar date")
83        }
84    };
85}
86
87/// Creates a [SAC13 date](Date) with a statically known and compile time checked value.
88///
89/// # Example
90///
91/// ```
92/// use sac13::prelude::*;
93///
94/// let date = date!(M020 - 04 - 14); // "regular" day
95/// let date = date!(M020 - 13 - 29); // year day
96/// let date = date!(M021 - 06 - 29); // leap day
97///
98/// // the following lines would not compile
99///
100/// // date = date!(M022 - 06 - 29); // M022 is not a leap year
101/// // date = date!(M022 - 04 - 29); // No month except August on leap years and Addenduary have more than 28 days
102///
103/// ```
104#[macro_export]
105macro_rules! date {
106    ($year:ident - $month:literal - $day:literal) => {
107        const {
108            let y = $crate::year!($year);
109
110            #[allow(clippy::zero_prefixed_literal)]
111            let m = $month;
112
113            #[allow(clippy::zero_prefixed_literal)]
114            let d = $day;
115
116            let m = $crate::Month::new(m).expect(concat!(
117                "Month must be a value from 1 - 13. Given: ",
118                $month
119            ));
120
121            $crate::Date::from_ymd(y, m, d).expect("The given input was not a valid SAC13 date")
122        }
123    };
124}
125
126macro_rules! ok {
127    ($opt:expr) => {
128        match $opt {
129            ::core::option::Option::None => return ::core::option::Option::None,
130            ::core::option::Option::Some(x) => x,
131        }
132    };
133}
134
135/// The type of the year.
136///
137/// A [`YearType::Common`] year has 365 days and a [`YearType::Leap`] year has 366 days.
138#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
139#[allow(missing_docs)]
140pub enum YearType {
141    Common,
142    Leap,
143}
144
145pub mod prelude;
146
147/// Primitive types for linear day counts like the [Julian Day Number](crate::scalars::JulianDay).
148pub mod day_counts {
149    pub use crate::date_sac13::raw_date::YearOrdinal;
150    pub use crate::scalars::{CycleEpochDay, JulianDay, Sac13Day, UnixDay};
151}
152
153pub use date_gregorian::GregorianDate;
154pub use date_sac13::Date;
155pub use month::Month;
156pub use parse::*;
157pub use sac_or_greg::SacOrGreg;
158pub use scalars::Year;
159pub use traits::CalendarDate;
160
161pub(crate) mod parse;
162
163mod date_gregorian;
164mod date_sac13;
165mod iterhelp;
166mod month;
167mod sac_or_greg;
168mod scalars;
169mod traits;
170
171#[cfg(test)]
172mod tests;
173
174#[cfg(any(test, doctest))]
175pub mod readme_test {
176    // The README.md of the crate is actually different from the
177    // root level documentation in docs.rs, so during testing we
178    // add an empty module that has the crate README.md linked
179    // to make sure the code examples are tested.
180
181    #![doc = include_str!("../README.md")]
182}