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, 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_range: false,
64 }),
65 }
66 }
67
68 pub const fn length(self, year: i32) -> u8 {
75 match self {
76 January | March | May | July | August | October | December => 31,
77 April | June | September | November => 30,
78 February if util::is_leap_year(year) => 29,
79 February => 28,
80 }
81 }
82
83 pub const fn previous(self) -> Self {
90 match self {
91 January => December,
92 February => January,
93 March => February,
94 April => March,
95 May => April,
96 June => May,
97 July => June,
98 August => July,
99 September => August,
100 October => September,
101 November => October,
102 December => November,
103 }
104 }
105
106 pub const fn next(self) -> Self {
113 match self {
114 January => February,
115 February => March,
116 March => April,
117 April => May,
118 May => June,
119 June => July,
120 July => August,
121 August => September,
122 September => October,
123 October => November,
124 November => December,
125 December => January,
126 }
127 }
128
129 pub const fn nth_next(self, n: u8) -> Self {
137 match (self as u8 - 1 + n % 12) % 12 {
138 0 => January,
139 1 => February,
140 2 => March,
141 3 => April,
142 4 => May,
143 5 => June,
144 6 => July,
145 7 => August,
146 8 => September,
147 9 => October,
148 10 => November,
149 val => {
150 debug_assert!(val == 11);
151 December
152 }
153 }
154 }
155
156 pub const fn nth_prev(self, n: u8) -> Self {
164 match self as i8 - 1 - (n % 12) as i8 {
165 1 | -11 => February,
166 2 | -10 => March,
167 3 | -9 => April,
168 4 | -8 => May,
169 5 | -7 => June,
170 6 | -6 => July,
171 7 | -5 => August,
172 8 | -4 => September,
173 9 | -3 => October,
174 10 | -2 => November,
175 11 | -1 => December,
176 val => {
177 debug_assert!(val == 0);
178 January
179 }
180 }
181 }
182}
183
184mod private {
185 #[non_exhaustive]
186 #[derive(Debug, Clone, Copy)]
187 pub struct MonthMetadata;
188}
189use private::MonthMetadata;
190
191impl SmartDisplay for Month {
192 type Metadata = MonthMetadata;
193
194 fn metadata(&self, _: FormatterOptions) -> Metadata<Self> {
195 match self {
196 January => Metadata::new(7, self, MonthMetadata),
197 February => Metadata::new(8, self, MonthMetadata),
198 March => Metadata::new(5, self, MonthMetadata),
199 April => Metadata::new(5, self, MonthMetadata),
200 May => Metadata::new(3, self, MonthMetadata),
201 June => Metadata::new(4, self, MonthMetadata),
202 July => Metadata::new(4, self, MonthMetadata),
203 August => Metadata::new(6, self, MonthMetadata),
204 September => Metadata::new(9, self, MonthMetadata),
205 October => Metadata::new(7, self, MonthMetadata),
206 November => Metadata::new(8, self, MonthMetadata),
207 December => Metadata::new(8, self, MonthMetadata),
208 }
209 }
210
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212 f.pad(match self {
213 January => "January",
214 February => "February",
215 March => "March",
216 April => "April",
217 May => "May",
218 June => "June",
219 July => "July",
220 August => "August",
221 September => "September",
222 October => "October",
223 November => "November",
224 December => "December",
225 })
226 }
227}
228
229impl fmt::Display for Month {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 SmartDisplay::fmt(self, f)
232 }
233}
234
235impl FromStr for Month {
236 type Err = error::InvalidVariant;
237
238 fn from_str(s: &str) -> Result<Self, Self::Err> {
239 match s {
240 "January" => Ok(January),
241 "February" => Ok(February),
242 "March" => Ok(March),
243 "April" => Ok(April),
244 "May" => Ok(May),
245 "June" => Ok(June),
246 "July" => Ok(July),
247 "August" => Ok(August),
248 "September" => Ok(September),
249 "October" => Ok(October),
250 "November" => Ok(November),
251 "December" => Ok(December),
252 _ => Err(error::InvalidVariant),
253 }
254 }
255}
256
257impl From<Month> for u8 {
258 fn from(month: Month) -> Self {
259 month as _
260 }
261}
262
263impl TryFrom<u8> for Month {
264 type Error = error::ComponentRange;
265
266 fn try_from(value: u8) -> Result<Self, Self::Error> {
267 match NonZeroU8::new(value) {
268 Some(value) => Self::from_number(value),
269 None => Err(error::ComponentRange {
270 name: "month",
271 minimum: 1,
272 maximum: 12,
273 value: 0,
274 conditional_range: false,
275 }),
276 }
277 }
278}