#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use bacnet_types::error::{Error, ReadRangeViolation};
use super::{RangeSpec, ReadRangeAck, ReadRangeRequest};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ReadRangeValidation {
#[default]
Strict,
Lenient,
}
#[derive(Debug, Clone)]
pub struct ReadRangeReply {
pub ack: ReadRangeAck,
pub violations: Vec<ReadRangeViolation>,
}
impl ReadRangeReply {
pub fn check(
request: &ReadRangeRequest,
ack: ReadRangeAck,
validation: ReadRangeValidation,
) -> Result<Self, Error> {
let violations = ack.violations(request);
match (validation, violations.first()) {
(ReadRangeValidation::Strict, Some(&violation)) => {
Err(Error::ReadRangeViolation(violation))
}
_ => Ok(Self { ack, violations }),
}
}
}
impl ReadRangeAck {
pub fn violations(&self, request: &ReadRangeRequest) -> Vec<ReadRangeViolation> {
let mut found = Vec::new();
if self.object_identifier != request.object_identifier {
found.push(ReadRangeViolation::ObjectMismatch);
}
if self.property_identifier != request.property_identifier {
found.push(ReadRangeViolation::PropertyMismatch);
}
if self.property_array_index != request.property_array_index {
found.push(ReadRangeViolation::ArrayIndexMismatch);
}
let (sequenced, count) = match &request.range {
None => (false, None),
Some(RangeSpec::ByPosition { count, .. }) => (false, Some(*count)),
Some(RangeSpec::BySequenceNumber { count, .. } | RangeSpec::ByTime { count, .. }) => {
(true, Some(*count))
}
};
match (sequenced, self.item_count, self.first_sequence_number) {
(true, 1.., None) => found.push(ReadRangeViolation::MissingFirstSequenceNumber),
(true, 1.., Some(0)) => found.push(ReadRangeViolation::ZeroFirstSequenceNumber),
(true, 1.., Some(_)) | (_, _, None) => {}
(_, _, Some(_)) => found.push(ReadRangeViolation::UnexpectedFirstSequenceNumber),
}
let (first_item, last_item, more_items) = self.result_flags;
let past_end = match count {
Some(count) if count < 0 => first_item,
Some(_) => last_item,
None => first_item && last_item,
};
if more_items && past_end {
found.push(ReadRangeViolation::MoreItemsPastEnd);
}
if count.is_some_and(|count| u64::from(self.item_count) > u64::from(count.unsigned_abs())) {
found.push(ReadRangeViolation::ItemCountExceedsRequest);
}
found
}
}
#[cfg(test)]
#[path = "read_range_checks_tests.rs"]
mod tests;