dpm_model/calendar/
clock.rs1use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub struct ClockTime(u16);
8
9const DAY_MINUTES: u16 = 24 * 60;
11
12impl ClockTime {
13 #[must_use]
15 pub const fn hours(hours: u16) -> Self {
16 let minutes = hours.saturating_mul(60);
17 Self(if minutes > DAY_MINUTES {
18 DAY_MINUTES
19 } else {
20 minutes
21 })
22 }
23
24 #[must_use]
26 pub fn from_minutes(minutes: u16) -> Option<Self> {
27 (minutes <= DAY_MINUTES).then_some(Self(minutes))
28 }
29
30 #[must_use]
32 pub fn minutes(self) -> u16 {
33 self.0
34 }
35}
36
37impl std::fmt::Display for ClockTime {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 write!(f, "{:02}:{:02}", self.0 / 60, self.0 % 60)
40 }
41}
42
43impl std::str::FromStr for ClockTime {
44 type Err = String;
45
46 fn from_str(text: &str) -> Result<Self, Self::Err> {
47 let invalid = || format!("time {text:?} must be HH:MM between 00:00 and 24:00");
48 let (hours, minutes) = text.split_once(':').ok_or_else(invalid)?;
49 if hours.len() != 2 || minutes.len() != 2 {
50 return Err(invalid());
51 }
52 let hours: u16 = hours.parse().map_err(|_| invalid())?;
53 let minutes: u16 = minutes.parse().map_err(|_| invalid())?;
54 if minutes >= 60 {
55 return Err(invalid());
56 }
57 Self::from_minutes(hours.saturating_mul(60).saturating_add(minutes)).ok_or_else(invalid)
58 }
59}
60
61impl Serialize for ClockTime {
62 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
63 serializer.collect_str(self)
64 }
65}
66
67struct ClockVisitor;
68
69impl de::Visitor<'_> for ClockVisitor {
70 type Value = ClockTime;
71
72 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73 f.write_str("a local time HH:MM")
74 }
75
76 fn visit_str<E: de::Error>(self, text: &str) -> Result<ClockTime, E> {
77 text.parse().map_err(E::custom)
78 }
79}
80
81impl<'de> Deserialize<'de> for ClockTime {
82 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
83 deserializer.deserialize_str(ClockVisitor)
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
90#[serde(deny_unknown_fields)]
91pub struct WorkingPeriod {
92 pub from: ClockTime,
94 pub to: ClockTime,
96}
97
98impl WorkingPeriod {
99 #[must_use]
101 pub fn new(from: ClockTime, to: ClockTime) -> Self {
102 Self { from, to }
103 }
104
105 #[must_use]
107 pub fn start(self) -> ClockTime {
108 self.from
109 }
110
111 #[must_use]
113 pub fn end(self) -> ClockTime {
114 self.to
115 }
116}