use super::*;
use bacnet_encoding::constructed::{
decode_cov_multiple_subscription, decode_cov_subscription, decode_destination,
decode_device_object_property_reference, decode_event_notification_subscription,
};
#[derive(Clone, Copy)]
enum FramedElement {
Destination,
DeviceObjectPropertyReference,
CovSubscription,
CovMultipleSubscription,
EventNotificationSubscription,
}
impl FramedElement {
fn for_datatype(object_type: ObjectType, property: PropertyIdentifier) -> Option<Self> {
match property {
PropertyIdentifier::RECIPIENT_LIST
if matches!(
object_type,
ObjectType::NOTIFICATION_CLASS | ObjectType::NOTIFICATION_FORWARDER
) =>
{
Some(Self::Destination)
}
PropertyIdentifier::LIST_OF_OBJECT_PROPERTY_REFERENCES
if matches!(object_type, ObjectType::SCHEDULE | ObjectType::TIMER) =>
{
Some(Self::DeviceObjectPropertyReference)
}
PropertyIdentifier::ACTIVE_COV_SUBSCRIPTIONS if object_type == ObjectType::DEVICE => {
Some(Self::CovSubscription)
}
PropertyIdentifier::ACTIVE_COV_MULTIPLE_SUBSCRIPTIONS
if object_type == ObjectType::DEVICE =>
{
Some(Self::CovMultipleSubscription)
}
PropertyIdentifier::SUBSCRIBED_RECIPIENTS
if object_type == ObjectType::NOTIFICATION_FORWARDER =>
{
Some(Self::EventNotificationSubscription)
}
_ => None,
}
}
fn end(self, data: &[u8], offset: usize) -> Result<usize, Error> {
match self {
Self::Destination => decode_destination(data, offset).map(|(_, end)| end),
Self::DeviceObjectPropertyReference => {
decode_device_object_property_reference(data, offset).map(|(_, end)| end)
}
Self::CovSubscription => decode_cov_subscription(data, offset).map(|(_, end)| end),
Self::CovMultipleSubscription => {
decode_cov_multiple_subscription(data, offset).map(|(_, end)| end)
}
Self::EventNotificationSubscription => {
decode_event_notification_subscription(data, offset).map(|(_, end)| end)
}
}
}
}
fn unsplittable() -> Error {
Error::protocol(
ErrorClass::SERVICES,
ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED,
)
}
pub(super) fn list_items(
object_type: ObjectType,
property: PropertyIdentifier,
value: PropertyValue,
) -> Result<Vec<PropertyValue>, Error> {
let bytes = match value {
PropertyValue::List(items) => return Ok(items),
PropertyValue::ApplicationData(bytes) => bytes,
_ => return Err(unsplittable()),
};
let element = FramedElement::for_datatype(object_type, property).ok_or_else(unsplittable)?;
let mut items = Vec::new();
let mut offset = 0;
while offset < bytes.len() {
let end = element.end(&bytes, offset).map_err(|_| unsplittable())?;
items.push(PropertyValue::ApplicationData(bytes[offset..end].to_vec()));
offset = end;
}
Ok(items)
}