time/
internal_macros.rs

1//! Macros for use within the library. They are not publicly available.
2
3/// Helper macro for easily implementing `OpAssign`.
4macro_rules! __impl_assign {
5    ($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$(
6        #[allow(unused_qualifications)]
7        $(#[$attr])*
8        impl core::ops::$op<$t> for $target {
9            fn $fn(&mut self, rhs: $t) {
10                *self = *self $sym rhs;
11            }
12        }
13    )+};
14}
15
16/// Implement `AddAssign` for the provided types.
17macro_rules! impl_add_assign {
18    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
19        $crate::internal_macros::__impl_assign!(
20            + AddAssign add_assign $target : $($(#[$attr])* $t),+
21        );
22    };
23}
24
25/// Implement `SubAssign` for the provided types.
26macro_rules! impl_sub_assign {
27    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
28        $crate::internal_macros::__impl_assign!(
29            - SubAssign sub_assign $target : $($(#[$attr])* $t),+
30        );
31    };
32}
33
34/// Implement `MulAssign` for the provided types.
35macro_rules! impl_mul_assign {
36    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
37        $crate::internal_macros::__impl_assign!(
38            * MulAssign mul_assign $target : $($(#[$attr])* $t),+
39        );
40    };
41}
42
43/// Implement `DivAssign` for the provided types.
44macro_rules! impl_div_assign {
45    ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => {
46        $crate::internal_macros::__impl_assign!(
47            / DivAssign div_assign $target : $($(#[$attr])* $t),+
48        );
49    };
50}
51
52/// Division of integers, rounding the resulting value towards negative infinity.
53macro_rules! div_floor {
54    ($a:expr, $b:expr) => {{
55        let _a = $a;
56        let _b = $b;
57
58        let (_quotient, _remainder) = (_a / _b, _a % _b);
59
60        if (_remainder > 0 && _b < 0) || (_remainder < 0 && _b > 0) {
61            _quotient - 1
62        } else {
63            _quotient
64        }
65    }};
66}
67
68/// Cascade an out-of-bounds value.
69macro_rules! cascade {
70    (@ordinal ordinal) => {};
71    (@year year) => {};
72
73    // Cascade an out-of-bounds value from "from" to "to".
74    ($from:ident in $min:literal.. $max:expr => $to:tt) => {
75        #[allow(unused_comparisons, unused_assignments)]
76        let min = $min;
77        let max = $max;
78        if $from >= max {
79            $from -= max - min;
80            $to += 1;
81        } else if $from < min {
82            $from += max - min;
83            $to -= 1;
84        }
85    };
86
87    // Special case the ordinal-to-year cascade, as it has different behavior.
88    ($ordinal:ident => $year:ident) => {
89        // We need to actually capture the idents. Without this, macro hygiene causes errors.
90        cascade!(@ordinal $ordinal);
91        cascade!(@year $year);
92        #[allow(unused_assignments)]
93        if $ordinal > crate::util::days_in_year($year) as i16 {
94            $ordinal -= crate::util::days_in_year($year) as i16;
95            $year += 1;
96        } else if $ordinal < 1 {
97            $year -= 1;
98            $ordinal += crate::util::days_in_year($year) as i16;
99        }
100    };
101}
102
103/// Constructs a ranged integer, returning a `ComponentRange` error if the value is out of range.
104macro_rules! ensure_ranged {
105    ($type:ident : $value:ident) => {
106        match $type::new($value) {
107            Some(val) => val,
108            None => {
109                #[allow(trivial_numeric_casts)]
110                return Err(crate::error::ComponentRange {
111                    name: stringify!($value),
112                    minimum: $type::MIN.get() as _,
113                    maximum: $type::MAX.get() as _,
114                    value: $value as _,
115                    conditional_message: None,
116                });
117            }
118        }
119    };
120
121    ($type:ident : $value:ident $(as $as_type:ident)? * $factor:expr) => {
122        match ($value $(as $as_type)?).checked_mul($factor) {
123            Some(val) => match $type::new(val) {
124                Some(val) => val,
125                None => {
126                    #[allow(trivial_numeric_casts)]
127                    return Err(crate::error::ComponentRange {
128                        name: stringify!($value),
129                        minimum: $type::MIN.get() as i64 / $factor as i64,
130                        maximum: $type::MAX.get() as i64 / $factor as i64,
131                        value: $value as _,
132                        conditional_message: None,
133                    });
134                }
135            },
136            None => {
137                return Err(crate::error::ComponentRange {
138                    name: stringify!($value),
139                    minimum: $type::MIN.get() as i64 / $factor as i64,
140                    maximum: $type::MAX.get() as i64 / $factor as i64,
141                    value: $value as _,
142                    conditional_message: None,
143                });
144            }
145        }
146    };
147}
148
149/// Try to unwrap an expression, returning if not possible.
150///
151/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
152/// usable in `const` contexts.
153macro_rules! const_try {
154    ($e:expr) => {
155        match $e {
156            Ok(value) => value,
157            Err(error) => return Err(error),
158        }
159    };
160}
161
162/// Try to unwrap an expression, returning if not possible.
163///
164/// This is similar to the `?` operator, but is usable in `const` contexts.
165macro_rules! const_try_opt {
166    ($e:expr) => {
167        match $e {
168            Some(value) => value,
169            None => return None,
170        }
171    };
172}
173
174/// Try to unwrap an expression, panicking if not possible.
175///
176/// This is similar to `$e.expect($message)`, but is usable in `const` contexts.
177macro_rules! expect_opt {
178    ($e:expr, $message:literal) => {
179        match $e {
180            Some(value) => value,
181            None => crate::expect_failed($message),
182        }
183    };
184}
185
186/// `unreachable!()`, but better.
187#[cfg(any(feature = "formatting", feature = "parsing"))]
188macro_rules! bug {
189    () => { compile_error!("provide an error message to help fix a possible bug") };
190    ($descr:literal $($rest:tt)?) => {
191        panic!(concat!("internal error: ", $descr) $($rest)?)
192    }
193}
194
195#[cfg(any(feature = "formatting", feature = "parsing"))]
196pub(crate) use bug;
197pub(crate) use {
198    __impl_assign, cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt,
199    impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign,
200};