Skip to main content

bacnet_encoding/constructed/
shed_level.rs

1//! Clause 21 codec for `BACnetShedLevel`, the datatype of the Load Control
2//! object's Requested_Shed_Level, Expected_Shed_Level and Actual_Shed_Level
3//! (Clause 12.28).
4//!
5//! The CHOICE has three alternatives, each a primitive context tag: percent
6//! `[0]` and level `[1]` carry an Unsigned, amount `[2]` a REAL. A value is
7//! that one tag, with no frame around it.
8
9use bacnet_types::constructed::BACnetShedLevel;
10use bacnet_types::error::{DecodeError, DecodingKind, Error};
11use bytes::BytesMut;
12
13use super::tagged::contents;
14use crate::{primitives, tags};
15
16const WHAT: &str = "BACnetShedLevel";
17
18/// Encode one `BACnetShedLevel` CHOICE, appending to `buf`.
19pub fn encode_shed_level(buf: &mut BytesMut, value: &BACnetShedLevel) {
20    match *value {
21        BACnetShedLevel::Percent(percent) => primitives::encode_ctx_unsigned(buf, 0, percent),
22        BACnetShedLevel::Level(level) => primitives::encode_ctx_unsigned(buf, 1, level),
23        BACnetShedLevel::Amount(amount) => primitives::encode_ctx_real(buf, 2, amount),
24    }
25}
26
27/// Decode one `BACnetShedLevel` CHOICE at `offset`.
28///
29/// Returns the value and the offset just past it; a caller holding exactly
30/// one value must check that offset reaches the end of its data. Any tag but
31/// a primitive context `[0]`, `[1]` or `[2]`, an Unsigned of no octets or more
32/// than eight, a REAL that isn't four octets, or truncated contents fails with
33/// [`Error::Decoding`] or [`Error::BufferTooShort`]. The decoder checks
34/// structure only: what range each alternative may take is up to the object.
35pub fn decode_shed_level(data: &[u8], offset: usize) -> Result<(BACnetShedLevel, usize), Error> {
36    let (tag, pos) = tags::decode_tag(data, offset)?;
37    let alternative = (0..=2u8).find(|&number| tag.is_context(number));
38    let Some(alternative) = alternative else {
39        return Err(DecodeError::new(
40            DecodingKind::InvalidEncoding,
41            offset,
42            "expected percent [0], level [1] or amount [2]",
43        )
44        .within(WHAT)
45        .into());
46    };
47    let (contents, end) = contents(data, pos, tag.length)?;
48    let refused = |label: &'static str| {
49        DecodeError::new(DecodingKind::InvalidEncoding, pos, label).within(WHAT)
50    };
51    let unsigned =
52        |label: &'static str| primitives::decode_unsigned(contents).map_err(|_| refused(label));
53    let value = match alternative {
54        0 => BACnetShedLevel::Percent(unsigned("percent [0] needs 1 to 8 octets")?),
55        1 => BACnetShedLevel::Level(unsigned("level [1] needs 1 to 8 octets")?),
56        _ => BACnetShedLevel::Amount(
57            primitives::decode_real(contents).map_err(|_| refused("amount [2] needs 4 octets"))?,
58        ),
59    };
60    Ok((value, end))
61}