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}