bacnet_services/object_mgmt/
error.rs1use crate::common::error_type::{
5 decode_element_error, decode_error_pdu, encode_element_error, error_pdu,
6};
7use bacnet_encoding::apdu::ErrorPdu;
8use bacnet_types::enums::{ConfirmedServiceChoice, ErrorClass, ErrorCode};
9use bacnet_types::error::{Error, ErrorDetail};
10use bytes::BytesMut;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct CreateObjectError {
18 pub error_class: ErrorClass,
20 pub error_code: ErrorCode,
22 pub first_failed_element_number: u32,
24}
25
26impl CreateObjectError {
27 pub fn encode(&self, buf: &mut BytesMut) {
29 encode_element_error(
30 buf,
31 self.error_class,
32 self.error_code,
33 self.first_failed_element_number,
34 );
35 }
36
37 pub fn decode(data: &[u8]) -> Result<Self, Error> {
39 let (error_class, error_code, first_failed_element_number) = decode_element_error(
40 data,
41 "CreateObject-Error",
42 "CreateObject-Error first-failed-element-number",
43 )?;
44 Ok(Self {
45 error_class,
46 error_code,
47 first_failed_element_number,
48 })
49 }
50
51 pub fn to_error_pdu(&self, invoke_id: u8) -> ErrorPdu {
53 error_pdu(
54 invoke_id,
55 ConfirmedServiceChoice::CREATE_OBJECT,
56 (self.error_class, self.error_code),
57 |body| self.encode(body),
58 )
59 }
60}
61
62impl TryFrom<&ErrorPdu> for CreateObjectError {
63 type Error = Error;
64
65 fn try_from(pdu: &ErrorPdu) -> Result<Self, Self::Error> {
68 decode_error_pdu(
69 pdu,
70 &[ConfirmedServiceChoice::CREATE_OBJECT],
71 "CreateObject-Error",
72 Self::decode,
73 |error| (error.error_class, error.error_code),
74 )
75 }
76}
77
78impl From<CreateObjectError> for Error {
79 fn from(error: CreateObjectError) -> Self {
80 Error::with_detail(
81 error.error_class,
82 error.error_code,
83 Some(ErrorDetail::FirstFailedElementNumber(
84 error.first_failed_element_number,
85 )),
86 )
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use bytes::Bytes;
94
95 fn sample(first_failed_element_number: u32) -> CreateObjectError {
96 CreateObjectError {
97 error_class: ErrorClass::PROPERTY,
98 error_code: ErrorCode::WRITE_ACCESS_DENIED,
99 first_failed_element_number,
100 }
101 }
102
103 #[test]
104 fn create_object_error_round_trips_with_exact_shape() {
105 for (number, tail) in [
108 (0, &[0x19, 0][..]),
109 (3, &[0x19, 3]),
110 (300, &[0x1A, 1, 44]),
111 (u32::MAX, &[0x1C, 0xFF, 0xFF, 0xFF, 0xFF]),
112 ] {
113 let error = sample(number);
114 let mut body = BytesMut::new();
115 error.encode(&mut body);
116 assert_eq!(&body[..6], &[0x0E, 0x91, 2, 0x91, 40, 0x0F]);
117 assert_eq!(&body[6..], tail);
118 assert_eq!(CreateObjectError::decode(&body).unwrap(), error);
119 }
120 }
121
122 #[test]
123 fn create_object_error_pdu_conversion_and_projection() {
124 let pdu = sample(2).to_error_pdu(9);
125 assert_eq!(pdu.invoke_id, 9);
126 assert_eq!(pdu.service_choice, ConfirmedServiceChoice::CREATE_OBJECT);
127 assert_eq!(CreateObjectError::try_from(&pdu).unwrap(), sample(2));
128 assert!(matches!(
129 Error::from(sample(2)),
130 Error::Structured { class: ErrorClass::PROPERTY, code: ErrorCode::WRITE_ACCESS_DENIED, detail }
131 if *detail == ErrorDetail::FirstFailedElementNumber(2)
132 ));
133 }
134
135 #[test]
136 fn create_object_error_refuses_malformed_plain_and_mismatched() {
137 let mut body = BytesMut::new();
138 sample(2).encode(&mut body);
139 for malformed in [
140 body[..6].to_vec(),
141 [&body[..6], &[0x29, 2][..]].concat(),
142 [&body[..], &[0x19, 2][..]].concat(),
143 [&body[..6], &[0x1D, 5, 1, 0, 0, 0, 0][..]].concat(),
144 ] {
145 assert!(
146 CreateObjectError::decode(&malformed).is_err(),
147 "{malformed:02x?}"
148 );
149 }
150
151 let mut pdu = sample(1).to_error_pdu(1);
152 pdu.error_code = ErrorCode::INVALID_DATA_TYPE;
153 assert!(CreateObjectError::try_from(&pdu).is_err());
154 let mut pdu = sample(1).to_error_pdu(1);
155 pdu.error_data = Bytes::new();
156 assert!(CreateObjectError::try_from(&pdu).is_err());
157 let mut pdu = sample(1).to_error_pdu(1);
158 pdu.service_choice = ConfirmedServiceChoice::ADD_LIST_ELEMENT;
159 assert!(CreateObjectError::try_from(&pdu).is_err());
160 }
161}