1use core::fmt;
4use core::num::NonZeroU8;
5use core::str::FromStr;
6
7use powerfmt::smart_display::{FormatterOptions, Metadata, SmartDisplay};
8
9use self::Month::*;
10use crate::{error, hint, util};
11
12#[repr(u8)]
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum Month {
16 #[allow(missing_docs)]
17 January = 1,
18 #[allow(missing_docs)]
19 February = 2,
20 #[allow(missing_docs)]
21 March = 3,
22 #[allow(missing_docs)]
23 April = 4,
24 #[allow(missing_docs)]
25 May = 5,
26 #[allow(missing_docs)]
27 June = 6,
28 #[allow(missing_docs)]
29 July = 7,
30 #[allow(missing_docs)]
31 August = 8,
32 #[allow(missing_docs)]
33 September = 9,
34 #[allow(missing_docs)]
35 October = 10,
36 #[allow(missing_docs)]
37 November = 11,
38 #[allow(missing_docs)]
39 December = 12,
40}
41
42impl Month {
43 pub(crate) const fn from_number(n: NonZeroU8) -> Result<Self, error::ComponentRange> {
45 match n.get() {
46 1 => Ok(January),
47 2 => Ok(February),
48 3 => Ok(March),
49 4 => Ok(April),
50 5 => Ok(May),
51 6 => Ok(June),
52 7 => Ok(July),
53 8 => Ok(August),
54 9 => Ok(September),
55 10 => Ok(October),
56 11 => Ok(November),
57 12 => Ok(December),
58 n => Err(error::ComponentRange {
59 name: "month",
60 minimum: 1,
61 maximum: 12,
62 value: n as _,
63 conditional_message: None,
64 }),
65 }
66 }
67
68 pub const fn length(self, year: i32) -> u8 {
75 let val = self as u8;
76 if hint::unlikely(val == 2) {
77 if util::is_leap_year(year) {
78 29
79 } else {
80 28
81 }
82 } else {
83 30 | val ^ (val >> 3)
84 }
85 }
86
87 pub const fn previous(self) -> Self {
94 match self {
95 January => December,
96 February => January,
97 March => February,
98 April => March,
99 May => April,
100 June => May,
101 July => June,
102 August => July,
103 September => August,
104 October => September,
105 November => October,
106 December => November,
107 }
108 }
109
110 pub const fn next(self) -> Self {
117 match self {
118 January => February,
119 February => March,
120 March => April,
121 April => May,
122 May => June,
123 June => July,
124 July => August,
125 August => September,
126 September => October,
127 October => November,
128 November => December,
129 December => January,
130 }
131 }
132
133 pub const fn nth_next(self, n: u8) -> Self {
141 match (self as u8 - 1 + n % 12) % 12 {
142 0 => January,
143 1 => February,
144 2 => March,
145 3 => April,
146 4 => May,
147 5 => June,
148 6 => July,
149 7 => August,
150 8 => September,
151 9 => October,
152 10 => November,
153 val => {
154 debug_assert!(val == 11);
155 December
156 }
157 }
158 }
159
160 pub const fn nth_prev(self, n: u8) -> Self {
168 match self as i8 - 1 - (n % 12) as i8 {
169 1 | -11 => February,
170 2 | -10 => March,
171 3 | -9 => April,
172 4 | -8 => May,
173 5 | -7 => June,
174 6 | -6 => July,
175 7 | -5 => August,
176 8 | -4 => September,
177 9 | -3 => October,
178 10 | -2 => November,
179 11 | -1 => December,
180 val => {
181 debug_assert!(val == 0);
182 January
183 }
184 }
185 }
186}
187
188mod private {
189 #[non_exhaustive]
190 #[derive(Debug, Clone, Copy)]
191 pub struct MonthMetadata;
192}
193use private::MonthMetadata;
194
195impl SmartDisplay for Month {
196 type Metadata = MonthMetadata;
197
198 fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
199 match self {
200 January => Metadata::new(7, self, MonthMetadata),
201 February => Metadata::new(8, self, MonthMetadata),
202 March => Metadata::new(5, self, MonthMetadata),
203 April => Metadata::new(5, self, MonthMetadata),
204 May => Metadata::new(3, self, MonthMetadata),
205 June => Metadata::new(4, self, MonthMetadata),
206 July => Metadata::new(4, self, MonthMetadata),
207 August => Metadata::new(6, self, MonthMetadata),
208 September => Metadata::new(9, self, MonthMetadata),
209 October => Metadata::new(7, self, MonthMetadata),
210 November => Metadata::new(8, self, MonthMetadata),
211 December => Metadata::new(8, self, MonthMetadata),
212 }
213 }
214
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 f.pad(match self {
217 January => "January",
218 February => "February",
219 March => "March",
220 April => "April",
221 May => "May",
222 June => "June",
223 July => "July",
224 August => "August",
225 September => "September",
226 October => "October",
227 November => "November",
228 December => "December",
229 })
230 }
231}
232
233impl fmt::Display for Month {
234 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235 SmartDisplay::fmt(self, f)
236 }
237}
238
239impl FromStr for Month {
240 type Err = error::InvalidVariant;
241
242 fn from_str(s: &str) -> Result<Self, Self::Err> {
243 match s {
244 "January" => Ok(January),
245 "February" => Ok(February),
246 "March" => Ok(March),
247 "April" => Ok(April),
248 "May" => Ok(May),
249 "June" => Ok(June),
250 "July" => Ok(July),
251 "August" => Ok(August),
252 "September" => Ok(September),
253 "October" => Ok(October),
254 "November" => Ok(November),
255 "December" => Ok(December),
256 _ => Err(error::InvalidVariant),
257 }
258 }
259}
260
261impl From<Month> for u8 {
262 fn from(month: Month) -> Self {
263 month as _
264 }
265}
266
267impl TryFrom<u8> for Month {
268 type Error = error::ComponentRange;
269
270 fn try_from(value: u8) -> Result<Self, Self::Error> {
271 match NonZeroU8::new(value) {
272 Some(value) => Self::from_number(value),
273 None => Err(error::ComponentRange {
274 name: "month",
275 minimum: 1,
276 maximum: 12,
277 value: 0,
278 conditional_message: None,
279 }),
280 }
281 }
282}