bacnet-services 0.15.0

BACnet service request/response encode/decode per ASHRAE 135-2020
Documentation
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;

use super::AuditLogQueryAck;
use crate::common::MAX_DECODED_ITEMS;
use bacnet_encoding::constructed::tagged::{
    decode_ctx_boolean, decode_ctx_constructed, decode_ctx_object_id,
};
use bacnet_encoding::constructed::{
    decode_audit_log_record_result_at, encode_audit_log_record_result,
};
use bacnet_encoding::{primitives, tags};
use bacnet_types::error::{DecodeError, DecodingKind, Error, RangeError};
use bytes::BytesMut;

const AUDIT_LOG_TAG: u8 = 0;
const RECORDS_TAG: u8 = 1;
const NO_MORE_ITEMS_TAG: u8 = 2;

pub(super) fn encode(ack: &AuditLogQueryAck, buf: &mut BytesMut) -> Result<(), Error> {
    if ack.records.len() > MAX_DECODED_ITEMS {
        return Err(RangeError::new("AuditLogQuery-ACK record count {} exceeds {}")
            .with_values(ack.records.len() as u64, MAX_DECODED_ITEMS as u64)
            .into());
    }

    let mut encoded = BytesMut::new();
    primitives::encode_ctx_object_id(&mut encoded, AUDIT_LOG_TAG, &ack.audit_log);
    tags::encode_opening_tag(&mut encoded, RECORDS_TAG);
    for result in &ack.records {
        encode_audit_log_record_result(result, &mut encoded)?;
    }
    tags::encode_closing_tag(&mut encoded, RECORDS_TAG);
    primitives::encode_ctx_boolean(&mut encoded, NO_MORE_ITEMS_TAG, ack.no_more_items);
    buf.extend_from_slice(&encoded);
    Ok(())
}

pub(super) fn decode(data: &[u8]) -> Result<AuditLogQueryAck, Error> {
    let (audit_log, mut offset) =
        decode_ctx_object_id(data, 0, AUDIT_LOG_TAG, "AuditLogQuery-ACK audit-log")?;

    let (records_body, records_end) =
        decode_ctx_constructed(data, offset, RECORDS_TAG, "AuditLogQuery-ACK record list")?;
    let mut records = Vec::new();
    let mut record_offset = 0;
    while record_offset < records_body.len() {
        if records.len() >= MAX_DECODED_ITEMS {
            return Err(DecodeError::new(
                DecodingKind::Overflow,
                offset + record_offset,
                "AuditLogQuery-ACK record count exceeds {}",
            )
            .with_value(MAX_DECODED_ITEMS as u64)
            .into());
        }
        let (result, next) = decode_audit_log_record_result_at(records_body, record_offset)?;
        if next <= record_offset {
            return Err(Error::decoding(
                offset + record_offset,
                "AuditLogQuery-ACK record decoder made no progress",
            ));
        }
        records.push(result);
        record_offset = next;
    }
    offset = records_end;

    let (no_more_items, end) = decode_ctx_boolean(
        data,
        offset,
        NO_MORE_ITEMS_TAG,
        "AuditLogQuery-ACK no-more-items",
    )?;
    if end != data.len() {
        return Err(Error::decoding(
            end,
            "AuditLogQuery-ACK has trailing service data",
        ));
    }

    Ok(AuditLogQueryAck {
        audit_log,
        records,
        no_more_items,
    })
}