use bacnet_encoding::constructed::decode_access_rule;
use bacnet_encoding::tags::Tag;
use bacnet_types::constructed::BACnetAccessRule;
use bacnet_types::enums::{AccessRuleLocationSpecifier, AccessRuleTimeRangeSpecifier, ObjectType};
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
use super::rights::{check_access_rule, check_rule_count, checked_rules};
use crate::{common, device_reference};
pub(super) fn grown_rule() -> BACnetAccessRule {
BACnetAccessRule {
time_range_specifier: AccessRuleTimeRangeSpecifier::SPECIFIED,
time_range: Some(device_reference::unset_reference(ObjectType::SCHEDULE)),
location_specifier: AccessRuleLocationSpecifier::SPECIFIED,
location: Some(device_reference::unset_identifier(ObjectType::ACCESS_POINT).into()),
enable: false,
}
}
fn starts_rule(tag: &Tag) -> bool {
tag.is_context(0)
}
fn decode_rules(value: PropertyValue) -> Result<Vec<BACnetAccessRule>, Error> {
common::decode_elements(&value, starts_rule, decode_access_rule)
}
pub(super) fn write_rules(
rules: &mut Vec<BACnetAccessRule>,
array_index: Option<u32>,
value: PropertyValue,
) -> Result<(), Error> {
match array_index {
None => *rules = checked_rules(decode_rules(value)?)?,
Some(0) => {
let PropertyValue::Unsigned(size) = value else {
return Err(common::invalid_data_type_error());
};
let size = usize::try_from(size).unwrap_or(usize::MAX);
check_rule_count(size)?;
rules.resize_with(size, grown_rule);
}
Some(index) => {
let slot = usize::try_from(index - 1)
.ok()
.filter(|slot| *slot < rules.len())
.ok_or_else(common::invalid_array_index_error)?;
let mut decoded = decode_rules(value)?;
let rule = match decoded.pop() {
Some(rule) if decoded.is_empty() => rule,
_ => return Err(common::invalid_data_encoding_error()),
};
check_access_rule(&rule)?;
rules[slot] = rule;
}
}
Ok(())
}
#[cfg(test)]
#[path = "rights_writes_tests.rs"]
mod tests;