bacnet_services/private_transfer/
error.rs1#[cfg(not(feature = "std"))]
5use alloc::vec::Vec;
6
7use crate::common::error_type::{
8 decode_error_pdu, decode_error_type, encode_error_type, error_pdu,
9};
10use bacnet_encoding::apdu::ErrorPdu;
11use bacnet_encoding::constructed::tagged::{
12 decode_ctx_constructed, decode_ctx_unsigned, expect_end,
13};
14use bacnet_encoding::{primitives, tags};
15use bacnet_types::enums::{ConfirmedServiceChoice, ErrorClass, ErrorCode};
16use bacnet_types::error::{Error, ErrorDetail};
17use bytes::{BufMut, BytesMut};
18
19const WHAT: &str = "ConfirmedPrivateTransfer-Error";
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct PrivateTransferError {
24 pub error_class: ErrorClass,
26 pub error_code: ErrorCode,
28 pub vendor_id: u32,
30 pub service_number: u32,
32 pub error_parameters: Option<Vec<u8>>,
35}
36
37impl PrivateTransferError {
38 pub fn encode(&self, buf: &mut BytesMut) {
40 encode_error_type(buf, self.error_class, self.error_code);
41 primitives::encode_ctx_unsigned(buf, 1, u64::from(self.vendor_id));
42 primitives::encode_ctx_unsigned(buf, 2, u64::from(self.service_number));
43 if let Some(parameters) = &self.error_parameters {
44 tags::encode_opening_tag(buf, 3);
45 buf.put_slice(parameters);
46 tags::encode_closing_tag(buf, 3);
47 }
48 }
49
50 pub fn decode(data: &[u8]) -> Result<Self, Error> {
52 let ((error_class, error_code), offset) = decode_error_type(data, WHAT)?;
53 let (vendor_id, offset) = decode_ctx_unsigned::<u32>(
54 data,
55 offset,
56 1,
57 "ConfirmedPrivateTransfer-Error vendor-id",
58 )?;
59 let (service_number, mut offset) = decode_ctx_unsigned::<u32>(
60 data,
61 offset,
62 2,
63 "ConfirmedPrivateTransfer-Error service-number",
64 )?;
65 let mut error_parameters = None;
66 if offset < data.len() {
67 let (parameters, end) = decode_ctx_constructed(data, offset, 3, WHAT)?;
68 error_parameters = Some(parameters.to_vec());
69 offset = end;
70 }
71 expect_end(data, offset, offset, WHAT)?;
72 Ok(Self {
73 error_class,
74 error_code,
75 vendor_id,
76 service_number,
77 error_parameters,
78 })
79 }
80
81 pub fn to_error_pdu(&self, invoke_id: u8) -> ErrorPdu {
83 error_pdu(
84 invoke_id,
85 ConfirmedServiceChoice::CONFIRMED_PRIVATE_TRANSFER,
86 (self.error_class, self.error_code),
87 |body| self.encode(body),
88 )
89 }
90}
91
92impl TryFrom<&ErrorPdu> for PrivateTransferError {
93 type Error = Error;
94
95 fn try_from(pdu: &ErrorPdu) -> Result<Self, Self::Error> {
98 decode_error_pdu(
99 pdu,
100 &[ConfirmedServiceChoice::CONFIRMED_PRIVATE_TRANSFER],
101 WHAT,
102 Self::decode,
103 |error| (error.error_class, error.error_code),
104 )
105 }
106}
107
108impl From<PrivateTransferError> for Error {
109 fn from(error: PrivateTransferError) -> Self {
110 Error::with_detail(
111 error.error_class,
112 error.error_code,
113 Some(ErrorDetail::PrivateTransfer {
114 vendor_id: error.vendor_id,
115 service_number: error.service_number,
116 error_parameters: error.error_parameters,
117 }),
118 )
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use bytes::Bytes;
126
127 fn sample(error_parameters: Option<Vec<u8>>) -> PrivateTransferError {
128 PrivateTransferError {
129 error_class: ErrorClass::SERVICES,
130 error_code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED,
131 vendor_id: 555,
132 service_number: 7,
133 error_parameters,
134 }
135 }
136
137 fn encoded(error: &PrivateTransferError) -> Vec<u8> {
138 let mut body = BytesMut::new();
139 error.encode(&mut body);
140 body.to_vec()
141 }
142
143 #[test]
144 fn private_transfer_error_round_trips_with_exact_shape() {
145 let head = [0x0E, 0x91, 5, 0x91, 45, 0x0F, 0x1A, 0x02, 0x2B, 0x29, 7];
148 for (parameters, tail) in [
149 (None, &[][..]),
150 (Some(vec![0x21, 1]), &[0x3E, 0x21, 1, 0x3F]),
151 (Some(Vec::new()), &[0x3E, 0x3F]),
152 (
153 Some(vec![0x3E, 0x21, 1, 0x3F]),
154 &[0x3E, 0x3E, 0x21, 1, 0x3F, 0x3F],
155 ),
156 ] {
157 let error = sample(parameters);
158 let wire = [&head[..], tail].concat();
159 assert_eq!(encoded(&error), wire);
160 assert_eq!(PrivateTransferError::decode(&wire).unwrap(), error);
161 let pdu = error.to_error_pdu(3);
162 assert_eq!(PrivateTransferError::try_from(&pdu).unwrap(), error);
163 }
164 }
165
166 #[test]
167 fn private_transfer_error_projects_vendor_service_and_parameters() {
168 assert!(matches!(
169 Error::from(sample(Some(vec![0x21, 1]))),
170 Error::Structured { class: ErrorClass::SERVICES, code: ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED, detail }
171 if *detail == ErrorDetail::PrivateTransfer {
172 vendor_id: 555,
173 service_number: 7,
174 error_parameters: Some(vec![0x21, 1]),
175 }
176 ));
177 }
178
179 #[test]
180 fn private_transfer_error_refuses_malformed_plain_and_mismatched() {
181 let body = encoded(&sample(Some(vec![0x21, 1])));
182 for malformed in [
183 body[..6].to_vec(),
184 body[..9].to_vec(),
185 body[..body.len() - 1].to_vec(),
186 [&body[..], &[0x00][..]].concat(),
187 [&body[..6], &[0x29, 7, 0x1A, 0x02, 0x2B][..]].concat(),
188 [&body[..11], &[0x39, 1][..]].concat(),
189 ] {
190 assert!(
191 PrivateTransferError::decode(&malformed).is_err(),
192 "{malformed:02x?}"
193 );
194 }
195
196 let mut pdu = sample(None).to_error_pdu(1);
197 pdu.error_class = ErrorClass::DEVICE;
198 assert!(PrivateTransferError::try_from(&pdu).is_err());
199 let mut pdu = sample(None).to_error_pdu(1);
200 pdu.error_data = Bytes::new();
201 assert!(PrivateTransferError::try_from(&pdu).is_err());
202 let mut pdu = sample(None).to_error_pdu(1);
203 pdu.service_choice = ConfirmedServiceChoice::CONFIRMED_TEXT_MESSAGE;
204 assert!(PrivateTransferError::try_from(&pdu).is_err());
205 }
206}