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