1use crate::error::BticError;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
7#[repr(u8)]
8pub enum Granularity {
9 Millisecond = 0,
10 Second = 1,
11 Minute = 2,
12 Hour = 3,
13 Day = 4,
14 Month = 5,
15 Quarter = 6,
16 Year = 7,
17 Decade = 8,
18 Century = 9,
19 Millennium = 10,
20}
21
22impl Granularity {
23 pub fn from_code(code: u8) -> Result<Self, BticError> {
25 match code {
26 0 => Ok(Self::Millisecond),
27 1 => Ok(Self::Second),
28 2 => Ok(Self::Minute),
29 3 => Ok(Self::Hour),
30 4 => Ok(Self::Day),
31 5 => Ok(Self::Month),
32 6 => Ok(Self::Quarter),
33 7 => Ok(Self::Year),
34 8 => Ok(Self::Decade),
35 9 => Ok(Self::Century),
36 10 => Ok(Self::Millennium),
37 _ => Err(BticError::GranularityRange(code)),
38 }
39 }
40
41 pub fn code(self) -> u8 {
43 self as u8
44 }
45
46 pub fn name(self) -> &'static str {
48 match self {
49 Self::Millisecond => "millisecond",
50 Self::Second => "second",
51 Self::Minute => "minute",
52 Self::Hour => "hour",
53 Self::Day => "day",
54 Self::Month => "month",
55 Self::Quarter => "quarter",
56 Self::Year => "year",
57 Self::Decade => "decade",
58 Self::Century => "century",
59 Self::Millennium => "millennium",
60 }
61 }
62
63 pub fn from_name(s: &str) -> Option<Self> {
65 match s.to_lowercase().as_str() {
66 "millisecond" | "ms" => Some(Self::Millisecond),
67 "second" | "s" => Some(Self::Second),
68 "minute" | "min" => Some(Self::Minute),
69 "hour" | "h" => Some(Self::Hour),
70 "day" | "d" => Some(Self::Day),
71 "month" | "mon" => Some(Self::Month),
72 "quarter" | "q" => Some(Self::Quarter),
73 "year" | "y" => Some(Self::Year),
74 "decade" => Some(Self::Decade),
75 "century" => Some(Self::Century),
76 "millennium" => Some(Self::Millennium),
77 _ => None,
78 }
79 }
80
81 pub fn finer(self, other: Self) -> Self {
83 std::cmp::min(self, other)
84 }
85}
86
87impl std::fmt::Display for Granularity {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 f.write_str(self.name())
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[test]
98 fn roundtrip_all_codes() {
99 for code in 0..=10u8 {
100 let g = Granularity::from_code(code).unwrap();
101 assert_eq!(g.code(), code);
102 }
103 }
104
105 #[test]
106 fn reserved_codes_rejected() {
107 for code in 11..=15u8 {
108 assert!(Granularity::from_code(code).is_err());
109 }
110 }
111
112 #[test]
113 fn name_roundtrip() {
114 for code in 0..=10u8 {
115 let g = Granularity::from_code(code).unwrap();
116 let g2 = Granularity::from_name(g.name()).unwrap();
117 assert_eq!(g, g2);
118 }
119 }
120
121 #[test]
122 fn finer_picks_lower_code() {
123 assert_eq!(Granularity::Day.finer(Granularity::Year), Granularity::Day);
124 assert_eq!(
125 Granularity::Year.finer(Granularity::Month),
126 Granularity::Month
127 );
128 assert_eq!(
129 Granularity::Millisecond.finer(Granularity::Millennium),
130 Granularity::Millisecond
131 );
132 }
133}