use bacnet_encoding::tags::{self, Tag};
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
pub(crate) type ElementDecoder<T> = fn(&[u8], usize) -> Result<(T, usize), Error>;
pub(crate) fn chunks(value: &PropertyValue) -> Result<Vec<&[u8]>, Error> {
match value {
PropertyValue::ApplicationData(bytes) => Ok(vec![bytes]),
PropertyValue::List(elements) => elements
.iter()
.map(|element| match element {
PropertyValue::ApplicationData(bytes) => Ok(bytes.as_slice()),
_ => Err(super::invalid_data_type_error()),
})
.collect(),
_ => Err(super::invalid_data_type_error()),
}
}
pub(crate) fn decode_elements<T>(
value: &PropertyValue,
starts: fn(&Tag) -> bool,
decode: ElementDecoder<T>,
) -> Result<Vec<T>, Error> {
let mut elements = Vec::new();
for bytes in chunks(value)? {
let mut offset = 0;
while offset < bytes.len() {
let (element, end) = decode_element(bytes, offset, starts, decode)?;
elements.push(element);
offset = end;
}
}
Ok(elements)
}
pub(crate) fn decode_single_element<T>(
value: &PropertyValue,
starts: fn(&Tag) -> bool,
decode: ElementDecoder<T>,
) -> Result<T, Error> {
let bytes = chunks(value)?.concat();
if bytes.is_empty() {
return Err(super::invalid_data_encoding_error());
}
let (element, end) = decode_element(&bytes, 0, starts, decode)?;
if end != bytes.len() {
return Err(super::invalid_data_encoding_error());
}
Ok(element)
}
pub(crate) fn decode_element<T>(
bytes: &[u8],
offset: usize,
starts: fn(&Tag) -> bool,
decode: ElementDecoder<T>,
) -> Result<(T, usize), Error> {
match tags::decode_tag(bytes, offset) {
Ok((tag, _)) if starts(&tag) => {}
Ok(_) => return Err(super::invalid_data_type_error()),
Err(_) => return Err(super::invalid_data_encoding_error()),
}
decode(bytes, offset).map_err(|_| super::invalid_data_encoding_error())
}