rustbac_client/
schedule.rs1use crate::ClientDataValue;
7use rustbac_core::types::{Date, Time};
8
9#[derive(Debug, Clone, PartialEq)]
11pub struct TimeValue {
12 pub time: Time,
13 pub value: ClientDataValue,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct DateRange {
19 pub start: Date,
20 pub end: Date,
21}
22
23#[derive(Debug, Clone, PartialEq)]
25pub enum CalendarEntry {
26 Date(Date),
27 Range(DateRange),
28 WeekNDay {
29 month: u8,
30 week_of_month: u8,
31 day_of_week: u8,
32 },
33}
34
35pub fn decode_weekly_schedule(value: &ClientDataValue) -> Option<Vec<Vec<TimeValue>>> {
40 let days = match value {
41 ClientDataValue::Constructed { values, .. } => values,
42 _ => return None,
43 };
44
45 let mut week = Vec::with_capacity(7);
46 for day in days {
47 let day_values = match day {
48 ClientDataValue::Constructed { values, .. } => values,
49 _ => {
50 week.push(Vec::new());
51 continue;
52 }
53 };
54
55 let mut entries = Vec::new();
56 let mut i = 0;
57 while i + 1 < day_values.len() {
58 if let ClientDataValue::Time(t) = &day_values[i] {
59 entries.push(TimeValue {
60 time: *t,
61 value: day_values[i + 1].clone(),
62 });
63 i += 2;
64 } else {
65 i += 1;
66 }
67 }
68 week.push(entries);
69 }
70
71 Some(week)
72}
73
74pub fn encode_weekly_schedule(week: &[Vec<TimeValue>]) -> ClientDataValue {
76 let mut days = Vec::with_capacity(week.len());
77 for (i, day) in week.iter().enumerate() {
78 let mut values = Vec::with_capacity(day.len() * 2);
79 for entry in day {
80 values.push(ClientDataValue::Time(entry.time));
81 values.push(entry.value.clone());
82 }
83 days.push(ClientDataValue::Constructed {
84 tag_num: i as u8,
85 values,
86 });
87 }
88 ClientDataValue::Constructed {
89 tag_num: 0,
90 values: days,
91 }
92}
93
94pub fn decode_date_list(value: &ClientDataValue) -> Option<Vec<CalendarEntry>> {
96 let items = match value {
97 ClientDataValue::Constructed { values, .. } => values,
98 _ => return None,
99 };
100
101 let mut entries = Vec::new();
102 for item in items {
103 match item {
104 ClientDataValue::Date(d) => entries.push(CalendarEntry::Date(*d)),
105 ClientDataValue::Constructed { tag_num: 1, values } if values.len() == 2 => {
106 if let (ClientDataValue::Date(start), ClientDataValue::Date(end)) =
107 (&values[0], &values[1])
108 {
109 entries.push(CalendarEntry::Range(DateRange {
110 start: *start,
111 end: *end,
112 }));
113 }
114 }
115 ClientDataValue::Constructed { tag_num: 2, values } if values.len() == 3 => {
116 if let (
117 ClientDataValue::Unsigned(month),
118 ClientDataValue::Unsigned(week),
119 ClientDataValue::Unsigned(day),
120 ) = (&values[0], &values[1], &values[2])
121 {
122 entries.push(CalendarEntry::WeekNDay {
123 month: *month as u8,
124 week_of_month: *week as u8,
125 day_of_week: *day as u8,
126 });
127 }
128 }
129 _ => {}
130 }
131 }
132
133 Some(entries)
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use rustbac_core::types::Time;
140
141 #[test]
142 fn weekly_schedule_roundtrip() {
143 let monday = vec![
144 TimeValue {
145 time: Time {
146 hour: 8,
147 minute: 0,
148 second: 0,
149 hundredths: 0,
150 },
151 value: ClientDataValue::Real(72.0),
152 },
153 TimeValue {
154 time: Time {
155 hour: 18,
156 minute: 0,
157 second: 0,
158 hundredths: 0,
159 },
160 value: ClientDataValue::Real(65.0),
161 },
162 ];
163
164 let mut week = vec![Vec::new(); 7];
165 week[1] = monday.clone();
166
167 let encoded = encode_weekly_schedule(&week);
168 let decoded = decode_weekly_schedule(&encoded).unwrap();
169 assert_eq!(decoded.len(), 7);
170 assert!(decoded[0].is_empty());
171 assert_eq!(decoded[1].len(), 2);
172 assert_eq!(decoded[1][0].time.hour, 8);
173 assert_eq!(decoded[1][1].time.hour, 18);
174 }
175
176 #[test]
177 fn decode_date_list_entries() {
178 let date = Date {
179 year_since_1900: 124,
180 month: 12,
181 day: 25,
182 weekday: 0xFF,
183 };
184
185 let value = ClientDataValue::Constructed {
186 tag_num: 0,
187 values: vec![ClientDataValue::Date(date)],
188 };
189
190 let entries = decode_date_list(&value).unwrap();
191 assert_eq!(entries.len(), 1);
192 assert_eq!(entries[0], CalendarEntry::Date(date));
193 }
194}