Skip to main content

bacnet_encoding/constructed/
access_rule.rs

1//! Clause 21 codec for `BACnetAccessRule`, the element of an Access Rights
2//! object's Positive_Access_Rules and Negative_Access_Rules arrays (Clause
3//! 12.34.9.1).
4//!
5//! A rule is a bare SEQUENCE and a whole array concatenates them with no
6//! wrapper, so the decoder reads one rule at `offset` and returns the offset
7//! just past it, the way the Access Credential element codecs do.
8//!
9//! The decoder checks structure only. It keeps each specifier as received,
10//! a value outside the two named ones included, and doesn't compare a
11//! specifier with the presence of its reference; the Access Rights object
12//! judges both. A specifier wider than 32 bits is malformed.
13
14use bacnet_types::constructed::BACnetAccessRule;
15use bacnet_types::enums::{AccessRuleLocationSpecifier, AccessRuleTimeRangeSpecifier};
16use bacnet_types::error::Error;
17use bytes::BytesMut;
18
19use super::tagged::{
20    decode_ctx_boolean, decode_ctx_unsigned, expect_closing, expect_opening, next_is_opening,
21};
22use super::{
23    decode_device_object_reference, decode_dopr_body, encode_device_object_reference,
24    encode_dopr_body,
25};
26use crate::{primitives, tags};
27
28const RULE: &str = "BACnetAccessRule";
29
30/// Encode one `BACnetAccessRule` SEQUENCE: the time-range specifier as
31/// context `[0]`, the time-range reference (when present) inside opening and
32/// closing tag 1, the location specifier as context `[2]`, the location
33/// reference (when present) inside opening and closing tag 3, and the enable
34/// flag as context `[4]`.
35pub fn encode_access_rule(buf: &mut BytesMut, rule: &BACnetAccessRule) {
36    primitives::encode_ctx_enumerated(buf, 0, rule.time_range_specifier.to_raw());
37    if let Some(time_range) = &rule.time_range {
38        tags::encode_opening_tag(buf, 1);
39        encode_dopr_body(buf, time_range);
40        tags::encode_closing_tag(buf, 1);
41    }
42    primitives::encode_ctx_enumerated(buf, 2, rule.location_specifier.to_raw());
43    if let Some(location) = &rule.location {
44        tags::encode_opening_tag(buf, 3);
45        encode_device_object_reference(buf, location);
46        tags::encode_closing_tag(buf, 3);
47    }
48    primitives::encode_ctx_boolean(buf, 4, rule.enable);
49}
50
51/// Decode one `BACnetAccessRule` SEQUENCE at `offset`.
52///
53/// A missing specifier or enable flag, members out of order, a reference
54/// frame that doesn't close right after one reference, a truncated member
55/// or a BOOLEAN whose contents aren't 0 or 1 fails with [`Error::Decoding`]
56/// or [`Error::BufferTooShort`].
57pub fn decode_access_rule(data: &[u8], offset: usize) -> Result<(BACnetAccessRule, usize), Error> {
58    let (time_range_specifier, mut pos) = decode_ctx_unsigned::<u32>(data, offset, 0, RULE)?;
59    let time_range = if next_is_opening(data, pos, 1)? {
60        let content = expect_opening(data, pos, 1, RULE)?;
61        let (reference, end) = decode_dopr_body(data, content, RULE)?;
62        pos = expect_closing(data, end, 1, RULE)?;
63        Some(reference)
64    } else {
65        None
66    };
67    let (location_specifier, mut pos) = decode_ctx_unsigned::<u32>(data, pos, 2, RULE)?;
68    let location = if next_is_opening(data, pos, 3)? {
69        let content = expect_opening(data, pos, 3, RULE)?;
70        let (reference, end) = decode_device_object_reference(data, content)?;
71        pos = expect_closing(data, end, 3, RULE)?;
72        Some(reference)
73    } else {
74        None
75    };
76    let (enable, end) = decode_ctx_boolean(data, pos, 4, RULE)?;
77    Ok((
78        BACnetAccessRule {
79            time_range_specifier: AccessRuleTimeRangeSpecifier::from_raw(time_range_specifier),
80            time_range,
81            location_specifier: AccessRuleLocationSpecifier::from_raw(location_specifier),
82            location,
83            enable,
84        },
85        end,
86    ))
87}