use bacnet_types::enums::{AuthorizationMode, PropertyIdentifier as P};
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
use crate::common;
const FIRST_PROPRIETARY_MODE: u32 = 64;
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct Authorization {
policies: u32,
active_policy: u32,
mode: AuthorizationMode,
supported_modes: Vec<AuthorizationMode>,
priority_for_writing: u8,
}
impl Authorization {
pub(super) fn new() -> Self {
Self {
policies: 1,
active_policy: 1,
mode: AuthorizationMode::AUTHORIZE,
supported_modes: vec![AuthorizationMode::AUTHORIZE],
priority_for_writing: 16,
}
}
pub(super) fn read(&self, property: P) -> Option<PropertyValue> {
Some(match property {
P::ACTIVE_AUTHENTICATION_POLICY => PropertyValue::Unsigned(self.active_policy.into()),
P::NUMBER_OF_AUTHENTICATION_POLICIES => PropertyValue::Unsigned(self.policies.into()),
P::AUTHORIZATION_MODE => PropertyValue::Enumerated(self.mode.to_raw()),
P::PRIORITY_FOR_WRITING => PropertyValue::Unsigned(self.priority_for_writing.into()),
_ => return None,
})
}
pub(super) fn write(
&mut self,
property: P,
array_index: Option<u32>,
value: &PropertyValue,
) -> Option<Result<(), Error>> {
if property != P::ACTIVE_AUTHENTICATION_POLICY && property != P::AUTHORIZATION_MODE {
return None;
}
if array_index.is_some() {
return Some(Err(common::property_is_not_an_array_error()));
}
Some(if property == P::ACTIVE_AUTHENTICATION_POLICY {
self.write_active_policy(value)
} else {
self.write_mode(value)
})
}
fn write_active_policy(&mut self, value: &PropertyValue) -> Result<(), Error> {
let PropertyValue::Unsigned(policy) = *value else {
return Err(common::invalid_data_type_error());
};
self.active_policy = u32::try_from(policy)
.ok()
.filter(|policy| (1..=self.policies).contains(policy))
.ok_or_else(common::value_out_of_range_error)?;
Ok(())
}
fn write_mode(&mut self, value: &PropertyValue) -> Result<(), Error> {
let PropertyValue::Enumerated(raw) = *value else {
return Err(common::invalid_data_type_error());
};
let mode = AuthorizationMode::from_raw(raw);
if !self.supported_modes.contains(&mode) {
return Err(common::value_out_of_range_error());
}
self.mode = mode;
Ok(())
}
pub(super) fn set_policies(&mut self, count: u32) -> Result<(), Error> {
if count == 0 || count < self.active_policy {
return Err(common::value_out_of_range_error());
}
self.policies = count;
Ok(())
}
pub(super) fn set_supported_modes(
&mut self,
modes: impl IntoIterator<Item = AuthorizationMode>,
) -> Result<(), Error> {
let mut modes: Vec<_> = modes.into_iter().collect();
if !modes.iter().all(|&mode| in_production(mode))
|| !modes.contains(&AuthorizationMode::AUTHORIZE)
|| !modes.contains(&self.mode)
{
return Err(common::value_out_of_range_error());
}
modes.sort_unstable_by_key(|mode| mode.to_raw());
modes.dedup();
self.supported_modes = modes;
Ok(())
}
pub(super) fn set_priority_for_writing(&mut self, priority: u8) -> Result<(), Error> {
if !(1..=16).contains(&priority) {
return Err(common::value_out_of_range_error());
}
self.priority_for_writing = priority;
Ok(())
}
}
fn in_production(mode: AuthorizationMode) -> bool {
let raw = mode.to_raw();
raw <= AuthorizationMode::NONE.to_raw() || (FIRST_PROPRIETARY_MODE..=65_535).contains(&raw)
}