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    ($self:expr, $rhs:expr) => {
55        match ($self, $rhs) {
56            (this, rhs) => {
57                let d = this / rhs;
58                let r = this % rhs;
59
60                // If the remainder is non-zero, we need to subtract one if the
61                // signs of self and rhs differ, as this means we rounded upwards
62                // instead of downwards. We do this branchlessly by creating a mask
63                // which is all-ones iff the signs differ, and 0 otherwise. Then by
64                // adding this mask (which corresponds to the signed value -1), we
65                // get our correction.
66                let correction = (this ^ rhs) >> ($crate::size_of_val(&this) * 8 - 1);
67                if r != 0 {
68                    d + correction
69                } else {
70                    d
71                }
72            }
73        }
74    };
75}
76
77/// Cascade an out-of-bounds value.
78macro_rules! cascade {
79    (@ordinal ordinal) => {};
80    (@year year) => {};
81
82    // Cascade an out-of-bounds value from "from" to "to".
83    ($from:ident in $min:literal.. $max:expr => $to:tt) => {
84        #[allow(unused_comparisons, unused_assignments)]
85        let min = $min;
86        let max = $max;
87        if $from >= max {
88            $from -= max - min;
89            $to += 1;
90        } else if $from < min {
91            $from += max - min;
92            $to -= 1;
93        }
94    };
95
96    // Special case the ordinal-to-year cascade, as it has different behavior.
97    ($ordinal:ident => $year:ident) => {
98        // We need to actually capture the idents. Without this, macro hygiene causes errors.
99        cascade!(@ordinal $ordinal);
100        cascade!(@year $year);
101        #[allow(unused_assignments)]
102        if $ordinal > crate::util::days_in_year($year) as i16 {
103            $ordinal -= crate::util::days_in_year($year) as i16;
104            $year += 1;
105        } else if $ordinal < 1 {
106            $year -= 1;
107            $ordinal += crate::util::days_in_year($year) as i16;
108        }
109    };
110}
111
112/// Constructs a ranged integer, returning a `ComponentRange` error if the value is out of range.
113macro_rules! ensure_ranged {
114    ($type:ident : $value:ident) => {
115        match $type::new($value) {
116            Some(val) => val,
117            None => {
118                #[allow(trivial_numeric_casts)]
119                return Err(crate::error::ComponentRange {
120                    name: stringify!($value),
121                    minimum: $type::MIN.get() as _,
122                    maximum: $type::MAX.get() as _,
123                    value: $value as _,
124                    conditional_message: None,
125                });
126            }
127        }
128    };
129
130    ($type:ident : $value:ident $(as $as_type:ident)? * $factor:expr) => {
131        match ($value $(as $as_type)?).checked_mul($factor) {
132            Some(val) => match $type::new(val) {
133                Some(val) => val,
134                None => {
135                    #[allow(trivial_numeric_casts)]
136                    return Err(crate::error::ComponentRange {
137                        name: stringify!($value),
138                        minimum: $type::MIN.get() as i64 / $factor as i64,
139                        maximum: $type::MAX.get() as i64 / $factor as i64,
140                        value: $value as _,
141                        conditional_message: None,
142                    });
143                }
144            },
145            None => {
146                return Err(crate::error::ComponentRange {
147                    name: stringify!($value),
148                    minimum: $type::MIN.get() as i64 / $factor as i64,
149                    maximum: $type::MAX.get() as i64 / $factor as i64,
150                    value: $value as _,
151                    conditional_message: None,
152                });
153            }
154        }
155    };
156}
157
158/// Try to unwrap an expression, returning if not possible.
159///
160/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is
161/// usable in `const` contexts.
162macro_rules! const_try {
163    ($e:expr) => {
164        match $e {
165            Ok(value) => value,
166            Err(error) => return Err(error),
167        }
168    };
169}
170
171/// Try to unwrap an expression, returning if not possible.
172///
173/// This is similar to the `?` operator, but is usable in `const` contexts.
174macro_rules! const_try_opt {
175    ($e:expr) => {
176        match $e {
177            Some(value) => value,
178            None => return None,
179        }
180    };
181}
182
183/// Try to unwrap an expression, panicking if not possible.
184///
185/// This is similar to `$e.expect($message)`, but is usable in `const` contexts.
186macro_rules! expect_opt {
187    ($e:expr, $message:literal) => {
188        match $e {
189            Some(value) => value,
190            None => crate::expect_failed($message),
191        }
192    };
193}
194
195/// `unreachable!()`, but better.
196#[cfg(any(feature = "formatting", feature = "parsing"))]
197macro_rules! bug {
198    () => { compile_error!("provide an error message to help fix a possible bug") };
199    ($descr:literal $($rest:tt)?) => {
200        panic!(concat!("internal error: ", $descr) $($rest)?)
201    }
202}
203
204#[cfg(any(feature = "formatting", feature = "parsing"))]
205pub(crate) use bug;
206pub(crate) use {
207    __impl_assign, cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt,
208    impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign,
209};