use bacnet_types::error::{DecodeError, Error};
use bacnet_types::primitives::{Date, Time};
#[cfg(feature = "alloc")]
use bytes::BytesMut;
use crate::constructed::tagged::{self, misplaced_kind};
use crate::tags::{self, app_tag};
pub const PRIORITY_DATE_TIME_TAG: u8 = 1;
#[cfg(feature = "alloc")]
pub fn encode_priority_date_time(buf: &mut BytesMut, date: &Date, time: &Time) {
tags::encode_opening_tag(buf, PRIORITY_DATE_TIME_TAG);
super::encode_app_date(buf, date);
super::encode_app_time(buf, time);
tags::encode_closing_tag(buf, PRIORITY_DATE_TIME_TAG);
}
pub fn decode_priority_date_time(
data: &[u8],
offset: usize,
) -> Result<((Date, Time), usize), Error> {
const WHAT: &str = "BACnetPriorityValue datetime";
let (tag, after) = tags::decode_tag(data, offset)?;
if !tag.is_opening_tag(PRIORITY_DATE_TIME_TAG) {
return Err(DecodeError::new(
misplaced_kind(data, offset, &tag, Some(PRIORITY_DATE_TIME_TAG)),
offset,
"expected opening tag 1 for a BACnetPriorityValue datetime",
)
.into());
}
let (date, date_end) = tagged::decode_app_fixed(data, after, app_tag::DATE, 4, WHAT)?;
let (time, time_end) = tagged::decode_app_fixed(data, date_end, app_tag::TIME, 4, WHAT)?;
let end = tagged::expect_closing(data, time_end, PRIORITY_DATE_TIME_TAG, WHAT)?;
Ok(((Date::decode(date)?, Time::decode(time)?), end))
}
#[cfg(all(test, feature = "alloc"))]
mod tests {
use super::*;
fn sample() -> (Date, Time) {
(
Date {
year: 126,
month: 10,
day: 25,
day_of_week: 7,
},
Time {
hour: 7,
minute: 30,
second: 15,
hundredths: 0,
},
)
}
#[test]
fn the_datetime_choice_is_wrapped_in_context_tag_one() {
let (date, time) = sample();
let mut buf = BytesMut::new();
encode_priority_date_time(&mut buf, &date, &time);
assert_eq!(
&buf[..],
&[
0x1E, 0xA4, 126, 10, 25, 7, 0xB4, 7, 30, 15, 0, 0x1F, ][..]
);
}
#[test]
fn the_datetime_choice_round_trips() {
let (date, time) = sample();
let mut buf = BytesMut::new();
encode_priority_date_time(&mut buf, &date, &time);
buf.extend_from_slice(&[0x00]); let ((d, t), end) = decode_priority_date_time(&buf, 0).unwrap();
assert_eq!((d, t), (date, time));
assert_eq!(end, 12);
}
#[test]
fn a_bare_date_time_or_other_tag_is_not_the_datetime_choice() {
let (date, time) = sample();
let mut bare = BytesMut::new();
super::super::encode_app_date(&mut bare, &date);
super::super::encode_app_time(&mut bare, &time);
assert!(decode_priority_date_time(&bare, 0).is_err());
assert!(decode_priority_date_time(&[0x0E, 0x0F], 0).is_err());
let mut cut = BytesMut::new();
encode_priority_date_time(&mut cut, &date, &time);
assert!(decode_priority_date_time(&cut[..11], 0).is_err());
assert!(decode_priority_date_time(&cut[..4], 0).is_err());
}
}