utc2k/
macros.rs

1/*!
2# Utc2k - Macros
3*/
4
5/// # Helper: `AsRef` and `Borrow`.
6macro_rules! as_ref_borrow_cast {
7	($parent:ty: $($cast:ident $ty:ty),+ $(,)?) => ($(
8		impl AsRef<$ty> for $parent {
9			#[inline]
10			fn as_ref(&self) -> &$ty { self.$cast() }
11		}
12
13		impl ::std::borrow::Borrow<$ty> for $parent {
14			#[inline]
15			fn borrow(&self) -> &$ty { self.$cast() }
16		}
17	)+);
18}
19
20/// # Helper: `Display`.
21macro_rules! display_str {
22	($cast:ident $ty:ty) => (
23		impl ::std::fmt::Display for $ty {
24			#[inline]
25			fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
26				<str as ::std::fmt::Display>::fmt(self.$cast(), f)
27			}
28		}
29	);
30}
31
32/// # Helper: First in List.
33macro_rules! first {
34	// Slap a macro call around the answer.
35	(@$mac:ident $first:tt $($_rest:tt)*) => ( $mac!($first) );
36
37	// Passthrough.
38	($first:tt $($_rest:tt)*) => ( $first );
39}
40
41/// # Helper: Last in List.
42macro_rules! last {
43	// Slap a macro call around the answer.
44	(@$mac:ident $last:tt) => ( $mac!($last) );
45	(@$mac:ident $_next:tt $($rest:tt)+) => ( $crate::macros::last!(@$mac $($rest)+) );
46
47	// Passthrough.
48	($last:tt) => ( $last );
49	($_next:tt $($rest:tt)+) => ( $crate::macros::last!($($rest)+) );
50}
51
52/// # Helper: Pair Siblings.
53///
54/// This macro groups `Weekday`/`Month` variants with their siblings for
55/// consumption by other macros (on this page).
56macro_rules! pair {
57	// Pair Down.
58	(
59		@previous $dst:ident { $($args:tt)* }
60		$k1:ident $k2:ident $k3:ident $k4:ident $k5:ident $k6:ident $k7:ident
61	) => (
62		$crate::macros::$dst!(
63			$($args)*
64			($k1 $k7), ($k2 $k1), ($k3 $k2), ($k4 $k3), ($k5 $k4), ($k6 $k5),
65			($k7 $k6),
66		)
67	);
68	(
69		@previous $dst:ident { $($args:tt)* }
70		$k1:ident $k2:ident $k3:ident $k4:ident $k5:ident $k6:ident $k7:ident $k8:ident $k9:ident $k10:ident $k11:ident $k12:ident
71	) => (
72		$crate::macros::$dst!(
73			$($args)*
74			($k1 $k12), ($k2 $k1), ($k3 $k2), ($k4 $k3), ($k5 $k4), ($k6 $k5),
75			($k7 $k6), ($k8 $k7), ($k9 $k8), ($k10 $k9), ($k11 $k10), ($k12 $k11),
76		)
77	);
78
79	// Pair Up.
80	(
81		@next $dst:ident { $($args:tt)* }
82		$k1:ident $k2:ident $k3:ident $k4:ident $k5:ident $k6:ident $k7:ident
83	) => (
84		$crate::macros::$dst!(
85			$($args)*
86			($k1 $k2), ($k2 $k3), ($k3 $k4), ($k4 $k5), ($k5 $k6), ($k6 $k7),
87			($k7 $k1),
88		)
89	);
90	(
91		@next $dst:ident { $($args:tt)* }
92		$k1:ident $k2:ident $k3:ident $k4:ident $k5:ident $k6:ident $k7:ident $k8:ident $k9:ident $k10:ident $k11:ident $k12:ident
93	) => (
94		$crate::macros::$dst!(
95			$($args)*
96			($k1 $k2), ($k2 $k3), ($k3 $k4), ($k4 $k5), ($k5 $k6), ($k6 $k7),
97			($k7 $k8), ($k8 $k9), ($k9 $k10), ($k10 $k11), ($k11 $k12), ($k12 $k1),
98		)
99	);
100}
101
102/// # Helper: Set Up `Weekday`/`Month` Enums.
103///
104/// This monster enum generates all the common code for the two enums, ensuring
105/// consistent documentation, unit tests, etc.
106///
107/// Specifically:
108///
109/// * The main `enum` definition and derived traits:
110///   * `Clone`
111///   * `Copy`
112///   * `Debug`
113///   * `Display`
114///   * `Eq` / `PartialEq`
115///   * `Hash`
116///   * `Ord` / `PartialOrd`
117/// * `AsRef<str>`
118/// * `Borrow<str>`
119/// * `From<Utc2k>`
120/// * `FromStr`
121/// * `IntoIterator`
122/// * `TryFrom`
123///   * `&str`
124///   * `Box<str>` (and ref)
125///   * `Cow<str>` (and ref)
126///   * `String` (and ref)
127/// * `Self::ALL`
128/// * `Self::abbreviation`
129/// * `Self::as_str`
130/// * `Self::from_u8` (private)
131/// * `Self::next`
132/// * `Self::previous`
133/// * The iterator struct and its impls
134///
135/// This also handles the following cross-type implementations for `u8`, `u16`,
136/// `u32`, `u64`, and `usize`:
137///
138/// * `Add` / `AddAssign`
139/// * `From` (both ways)
140/// * `PartialEq` (both ways)
141/// * `Sub` / `SubAssign`
142///
143/// Big as this list is, there are three common components _not_ handled here:
144///
145/// * `Self::from_abbreviation` (one-off vars and weird sorting)
146/// * `Self::now`               (only `Weekday` has yesterday/tomorrow)
147/// * `TryFrom<&[u8]>`          (overly specific documentation)
148macro_rules! weekmonth {
149	// Docs: print the type's numerical range and first entry.
150	(@ex @range $ty:tt $($k:ident $v:literal)+) => (concat!(
151		stringify!($ty), "s range from `",
152		$crate::macros::first!(@stringify $($v)+),
153		"..=",
154		$crate::macros::last!(@stringify $($v)+),
155		"`, starting with ",
156		$crate::macros::first!(@stringify $($k)+),
157		".",
158	));
159
160	// Docs: addition/subtraction (code).
161	(@ex @addsub $uint:tt $ty:tt $op:literal $(($v:literal $k:ident $com:literal)),+ $(,)?) => (
162		concat!(
163			"let start = ", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ";\n",
164			$(
165				"assert_eq!(start ", $op, " ", $v, "_", stringify!($uint), ", ", stringify!($ty), "::", stringify!($k), "); ", $com, "\n",
166			)+
167			"// …\n",
168		)
169	);
170	(@ex @add $uint:tt $ty:tt $k1:ident $k2:ident $k3:ident $k4:ident $k5:ident $k6:ident $k7:ident) => (
171		$crate::macros::weekmonth!(
172			@ex @addsub
173			$uint $ty "+"
174			(0 $k1 "// Noop."),
175			(1 $k2 ""), (2 $k3 ""), (3 $k4 ""), (4 $k5 ""), (5 $k6 ""), (6 $k7 ""),
176			(7 $k1 "// Wrap."), (8 $k2 "// Wrap."), (9 $k3 "// Wrap."),
177		)
178	);
179	(@ex @add $uint:tt $ty:tt $k1:ident $k2:ident $k3:ident $k4:ident $k5:ident $k6:ident $k7:ident $k8:ident $k9:ident $k10:ident $k11:ident $k12:ident) => (
180		$crate::macros::weekmonth!(
181			@ex @addsub
182			$uint $ty "+"
183			(0 $k1 "// Noop."),
184			(1 $k2 ""), (2 $k3 ""), (3 $k4 ""), (4 $k5 ""), (5 $k6 ""), (6 $k7 ""),
185			(7 $k8 ""), (8 $k9 ""), (9 $k10 ""), (10 $k11 ""), (11 $k12 ""),
186			(12 $k1 "// Wrap."), (13 $k2 "// Wrap."), (14 $k3 "// Wrap."),
187		)
188	);
189	(@ex @sub $uint:tt $ty:tt $k1:ident $k2:ident $k3:ident $k4:ident $k5:ident $k6:ident $k7:ident) => (
190		$crate::macros::weekmonth!(
191			@ex @addsub
192			$uint $ty "-"
193			(0 $k1 "// Noop."),
194			(1 $k7 ""), (2 $k6 ""), (3 $k5 ""), (4 $k4 ""), (5 $k3 ""), (6 $k2 ""),
195			(7 $k1 "// Wrap."), (8 $k7 "// Wrap."), (9 $k6 "// Wrap."),
196		)
197	);
198	(@ex @sub $uint:tt $ty:tt $k1:ident $k2:ident $k3:ident $k4:ident $k5:ident $k6:ident $k7:ident $k8:ident $k9:ident $k10:ident $k11:ident $k12:ident) => (
199		$crate::macros::weekmonth!(
200			@ex @addsub
201			$uint $ty "-"
202			(0 $k1 "// Noop."),
203			(1 $k12 ""), (2 $k11 ""), (3 $k10 ""), (4 $k9 ""), (5 $k8 ""), (6 $k7 ""),
204			(7 $k6 ""), (8 $k5 ""), (9 $k4 ""), (10 $k3 ""), (11 $k2 ""),
205			(12 $k1 "// Wrap."), (13 $k12 "// Wrap."), (14 $k11 "// Wrap."),
206		)
207	);
208
209	// Docs: extra (wrapping) from-int (code).
210	(@ex @from_a $uint:tt $ty:tt $k1:ident $k2:ident $k3:ident $k4:ident $k5:ident $k6:ident $k7:ident) => (
211		concat!(
212			"assert_eq!(", stringify!($ty), "::from(8_", stringify!($uint), "), ", stringify!($ty), "::", stringify!($k1), "); // Wrap.\n",
213			"assert_eq!(", stringify!($ty), "::from(9_", stringify!($uint), "), ", stringify!($ty), "::", stringify!($k2), "); // Wrap.\n",
214			"assert_eq!(", stringify!($ty), "::from(10_", stringify!($uint), "), ", stringify!($ty), "::", stringify!($k3), "); // Wrap.\n",
215			"// …\n",
216		)
217	);
218	(@ex @from_a $uint:tt $ty:tt $k1:ident $k2:ident $k3:ident $k4:ident $k5:ident $k6:ident $k7:ident $k8:ident $k9:ident $k10:ident $k11:ident $k12:ident) => (
219		concat!(
220			"assert_eq!(", stringify!($ty), "::from(13_", stringify!($uint), "), ", stringify!($ty), "::", stringify!($k1), "); // Wrap.\n",
221			"assert_eq!(", stringify!($ty), "::from(14_", stringify!($uint), "), ", stringify!($ty), "::", stringify!($k2), "); // Wrap.\n",
222			"assert_eq!(", stringify!($ty), "::from(15_", stringify!($uint), "), ", stringify!($ty), "::", stringify!($k3), "); // Wrap.\n",
223			"// …\n",
224		)
225	);
226
227	// Docs: `Self::previous`/`Self::next` (partial code).
228	(@ex @pairs $ty:ident $fn:literal $(($from:ident $to:ident)),+ $(,)?) => (concat!(
229		$(
230			"assert_eq!(",
231			stringify!($ty), "::", stringify!($from), ".", $fn, "(), ",
232			stringify!($ty), "::", stringify!($to),
233			");\n",
234		)+
235	));
236
237	// Docs: `iter.next()` (code).
238	(@ex @next $ty:tt $(($k:ident $com:literal)),+ $(,)?) => (concat!(
239		$(
240			"assert_eq!(iter.next(), Some(", stringify!($ty), "::", stringify!($k), ")); ", $com, "\n",
241		)+
242	));
243
244	// Docs: `Self::previous`/`Self::next` (full code).
245	(@ex @iter $ty:tt $k1:ident $k2:ident $k3:ident $k4:ident $k5:ident $k6:ident $k7:ident) => (concat!(
246		"let mut iter = ", stringify!($ty), "::", stringify!($k1), ".into_iter();\n",
247		$crate::macros::weekmonth!(
248			@ex @next $ty
249			($k1 ""), ($k2 ""), ($k3 ""), ($k4 ""), ($k5 ""), ($k6 ""), ($k7 ""),
250			($k1 "// Wrap."), ($k2 "// Wrap."), ($k3 "// Wrap."),
251		),
252		"// …\n\n",
253		"// Or like Ginger, backwards and in high heels.\n",
254		"let mut iter = ", stringify!($ty), "::", stringify!($k7), ".into_iter().rev();\n",
255		$crate::macros::weekmonth!(
256			@ex @next $ty
257			($k7 ""), ($k6 ""), ($k5 ""), ($k4 ""), ($k3 ""), ($k2 ""), ($k1 ""),
258			($k7 "// Wrap."), ($k6 "// Wrap."), ($k5 "// Wrap."),
259		),
260		"// …\n",
261	));
262	(@ex @iter $ty:tt $k1:ident $k2:ident $k3:ident $k4:ident $k5:ident $k6:ident $k7:ident $k8:ident $k9:ident $k10:ident $k11:ident $k12:ident) => (concat!(
263		"let mut iter = ", stringify!($ty), "::", stringify!($k1), ".into_iter();\n",
264		$crate::macros::weekmonth!(
265			@ex @next $ty
266			($k1 ""), ($k2 ""), ($k3 ""), ($k4 ""), ($k5 ""), ($k6 ""), ($k7 ""),
267			($k8 ""), ($k9 ""), ($k10 ""), ($k11 ""), ($k12 ""),
268			($k1 "// Wrap."), ($k2 "// Wrap."), ($k3 "// Wrap."),
269		),
270		"// …\n\n",
271		"// Or like Ginger:\n",
272		"let mut iter = ", stringify!($ty), "::", stringify!($k12), ".into_iter().rev();\n",
273		$crate::macros::weekmonth!(
274			@ex @next $ty
275			($k12 ""), ($k11 ""), ($k10 ""), ($k9 ""), ($k8 ""), ($k7 ""), ($k6 ""),
276			($k5 ""), ($k4 ""), ($k3 ""), ($k2 ""), ($k1 ""),
277			($k12 "// Wrap."), ($k11 "// Wrap."), ($k10 "// Wrap."),
278		),
279		"// …\n",
280	));
281
282	// Wrong Word.
283	(@wrong Month) =>   ( "Janissary" );
284	(@wrong Weekday) => ( "Sunlight" );
285
286	// Match Pairs.
287	(@pairs $src:ident $(($from:ident $to:ident)),+ $(,)?) => (
288		match $src {
289			$( Self::$from => Self::$to, )+
290		}
291	);
292
293	// Add.
294	(@add $uint:tt $ty:tt $( $k:ident $v:literal)+) => (
295		impl ::std::ops::Add<$uint> for $ty {
296			type Output = Self;
297
298			#[inline]
299			#[doc = concat!(
300				"# Wrapping `", stringify!($uint), "` Addition.\n\n",
301
302				$crate::macros::weekmonth!(@ex @range $ty $($k $v)+), "\n\n",
303
304				"## Examples\n\n",
305
306				"```\n",
307				"use utc2k::", stringify!($ty), ";\n\n",
308				$crate::macros::weekmonth!(@ex @add $uint $ty $($k)+),
309				"```",
310			)]
311			fn add(self, other: $uint) -> Self {
312				Self::from(self as $uint + other % $crate::macros::last!($($v)+),)
313			}
314		}
315
316		impl ::std::ops::AddAssign<$uint> for $ty {
317			#[inline]
318			fn add_assign(&mut self, other: $uint) { *self = *self + other; }
319		}
320	);
321
322	// PartialEq.
323	(@eq $uint:ident $ty:ident $( $k:ident $v:literal)+) => (
324		impl PartialEq<$uint> for $ty {
325			#[inline]
326			#[doc = concat!(
327				"# `", stringify!($ty), "`/`", stringify!($uint), "` Equality.\n\n",
328				"```\n",
329				"use utc2k::", stringify!($ty), ";\n\n",
330				$(
331					"assert_eq!(", stringify!($ty), "::", stringify!($k), ", ", stringify!($v), "_", stringify!($uint), ");\n",
332				)+
333				"\n// Nope.\n",
334				"assert_ne!(", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ", ", stringify!($uint), "::MIN);\n",
335				"```",
336			)]
337			fn eq(&self, other: &$uint) -> bool { (*self as $uint) == *other }
338		}
339
340		impl PartialEq<$ty> for $uint {
341			#[inline]
342			#[doc = concat!(
343				"# `", stringify!($uint), "`/`", stringify!($ty), "` Equality.\n\n",
344				"```\n",
345				"use utc2k::", stringify!($ty), ";\n\n",
346				$(
347					"assert_eq!(", stringify!($v), "_", stringify!($uint), ", ", stringify!($ty), "::", stringify!($k), ");\n",
348				)+
349				"```",
350				"\n// Nope.\n",
351				"assert_ne!(", stringify!($uint), "::MIN, ", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ");\n",
352			)]
353			fn eq(&self, other: &$ty) -> bool { <$ty as PartialEq<$uint>>::eq(other, self) }
354		}
355	);
356
357	// From.
358	(@from $uint:ident $ty:ident $( $k:ident $v:literal )+ @last $k_last:ident $v_last:literal) => (
359		impl From<$uint> for $ty {
360			#[inline]
361			#[doc = concat!(
362				"# `", stringify!($ty), "` From `", stringify!($uint), "` (Wrapping).\n\n",
363
364				$crate::macros::weekmonth!(@ex @range $ty $($k $v)+ $k_last $v_last), "\n\n",
365
366				"## Examples\n\n",
367
368				"```\n",
369				"use utc2k::", stringify!($ty), ";\n\n",
370				"assert_eq!(", stringify!($ty), "::from(0_", stringify!($uint), "), ", stringify!($ty), "::", stringify!($k_last), "); // Wrap.\n",
371				$(
372					"assert_eq!(", stringify!($ty), "::from(", stringify!($v), "_", stringify!($uint), "), ", stringify!($ty), "::", stringify!($k), ");\n",
373				)+
374				"assert_eq!(", stringify!($ty), "::from(", stringify!($v_last), "_", stringify!($uint), "), ", stringify!($ty), "::", stringify!($k_last), ");\n",
375				$crate::macros::weekmonth!(@ex @from_a $uint $ty $($k)+ $k_last ),
376				"```",
377			)]
378			fn from(src: $uint) -> Self {
379				match src % $v_last {
380					$( $v => Self::$k ),+,
381					_ => Self::$k_last,
382				}
383			}
384		}
385
386		impl From<$ty> for $uint {
387			#[inline]
388			#[doc = concat!(
389				"# `", stringify!($uint), "` From `", stringify!($ty), "`.\n\n",
390
391				$crate::macros::weekmonth!(@ex @range $ty $($k $v)+ $k_last $v_last), "\n\n",
392
393				"## Examples\n\n",
394
395				"```\n",
396				"use utc2k::", stringify!($ty), ";\n\n",
397				$(
398					"assert_eq!(", stringify!($uint), "::from(", stringify!($ty), "::", stringify!($k), "), ", stringify!($v), ");\n",
399				)+
400				"assert_eq!(", stringify!($uint), "::from(", stringify!($ty), "::", stringify!($k_last), "), ", stringify!($v_last), ");\n\n",
401				"// Same as `as` casting.\n",
402				"for v in ", stringify!($ty), "::ALL {\n",
403				"    assert_eq!(", stringify!($uint), "::from(v), v as ", stringify!($uint), ");\n",
404				"}\n",
405				"```",
406			)]
407			fn from(src: $ty) -> Self {
408				match src {
409					$( <$ty>::$k => $v ),+,
410					<$ty>::$k_last => $v_last,
411				}
412			}
413		}
414	);
415	(
416		@from $uint:ident $ty:ident
417		$k1:ident $v1:literal $k2:ident $v2:literal $k3:ident $v3:literal $k4:ident $v4:literal $k5:ident $v5:literal $k6:ident $v6:literal $k7:ident $v7:literal
418	) => (
419		$crate::macros::weekmonth!(
420			@from $uint $ty
421			$k1 $v1 $k2 $v2 $k3 $v3 $k4 $v4 $k5 $v5 $k6 $v6
422			@last $k7 $v7
423		);
424	);
425	(
426		@from $uint:ident $ty:ident
427		$k1:ident $v1:literal $k2:ident $v2:literal $k3:ident $v3:literal $k4:ident $v4:literal $k5:ident $v5:literal $k6:ident $v6:literal
428		$k7:ident $v7:literal $k8:ident $v8:literal $k9:ident $v9:literal $k10:ident $v10:literal $k11:ident $v11:literal $k12:ident $v12:literal
429	) => (
430		$crate::macros::weekmonth!(
431			@from $uint $ty
432			$k1 $v1 $k2 $v2 $k3 $v3 $k4 $v4 $k5 $v5 $k6 $v6
433			$k7 $v7 $k8 $v8 $k9 $v9 $k10 $v10 $k11 $v11
434			@last $k12 $v12
435		);
436	);
437
438	// Constant From U8.
439	(@from_u8 $ty:ident $($k:ident $v:literal)+ @last $k_last:ident $v_last:literal) => (
440		impl $ty {
441			#[inline]
442			#[must_use]
443			/// # From `u8`.
444			pub(crate) const fn from_u8(src: u8) -> Self {
445				match src % $v_last {
446					$( $v => Self::$k ),+,
447					_ => Self::$k_last,
448				}
449			}
450		}
451	);
452	(
453		@from_u8 $ty:ident
454		$k1:ident $v1:literal $k2:ident $v2:literal $k3:ident $v3:literal $k4:ident $v4:literal $k5:ident $v5:literal $k6:ident $v6:literal $k7:ident $v7:literal
455	) => (
456		$crate::macros::weekmonth!(
457			@from_u8 $ty
458			$k1 $v1 $k2 $v2 $k3 $v3 $k4 $v4 $k5 $v5 $k6 $v6
459			@last $k7 $v7
460		);
461	);
462	(
463		@from_u8 $ty:ident
464		$k1:ident $v1:literal $k2:ident $v2:literal $k3:ident $v3:literal $k4:ident $v4:literal $k5:ident $v5:literal $k6:ident $v6:literal
465		$k7:ident $v7:literal $k8:ident $v8:literal $k9:ident $v9:literal $k10:ident $v10:literal $k11:ident $v11:literal $k12:ident $v12:literal
466	) => (
467		$crate::macros::weekmonth!(
468			@from_u8 $ty
469			$k1 $v1 $k2 $v2 $k3 $v3 $k4 $v4 $k5 $v5 $k6 $v6
470			$k7 $v7 $k8 $v8 $k9 $v9 $k10 $v10 $k11 $v11
471			@last $k12 $v12
472		);
473	);
474
475	// Subtract.
476	(@sub $uint:tt $ty:tt $k_first:ident $v_first:literal ($sub1_first:literal $sub2_first:literal), $( $k:ident $v:literal ($sub1:literal $sub2:literal) ),+ $(,)?) => (
477		impl ::std::ops::Sub<$uint> for $ty {
478			type Output = Self;
479
480			#[inline]
481			#[doc = concat!(
482				"# Wrapping `", stringify!($uint), "` Subtraction.\n\n",
483
484				$crate::macros::weekmonth!(@ex @range $ty $k_first $v_first $($k $v)+), "\n\n",
485
486				"## Examples\n\n",
487
488				"```\n",
489				"use utc2k::", stringify!($ty), ";\n\n",
490				$crate::macros::weekmonth!(@ex @sub $uint $ty $k_first $($k)+),
491				"```",
492			)]
493			fn sub(self, other: $uint) -> Self {
494				match (self as u8 - 1).wrapping_sub((other % $crate::macros::last!($($v)+)) as u8) {
495					0 => Self::$k_first,
496					$( $sub1 | $sub2 => Self::$k ),+,
497					_ => unreachable!(),
498				}
499			}
500		}
501
502		impl ::std::ops::SubAssign<$uint> for $ty {
503			#[inline]
504			fn sub_assign(&mut self, other: $uint) { *self = *self - other; }
505		}
506	);
507
508	// TryFrom Reference.
509	(@try_from @as_bytes $ty:tt $($from:ty)+) => ($(
510		impl TryFrom<$from> for $ty {
511			type Error = Utc2kError;
512
513			#[inline]
514			fn try_from(src: $from) -> Result<Self, Self::Error> {
515				Self::try_from(src.as_bytes())
516			}
517		}
518	)+);
519
520	// Integer implementations.
521	(@int $uint:ident $ty:ident $( $k:ident $v:literal ($sub1:literal $sub2:literal) ),+ $(,)?) => (
522		$crate::macros::weekmonth!(@add  $uint $ty $($k $v)+);
523		$crate::macros::weekmonth!(@eq   $uint $ty $($k $v)+);
524		$crate::macros::weekmonth!(@from $uint $ty $($k $v)+);
525		$crate::macros::weekmonth!(@sub  $uint $ty $($k $v ($sub1 $sub2)),+);
526	);
527
528	// Entrypoint.
529	($ty:tt $lower:ident $iter:ident $($k:ident $v:literal $abbr:literal ($sub1:literal $sub2:literal)),+ $(,)?) => (
530		#[expect(missing_docs, reason = "Redundant.")]
531		#[repr(u8)]
532		#[derive(Debug, Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
533		#[doc = concat!(
534			"# ", stringify!($ty), ".\n\n",
535
536			"This enum is used by [`Utc2k`] to differentiate between calendar ", stringify!($lower), "s.\n\n",
537
538			"## Examples\n\n",
539
540			"```\n",
541			"use utc2k::", stringify!($ty), ";\n\n",
542			"// The first.\n",
543			"# assert_eq!(", stringify!($ty), "::default(), ", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ");\n",
544			"assert_eq!(", stringify!($ty) ,"::", $crate::macros::first!(@stringify $($k)+), " as u8, 1_u8);\n",
545			"assert_eq!(", stringify!($ty) ,"::", $crate::macros::first!(@stringify $($k)+), ".as_str(), \"", $crate::macros::first!(@stringify $($k)+), "\");\n",
546			"assert_eq!(", stringify!($ty) ,"::", $crate::macros::first!(@stringify $($k)+), ".abbreviation(), \"", $crate::macros::first!($($abbr)+), "\");\n\n",
547			"// The last.\n",
548			"assert_eq!(", stringify!($ty) ,"::", $crate::macros::last!(@stringify $($k)+), " as u8, ", $crate::macros::last!(@stringify $($v)+), "_u8);\n",
549			"assert_eq!(", stringify!($ty) ,"::", $crate::macros::last!(@stringify $($k)+), ".as_str(), \"", $crate::macros::last!(@stringify $($k)+), "\");\n",
550			"assert_eq!(", stringify!($ty) ,"::", $crate::macros::last!(@stringify $($k)+), ".abbreviation(), \"", $crate::macros::last!($($abbr)+), "\");\n",
551			"```",
552		)]
553		pub enum $ty {
554			#[default]
555			$( $k = $v ),+
556		}
557
558		$crate::macros::as_ref_borrow_cast!($ty: as_str str);
559		$crate::macros::display_str!(as_str $ty);
560		$crate::macros::weekmonth!(
561			@try_from @as_bytes $ty
562			&str &String String &std::borrow::Cow<'_, str>
563			std::borrow::Cow<'_, str> &Box<str> Box<str>
564		);
565
566		impl From<Utc2k> for $ty {
567			#[inline]
568			#[doc = concat!(
569				"# From [`Utc2k`].\n\n",
570
571				"This is equivalent to calling [`Utc2k::", stringify!($lower), "`].\n\n",
572
573				"## Examples\n\n",
574
575				"```\n",
576				"use utc2k::{", stringify!($ty), ", Utc2k};\n\n",
577				"let utc = Utc2k::new(2030, 1, 6, 0, 0, 0);\n",
578				"assert_eq!(", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ", ", stringify!($ty), "::from(utc));\n",
579				"assert_eq!(", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ", utc.", stringify!($lower), "());\n",
580				"```",
581			)]
582			fn from(src: Utc2k) -> Self { src.$lower() }
583		}
584
585		impl ::std::str::FromStr for $ty {
586			type Err = Utc2kError;
587
588			#[inline]
589			#[doc = concat!(
590				"# Parse From String.\n\n",
591
592				"Parse a `", stringify!($ty), "` from the first three letters \
593				of a string, case-insensitively.\n\n",
594
595				"## Examples\n\n",
596
597				"```\n",
598				"use utc2k::", stringify!($ty), ";\n\n",
599				"for v in ", stringify!($ty), "::ALL {\n",
600				"    assert_eq!(v.as_str().parse::<", stringify!($ty), ">(), Ok(v));\n",
601				"    assert_eq!(v.abbreviation().parse::<", stringify!($ty), ">(), Ok(v));\n",
602				"}\n\n",
603				"// Remember that only the first three letters count!\n",
604				"assert_eq!(\"", $crate::macros::weekmonth!(@wrong $ty), "\".parse::<", stringify!($ty), ">(), Ok(", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), "));\n",
605				"```",
606			)]
607			fn from_str(src: &str) -> Result<Self, Self::Err> { Self::try_from(src) }
608		}
609
610		impl IntoIterator for $ty {
611			type Item = Self;
612			type IntoIter = $iter;
613
614			#[inline]
615			#[doc = concat!(
616				"# Endless `", stringify!($ty), "` Iterator.\n\n",
617
618				"Return an iterator that will cycle endlessly through the ", stringify!($lower), "s, \
619				in order, forward or backward, starting with `self`.\n\n",
620
621				"## Examples\n\n",
622
623				"```\n",
624				"use utc2k::", stringify!($ty), ";\n\n",
625				$crate::macros::weekmonth!(@ex @iter $ty $($k)+),
626				"```",
627			)]
628			fn into_iter(self) -> Self::IntoIter { $iter(self) }
629		}
630
631		$crate::macros::weekmonth!(@int u8    $ty $($k $v ($sub1 $sub2)),+);
632		$crate::macros::weekmonth!(@int u16   $ty $($k $v ($sub1 $sub2)),+);
633		$crate::macros::weekmonth!(@int u32   $ty $($k $v ($sub1 $sub2)),+);
634		$crate::macros::weekmonth!(@int u64   $ty $($k $v ($sub1 $sub2)),+);
635		$crate::macros::weekmonth!(@int usize $ty $($k $v ($sub1 $sub2)),+);
636
637		impl $ty {
638			#[doc = concat!(
639				"# All ", stringify!($ty), "s.\n\n",
640				"This array contains all of the ", stringify!($lower), "s, in order.\n\n",
641				"## Examples\n\n",
642				"```\n",
643				"# assert!(utc2k::", stringify!($ty), "::ALL.is_sorted());\n",
644				"for pair in utc2k::", stringify!($ty), "::ALL.windows(2) {\n",
645				"    assert!(pair[0] < pair[1]);\n",
646				"}\n",
647				"```",
648			)]
649			pub const ALL: [Self; $crate::macros::last!($($v)+)] = [ $( Self::$k ),+ ];
650
651			#[inline]
652			#[must_use]
653			#[doc = concat!(
654				"# As String Slice (Abbreviated).\n\n",
655
656				"Return the three-letter abbreviation for a given ", stringify!($lower), " as a static string slice.\n\n",
657
658				"## Examples\n\n",
659
660				"```\n",
661				"use utc2k::", stringify!($ty), ";\n\n",
662				$(
663					"assert_eq!(", stringify!($ty), "::", stringify!($k), ".abbreviation(), \"", $abbr, "\");\n",
664				)+
665				"# for v in ", stringify!($ty), "::ALL {\n",
666				"#    assert_eq!(v.abbreviation(), &v.as_str()[..3]);\n",
667				"# }\n",
668				"```",
669			)]
670			pub const fn abbreviation(self) -> &'static str {
671				match self {
672					$( Self::$k => $abbr ),+
673				}
674			}
675
676			#[inline]
677			#[must_use]
678			#[doc = concat!(
679				"# As String Slice.\n\n",
680
681				"Return the name of a given ", stringify!($lower), " as a static string slice.\n\n",
682
683				"## Examples\n\n",
684
685				"```\n",
686				"use utc2k::", stringify!($ty), ";\n\n",
687				$(
688					"assert_eq!(", stringify!($ty), "::", stringify!($k), ".as_str(), \"", stringify!($k), "\");\n",
689				)+
690				"```",
691			)]
692			pub const fn as_str(self) -> &'static str {
693				match self {
694					$( Self::$k => stringify!($k) ),+
695				}
696			}
697
698			#[inline]
699			#[must_use]
700			#[doc = concat!(
701				"# Previous ", stringify!($ty), " (Wrapping).\n\n",
702
703				"Return the previous [`", stringify!($ty), "`].\n\n",
704
705				"## Examples\n\n",
706
707				"```\n",
708				"use utc2k::", stringify!($ty), ";\n\n",
709				$crate::macros::pair!(@previous weekmonth { @ex @pairs $ty "previous" } $($k)+), "\n",
710				"// Same as math:\n",
711				"assert_eq!(", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ".previous(), ", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), " - 1_u8);\n\n",
712				"// Same as the proper iterator too (provided you skip the first value):\n",
713				"assert_eq!(\n",
714				"    Some(", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ".previous()),\n",
715				"    ", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ".into_iter().rev().skip(1).next(),\n",
716				");\n",
717				"```",
718			)]
719			pub const fn previous(self) -> Self {
720				$crate::macros::pair!(@previous weekmonth { @pairs self } $($k)+)
721			}
722
723			#[inline]
724			#[must_use]
725			#[doc = concat!(
726				"# Next ", stringify!($ty), " (Wrapping).\n\n",
727
728				"Return the next [`", stringify!($ty), "`].\n\n",
729
730				"## Examples\n\n",
731
732				"```\n",
733				"use utc2k::", stringify!($ty), ";\n\n",
734				$crate::macros::pair!(@next weekmonth { @ex @pairs $ty "next" } $($k)+), "\n",
735				"// Same as math:\n",
736				"assert_eq!(", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ".next(), ", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), " + 1_u8);\n\n",
737				"// Same as the proper iterator too (provided you skip the first value):\n",
738				"assert_eq!(\n",
739				"    Some(", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ".next()),\n",
740				"    ", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ".into_iter().skip(1).next(),\n",
741				");\n",
742				"```",
743			)]
744			pub const fn next(self) -> Self {
745				$crate::macros::pair!(@next weekmonth { @pairs self } $($k)+)
746			}
747
748			#[inline]
749			#[must_use]
750			#[doc = concat!(
751				"# Compare Two `", stringify!($ty), "`s.\n\n",
752
753				"Same as `Ord`/`PartialOrd`, but constant.\n\n",
754
755				"## Examples\n\n",
756
757				"```\n",
758				"use utc2k::", stringify!($ty), ";\n\n",
759				"assert_eq!(\n",
760				"    ", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ".cmp(&", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), "),\n",
761				"    ", stringify!($ty), "::cmp(", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ", ", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), "), // Ordering::Equal\n",
762				");\n",
763				"assert_eq!(\n",
764				"    ", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ".cmp(&", stringify!($ty), "::", $crate::macros::last!(@stringify $($k)+), "),\n",
765				"    ", stringify!($ty), "::cmp(", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), ", ", stringify!($ty), "::", $crate::macros::last!(@stringify $($k)+), "), // Ordering::Less\n",
766				");\n",
767				"assert_eq!(\n",
768				"    ", stringify!($ty), "::", $crate::macros::last!(@stringify $($k)+), ".cmp(&", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), "),\n",
769				"    ", stringify!($ty), "::cmp(", stringify!($ty), "::", $crate::macros::last!(@stringify $($k)+), ", ", stringify!($ty), "::", $crate::macros::first!(@stringify $($k)+), "), // Ordering::Greater\n",
770				");\n",
771				"```",
772			)]
773			pub const fn cmp(a: Self, b: Self) -> ::std::cmp::Ordering {
774				let a = a as u8;
775				let b = b as u8;
776				if a == b { ::std::cmp::Ordering::Equal }
777				else if a < b { ::std::cmp::Ordering::Less }
778				else { ::std::cmp::Ordering::Greater }
779			}
780		}
781
782		$crate::macros::weekmonth!(@from_u8 $ty $($k $v)+);
783
784		#[derive(Debug, Clone)]
785		#[doc = concat!(
786			"# Endless `", stringify!($ty), "` Iterator.\n\n",
787
788			"This iterator yields infinite [`", stringify!($ty), "`]s, \
789			in order, forward or backward, starting with any arbitrary variant.\n\n",
790
791			"See [`", stringify!($ty), "::into_iter`] for more details.",
792		)]
793		pub struct $iter($ty);
794
795		impl Iterator for $iter {
796			type Item = $ty;
797
798			#[inline]
799			#[doc = concat!("# Next [`", stringify!($ty), "`].")]
800			fn next(&mut self) -> Option<Self::Item> {
801				let next = self.0;
802				self.0 = next + 1_u8;
803				Some(next)
804			}
805
806			#[inline]
807			/// # Infinity.
808			///
809			/// This iterator never stops!
810			fn size_hint(&self) -> (usize, Option<usize>) { (usize::MAX, None) }
811		}
812
813		impl DoubleEndedIterator for $iter {
814			#[inline]
815			#[doc = concat!("# Previous [`", stringify!($ty), "`].")]
816			fn next_back(&mut self) -> Option<Self::Item> {
817				let next = self.0;
818				self.0 = next - 1_u8;
819				Some(next)
820			}
821		}
822	);
823}
824
825
826
827pub(super) use {
828	as_ref_borrow_cast,
829	display_str,
830	first,
831	last,
832	pair,
833	weekmonth,
834};