sofie_values/time/
weekday.rs1use jiff::civil;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use sofie_core::variants::{self, Variants};
4use sofie_core::{Cast, ColumnReader, ColumnSpec, Result, Show, Shown, SqlValue};
5
6#[cfg(feature = "web")]
7use crate::Conventions;
8use crate::{Input, Present};
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
12pub enum Weekday {
13 Monday,
14 Tuesday,
15 Wednesday,
16 Thursday,
17 Friday,
18 Saturday,
19 Sunday,
20}
21
22const NAMES: [&str; 7] = [
23 "MONDAY",
24 "TUESDAY",
25 "WEDNESDAY",
26 "THURSDAY",
27 "FRIDAY",
28 "SATURDAY",
29 "SUNDAY",
30];
31
32const IN_ORDER: [Weekday; 7] = [
33 Weekday::Monday,
34 Weekday::Tuesday,
35 Weekday::Wednesday,
36 Weekday::Thursday,
37 Weekday::Friday,
38 Weekday::Saturday,
39 Weekday::Sunday,
40];
41
42impl Weekday {
43 #[must_use]
46 pub const fn constant(name: &str) -> Self {
47 let mut index = 0;
48 while index < NAMES.len() {
49 if lowercase_equals(NAMES[index].as_bytes(), name.as_bytes()) {
50 return IN_ORDER[index];
51 }
52 index += 1;
53 }
54 panic!("a weekday is written in English and lowercase, such as monday");
55 }
56}
57
58const fn lowercase_equals(upper: &[u8], written: &[u8]) -> bool {
59 if upper.len() != written.len() {
60 return false;
61 }
62 let mut index = 0;
63 while index < upper.len() {
64 if upper[index].to_ascii_lowercase() != written[index] {
65 return false;
66 }
67 index += 1;
68 }
69 true
70}
71
72impl From<civil::Weekday> for Weekday {
73 fn from(weekday: civil::Weekday) -> Self {
74 let offset = weekday.to_monday_zero_offset().unsigned_abs();
75 IN_ORDER[usize::from(offset)]
76 }
77}
78
79impl Variants for Weekday {
80 const TYPE: &'static str = "Weekday";
81 const VARIANTS: &'static [(&'static str, Self)] = &[
82 (NAMES[0], Self::Monday),
83 (NAMES[1], Self::Tuesday),
84 (NAMES[2], Self::Wednesday),
85 (NAMES[3], Self::Thursday),
86 (NAMES[4], Self::Friday),
87 (NAMES[5], Self::Saturday),
88 (NAMES[6], Self::Sunday),
89 ];
90}
91
92impl Show for Weekday {
93 fn show(&self) -> Shown<'_> {
94 Shown::Text(self.name())
95 }
96}
97
98impl Cast for Weekday {
99 const COLUMNS: &'static [ColumnSpec] = &[variants::COLUMNS[0].with_values(&NAMES)];
100
101 fn encode(&self, out: &mut Vec<SqlValue>) {
102 variants::encode(*self, out);
103 }
104
105 fn decode(row: &mut dyn ColumnReader) -> Result<Self> {
106 variants::decode(row)
107 }
108}
109
110impl Serialize for Weekday {
111 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
112 variants::serialize(*self, serializer)
113 }
114}
115
116impl<'de> Deserialize<'de> for Weekday {
117 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
118 variants::deserialize(deserializer)
119 }
120}
121
122impl Present for Weekday {
123 const INPUT: Input = Input::select(&NAMES);
124 const SEARCH: crate::Search = crate::Search::Exact;
125
126 fn csv(&self) -> String {
127 self.name().to_owned()
128 }
129
130 #[cfg(feature = "web")]
131 fn write_localized(&self, out: &mut String, conventions: &dyn Conventions) {
132 conventions.write_variant(out, "weekday", self.name());
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use crate::Date;
140 use crate::testing::round_trip;
141
142 #[test]
143 fn a_day_falls_on_its_weekday() {
144 let sunday = Date::parse("2026-10-25").unwrap();
145
146 assert_eq!(sunday.weekday(), Weekday::Sunday);
147 assert_eq!(
148 Date::parse("2026-10-26").unwrap().weekday(),
149 Weekday::Monday
150 );
151 }
152
153 #[test]
154 fn a_constant_is_its_lowercase_english_name() {
155 assert_eq!(Weekday::constant("monday"), Weekday::Monday);
156 assert_eq!(Weekday::constant("sunday"), Weekday::Sunday);
157 }
158
159 #[test]
160 #[should_panic(expected = "a weekday is written in English and lowercase")]
161 fn a_wrong_constant_day_fails() {
162 let _ = Weekday::constant("Monday");
163 }
164
165 #[test]
166 fn round_trips_through_its_name_like_an_enum() {
167 assert_eq!(
168 round_trip(&Weekday::Wednesday),
169 vec![SqlValue::Text("WEDNESDAY".into())]
170 );
171 assert_eq!(Weekday::COLUMNS[0].values, NAMES);
172 let json = serde_json::to_value(Weekday::Friday).unwrap();
173 assert_eq!(json, serde_json::json!("FRIDAY"));
174 assert_eq!(
175 serde_json::from_value::<Weekday>(json).unwrap(),
176 Weekday::Friday
177 );
178 assert!(serde_json::from_value::<Weekday>(serde_json::json!("friday")).is_err());
179 assert_eq!(Weekday::INPUT.options, NAMES);
180 assert_eq!(Weekday::Saturday.csv(), "SATURDAY");
181 }
182}