bacnet-objects 0.13.0

BACnet object model: traits, database, and standard object types
Documentation
use bacnet_types::error::Error;

use super::{take, take_u32, take_u64};
use crate::audit::receipt::{
    self, CompletedAuditReceipt, MAX_AUDIT_RECEIPT_KEY_BYTES, MAX_COMPLETED_AUDIT_RECEIPTS,
};

pub(super) fn encoded_len(receipts: &[CompletedAuditReceipt]) -> Result<usize, Error> {
    receipt::validate_receipts(receipts)?;
    receipts.iter().try_fold(4usize, |length, receipt| {
        length
            .checked_add(8 + 4)
            .and_then(|length| length.checked_add(receipt.key().len()))
            .ok_or_else(|| Error::OutOfRange("AuditLog completed receipt length overflow".into()))
    })
}

pub(super) fn encode(
    receipts: &[CompletedAuditReceipt],
    payload: &mut Vec<u8>,
) -> Result<(), Error> {
    encoded_len(receipts)?;
    payload.extend_from_slice(&(receipts.len() as u32).to_be_bytes());
    for receipt in receipts {
        payload.extend_from_slice(&receipt.completed_at_unix_millis().to_be_bytes());
        payload.extend_from_slice(&(receipt.key().len() as u32).to_be_bytes());
        payload.extend_from_slice(receipt.key());
    }
    Ok(())
}

pub(super) fn decode(
    payload: &[u8],
    offset: &mut usize,
) -> Result<Vec<CompletedAuditReceipt>, Error> {
    let count = take_u32(payload, offset, "completed-receipt-count")? as usize;
    if count > MAX_COMPLETED_AUDIT_RECEIPTS {
        return Err(Error::OutOfRange(format!(
            "AuditLog completed receipt count {count} exceeds {MAX_COMPLETED_AUDIT_RECEIPTS}"
        )));
    }
    let mut receipts = Vec::with_capacity(count);
    for _ in 0..count {
        let completed_at = take_u64(payload, offset, "completed-receipt-timestamp")?;
        let key_len = take_u32(payload, offset, "completed-receipt-key-length")? as usize;
        if key_len > MAX_AUDIT_RECEIPT_KEY_BYTES {
            return Err(Error::OutOfRange(format!(
                "AuditLog completed receipt key length {key_len} exceeds {MAX_AUDIT_RECEIPT_KEY_BYTES}"
            )));
        }
        let key = take(payload, offset, key_len, "completed-receipt-key")?.to_vec();
        receipts.push(CompletedAuditReceipt::new(key, completed_at)?);
    }
    receipt::validate_receipts(&receipts)?;
    Ok(receipts)
}