1use crate::types::MonthCode;
6use displaydoc::Display;
7
8#[derive(Debug, Copy, Clone, PartialEq, Display)]
9#[non_exhaustive]
11pub enum DateError {
12 #[displaydoc("The {field} = {value} argument is out of range {min}..={max}")]
14 Range {
15 field: &'static str,
17 value: i32,
19 min: i32,
21 max: i32,
23 },
24 #[displaydoc("Unknown era")]
26 UnknownEra,
27 #[displaydoc("Unknown month code {0:?}")]
29 UnknownMonthCode(MonthCode),
30}
31
32impl core::error::Error for DateError {}
33
34#[derive(Debug, Copy, Clone, PartialEq, Display)]
35#[displaydoc("The {field} = {value} argument is out of range {min}..={max}")]
37#[allow(clippy::exhaustive_structs)]
38pub struct RangeError {
39 pub field: &'static str,
41 pub value: i32,
43 pub min: i32,
45 pub max: i32,
47}
48
49impl core::error::Error for RangeError {}
50
51impl From<RangeError> for DateError {
52 fn from(value: RangeError) -> Self {
53 let RangeError {
54 field,
55 value,
56 min,
57 max,
58 } = value;
59 DateError::Range {
60 field,
61 value,
62 min,
63 max,
64 }
65 }
66}
67
68pub(crate) fn year_check(
69 year: i32,
70 bounds: impl core::ops::RangeBounds<i32>,
71) -> Result<i32, RangeError> {
72 use core::ops::Bound::*;
73
74 if !bounds.contains(&year) {
75 return Err(RangeError {
76 field: "year",
77 value: year,
78 min: match bounds.start_bound() {
79 Included(&m) => m,
80 Excluded(&m) => m + 1,
81 Unbounded => i32::MIN,
82 },
83 max: match bounds.end_bound() {
84 Included(&m) => m,
85 Excluded(&m) => m - 1,
86 Unbounded => i32::MAX,
87 },
88 });
89 }
90
91 Ok(year)
92}