use bacnet_encoding::constructed::{decode_calendar_entry, encode_calendar_entry};
use bacnet_encoding::tags::{self, TagClass};
use bacnet_types::constructed::BACnetCalendarEntry;
use bacnet_types::enums::{ErrorClass, ErrorCode};
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
use bytes::BytesMut;
use crate::common;
pub(crate) const MAX_DATE_LIST_ENTRIES: usize = 1024;
pub(super) fn no_space_error() -> Error {
Error::Protocol {
class: ErrorClass::RESOURCES.to_raw() as u32,
code: ErrorCode::NO_SPACE_TO_WRITE_PROPERTY.to_raw() as u32,
}
}
pub(super) fn read(entries: &[BACnetCalendarEntry]) -> PropertyValue {
PropertyValue::List(
entries
.iter()
.map(|entry| {
let mut encoded = BytesMut::new();
encode_calendar_entry(&mut encoded, entry);
PropertyValue::ApplicationData(encoded.to_vec())
})
.collect(),
)
}
pub(super) fn decode_write(value: PropertyValue) -> Result<Vec<BACnetCalendarEntry>, Error> {
let elements = match value {
PropertyValue::List(elements) => elements,
element @ PropertyValue::ApplicationData(_) => vec![element],
_ => return Err(common::invalid_data_type_error()),
};
let mut entries = Vec::new();
for element in elements {
let PropertyValue::ApplicationData(bytes) = element else {
let error = common::invalid_data_type_error();
return Err(common::at_list_element(error, entries.len()));
};
let mut offset = 0;
while offset < bytes.len() {
if entries.len() == MAX_DATE_LIST_ENTRIES {
return Err(common::at_list_element(no_space_error(), entries.len()));
}
let (entry, end) = decode_entry(&bytes, offset)
.map_err(|error| common::at_list_element(error, entries.len()))?;
if !entry.is_valid() {
let error = common::value_out_of_range_error();
return Err(common::at_list_element(error, entries.len()));
}
entries.push(entry);
offset = end;
}
}
Ok(entries)
}
fn decode_entry(bytes: &[u8], offset: usize) -> Result<(BACnetCalendarEntry, usize), Error> {
match tags::decode_tag(bytes, offset) {
Ok((tag, _)) if tag.class == TagClass::Context && tag.number <= 2 => {}
Ok(_) => return Err(common::invalid_data_type_error()),
Err(_) => return Err(common::invalid_data_encoding_error()),
}
decode_calendar_entry(bytes, offset).map_err(|_| common::invalid_data_encoding_error())
}