use bacnet_types::constructed::{AccessResult, BACnetPropertyAccessResult};
use bacnet_types::enums::{ErrorClass, ErrorCode};
use bacnet_types::error::Error;
use bytes::BytesMut;
use super::tagged::{decode_app_enumerated, decode_framed_value, expect_closing};
use super::{decode_dopr_body, encode_dopr_body};
use crate::{primitives, tags};
const WHAT: &str = "BACnetPropertyAccessResult";
pub fn encode_property_access_result(
buf: &mut BytesMut,
result: &BACnetPropertyAccessResult,
) -> Result<(), Error> {
let start = buf.len();
encode_dopr_body(buf, &result.reference);
match &result.access_result {
AccessResult::Value(value) => {
tags::encode_opening_tag(buf, 4);
if let Err(error) = primitives::encode_property_value(buf, value) {
buf.truncate(start);
return Err(error);
}
tags::encode_closing_tag(buf, 4);
}
AccessResult::Error { class, code } => {
tags::encode_opening_tag(buf, 5);
primitives::encode_app_enumerated(buf, u32::from(class.to_raw()));
primitives::encode_app_enumerated(buf, u32::from(code.to_raw()));
tags::encode_closing_tag(buf, 5);
}
}
Ok(())
}
pub fn decode_property_access_result(
data: &[u8],
offset: usize,
) -> Result<(BACnetPropertyAccessResult, usize), Error> {
let (reference, offset) = decode_dopr_body(data, offset, WHAT)?;
let (tag, content) = tags::decode_tag(data, offset)?;
let (access_result, end) = if tag.is_opening_tag(4) {
let (value, end) = decode_framed_value(data, content, 4, WHAT)?;
(AccessResult::Value(value), end)
} else if tag.is_opening_tag(5) {
let (class, next) = decode_app_enumerated::<u32>(data, content, WHAT)?;
let (code, next) = decode_app_enumerated::<u32>(data, next, WHAT)?;
let class = u16::try_from(class).map_err(|_| {
Error::out_of_range(content, format!("{WHAT}: error class exceeds u16"))
})?;
let code = u16::try_from(code)
.map_err(|_| Error::out_of_range(content, format!("{WHAT}: error code exceeds u16")))?;
let end = expect_closing(data, next, 5, WHAT)?;
let error = AccessResult::Error {
class: ErrorClass::from_raw(class),
code: ErrorCode::from_raw(code),
};
(error, end)
} else {
return Err(Error::decoding(
offset,
format!("{WHAT}: expected property-value [4] or property-access-error [5]"),
));
};
Ok((
BACnetPropertyAccessResult {
reference,
access_result,
},
end,
))
}