bacnet_encoding/constructed/
calendar.rs1#[cfg(not(feature = "std"))]
20use alloc::vec::Vec;
21
22use bacnet_types::constructed::{BACnetCalendarEntry, BACnetDateRange, BACnetWeekNDay};
23use bacnet_types::error::{DecodeError, DecodingKind, Error};
24use bacnet_types::primitives::Date;
25use bytes::BytesMut;
26
27use super::tagged::{contents, expect_closing};
28use super::MAX_FRAMED_ITEMS;
29use crate::primitives;
30use crate::tags::{self, TagClass};
31
32pub fn encode_date_range(buf: &mut BytesMut, range: &BACnetDateRange) {
34 primitives::encode_app_date(buf, &range.start_date);
35 primitives::encode_app_date(buf, &range.end_date);
36}
37
38pub fn decode_date_range(data: &[u8], offset: usize) -> Result<(BACnetDateRange, usize), Error> {
43 let (start_date, offset) = decode_app_date(data, offset, "date range start-date")?;
44 let (end_date, offset) = decode_app_date(data, offset, "date range end-date")?;
45 Ok((
46 BACnetDateRange {
47 start_date,
48 end_date,
49 },
50 offset,
51 ))
52}
53
54pub fn encode_calendar_entry(buf: &mut BytesMut, entry: &BACnetCalendarEntry) {
56 match entry {
57 BACnetCalendarEntry::Date(date) => primitives::encode_ctx_date(buf, 0, date),
58 BACnetCalendarEntry::DateRange(range) => {
59 tags::encode_opening_tag(buf, 1);
60 encode_date_range(buf, range);
61 tags::encode_closing_tag(buf, 1);
62 }
63 BACnetCalendarEntry::WeekNDay(week_n_day) => {
64 primitives::encode_ctx_octet_string(buf, 2, &week_n_day.encode());
65 }
66 }
67}
68
69pub fn decode_calendar_entry(
78 data: &[u8],
79 offset: usize,
80) -> Result<(BACnetCalendarEntry, usize), Error> {
81 let (tag, content) = tags::decode_tag(data, offset)?;
82 if tag.is_context(0) {
83 let (octets, end) = fixed_content(data, offset, content, tag.length, 4, "date [0]")?;
84 return Ok((BACnetCalendarEntry::Date(Date::decode(octets)?), end));
85 }
86 if tag.is_opening_tag(1) {
87 let (range, after) = decode_date_range(data, content)?;
88 let end = expect_closing(data, after, 1, "calendar entry date-range")?;
89 return Ok((BACnetCalendarEntry::DateRange(range), end));
90 }
91 if tag.is_context(2) {
92 let (octets, end) = fixed_content(data, offset, content, tag.length, 3, "weekNDay [2]")?;
93 return Ok((
94 BACnetCalendarEntry::WeekNDay(BACnetWeekNDay::decode(octets)?),
95 end,
96 ));
97 }
98 Err(Error::decoding(
99 offset,
100 "calendar entry requires date [0], date-range [1] or weekNDay [2]",
101 ))
102}
103
104pub fn encode_calendar_entry_list(buf: &mut BytesMut, entries: &[BACnetCalendarEntry]) {
106 for entry in entries {
107 encode_calendar_entry(buf, entry);
108 }
109}
110
111pub fn decode_calendar_entry_list(data: &[u8]) -> Result<Vec<BACnetCalendarEntry>, Error> {
115 let mut entries = Vec::new();
116 let mut offset = 0;
117 while offset < data.len() {
118 if entries.len() >= MAX_FRAMED_ITEMS {
119 return Err(Error::overflow(
120 offset,
121 "calendar entry list exceeds item limit",
122 ));
123 }
124 let (entry, end) = decode_calendar_entry(data, offset)?;
125 entries.push(entry);
126 offset = end;
127 }
128 Ok(entries)
129}
130
131fn decode_app_date(data: &[u8], offset: usize, what: &'static str) -> Result<(Date, usize), Error> {
133 let (tag, content) = tags::decode_tag(data, offset)?;
134 if tag.class != TagClass::Application || tag.number != tags::app_tag::DATE {
135 return Err(DecodeError::new(
136 DecodingKind::InvalidEncoding,
137 offset,
138 "expected an application-tagged Date",
139 )
140 .within(what)
141 .into());
142 }
143 let (octets, end) = fixed_content(data, offset, content, tag.length, 4, what)?;
144 Ok((Date::decode(octets)?, end))
145}
146
147fn fixed_content<'a>(
150 data: &'a [u8],
151 offset: usize,
152 content: usize,
153 length: u32,
154 expected: usize,
155 what: &'static str,
156) -> Result<(&'a [u8], usize), Error> {
157 if usize::try_from(length).ok() != Some(expected) {
158 return Err(DecodeError::new(
159 DecodingKind::InvalidEncoding,
160 offset,
161 "expected {} content octets, got {}",
162 )
163 .within(what)
164 .with_values(expected as u64, length.into())
165 .into());
166 }
167 contents(data, content, length)
168}