Skip to main content

bacnet_encoding/constructed/
value_source.rs

1//! BACnetValueSource CHOICE framing (ASHRAE 135-2020 Clause 21).
2use bacnet_types::constructed::{BACnetAddress, BACnetValueSource};
3use bacnet_types::error::{DecodeError, DecodingKind, Error};
4use bytes::BytesMut;
5
6use super::recipient::{check_encoded_mac_len, decode_app_mac_address};
7use super::tagged::{decode_app_unsigned, expect_closing};
8use super::{decode_device_object_reference, encode_device_object_reference};
9use crate::{primitives, tags};
10
11const WHAT: &str = "BACnetValueSource";
12
13/// Encode one ValueSource CHOICE, appending to `buf`.
14///
15/// The generic codec preserves legal wire object types, wildcard instances,
16/// local network zero and empty (broadcast) MAC addresses. Source identity and
17/// authorization are mechanism-level concerns, not datatype restrictions.
18///
19/// A MAC longer than [`BACnetAddress::MAX_MAC_LEN`] octets, which the decoder
20/// refuses, returns an error before modifying the output buffer (#1156).
21pub fn encode_value_source(buf: &mut BytesMut, source: &BACnetValueSource) -> Result<(), Error> {
22    if let BACnetValueSource::Address(address) = source {
23        check_encoded_mac_len(&address.mac_address, WHAT)?;
24    }
25    match source {
26        BACnetValueSource::None => tags::encode_tag(buf, 0, tags::TagClass::Context, 0),
27        BACnetValueSource::Object(reference) => {
28            tags::encode_opening_tag(buf, 1);
29            encode_device_object_reference(buf, reference);
30            tags::encode_closing_tag(buf, 1);
31        }
32        BACnetValueSource::Address(address) => {
33            tags::encode_opening_tag(buf, 2);
34            primitives::encode_app_unsigned(buf, u64::from(address.network_number));
35            primitives::encode_app_octet_string(buf, &address.mac_address);
36            tags::encode_closing_tag(buf, 2);
37        }
38    }
39    Ok(())
40}
41
42/// Decode one ValueSource CHOICE at `offset`, returning its value and next offset.
43///
44/// Bytes after that CHOICE are left for the caller, allowing concatenated array
45/// elements. A full-property consumer must check that the returned offset equals
46/// the payload length. Structural acceptance does not establish a valid or
47/// authorized command source. An address MAC longer than
48/// [`BACnetAddress::MAX_MAC_LEN`] octets is refused before it is copied (#1156).
49pub fn decode_value_source(
50    data: &[u8],
51    offset: usize,
52) -> Result<(BACnetValueSource, usize), Error> {
53    let (tag, pos) = tags::decode_tag(data, offset)?;
54    if tag.is_context(0) && tag.length == 0 {
55        return Ok((BACnetValueSource::None, pos));
56    }
57    if tag.is_opening_tag(1) {
58        let (reference, end) = decode_device_object_reference(data, pos)?;
59        let end = expect_closing(data, end, 1, WHAT)?;
60        return Ok((BACnetValueSource::Object(reference), end));
61    }
62    if tag.is_opening_tag(2) {
63        let (network, pos) = decode_app_unsigned::<u64>(data, pos, WHAT)?;
64        let network_number = u16::try_from(network).map_err(|_| {
65            DecodeError::new(
66                DecodingKind::OutOfRange,
67                pos,
68                "network number exceeds Unsigned16",
69            )
70            .within(WHAT)
71        })?;
72        let (mac_address, pos) = decode_app_mac_address(data, pos, WHAT)?;
73        let end = expect_closing(data, pos, 2, WHAT)?;
74        return Ok((
75            BACnetValueSource::Address(BACnetAddress {
76                network_number,
77                mac_address,
78            }),
79            end,
80        ));
81    }
82    Err(DecodeError::new(
83        DecodingKind::InvalidEncoding,
84        offset,
85        "expected NULL [0], object [1], or address [2]",
86    )
87    .within(WHAT)
88    .into())
89}