1use bacnet_encoding::primitives;
7use bacnet_encoding::tags;
8use bacnet_types::enums::ObjectType;
9use bacnet_types::error::Error;
10use bacnet_types::primitives::ObjectIdentifier;
11use bytes::BytesMut;
12
13use crate::common::{BACnetPropertyValue, MAX_DECODED_ITEMS};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum ObjectSpecifier {
22 Type(ObjectType),
24 Identifier(ObjectIdentifier),
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct CreateObjectRequest {
31 pub object_specifier: ObjectSpecifier,
32 pub list_of_initial_values: Vec<BACnetPropertyValue>,
33}
34
35impl CreateObjectRequest {
36 pub fn encode(&self, buf: &mut BytesMut) {
37 tags::encode_opening_tag(buf, 0);
39 match &self.object_specifier {
40 ObjectSpecifier::Type(obj_type) => {
41 primitives::encode_ctx_enumerated(buf, 0, obj_type.to_raw());
42 }
43 ObjectSpecifier::Identifier(oid) => {
44 primitives::encode_ctx_object_id(buf, 1, oid);
45 }
46 }
47 tags::encode_closing_tag(buf, 0);
48
49 if !self.list_of_initial_values.is_empty() {
51 tags::encode_opening_tag(buf, 1);
52 for pv in &self.list_of_initial_values {
53 pv.encode(buf);
54 }
55 tags::encode_closing_tag(buf, 1);
56 }
57 }
58
59 pub fn decode(data: &[u8]) -> Result<Self, Error> {
60 let mut offset = 0;
61
62 let (tag, tag_end) = tags::decode_tag(data, offset)?;
64 if !tag.is_opening_tag(0) {
65 return Err(Error::decoding(
66 offset,
67 "CreateObject expected opening tag 0",
68 ));
69 }
70 offset = tag_end;
71
72 let (tag, pos) = tags::decode_tag(data, offset)?;
73 let end = pos + tag.length as usize;
74 if end > data.len() {
75 return Err(Error::decoding(
76 pos,
77 "CreateObject truncated at object-specifier",
78 ));
79 }
80
81 let object_specifier = if tag.is_context(0) {
82 let raw = primitives::decode_unsigned(&data[pos..end])?;
83 let raw = u32::try_from(raw)
84 .map_err(|_| Error::decoding(pos, "CreateObject object-type exceeds u32"))?;
85 ObjectSpecifier::Type(ObjectType::from_raw(raw))
86 } else if tag.is_context(1) {
87 ObjectSpecifier::Identifier(ObjectIdentifier::decode(&data[pos..end])?)
88 } else {
89 return Err(Error::decoding(
90 offset,
91 "CreateObject expected context tag 0 or 1 inside object-specifier",
92 ));
93 };
94 offset = end;
95
96 let (tag, tag_end) = tags::decode_tag(data, offset)?;
97 if !tag.is_closing_tag(0) {
98 return Err(Error::decoding(
99 offset,
100 "CreateObject expected closing tag 0",
101 ));
102 }
103 offset = tag_end;
104
105 let mut values = Vec::new();
107 if offset < data.len() {
108 let (tag, tag_end) = tags::decode_tag(data, offset)?;
109 if tag.is_opening_tag(1) {
110 offset = tag_end;
111 loop {
112 if offset >= data.len() {
113 return Err(Error::decoding(
114 offset,
115 "CreateObject missing closing tag 1",
116 ));
117 }
118 let (tag, closing_end) = tags::decode_tag(data, offset)?;
119 if tag.is_closing_tag(1) {
120 offset = closing_end;
121 break;
122 }
123 if values.len() >= MAX_DECODED_ITEMS {
124 return Err(Error::decoding(offset, "CreateObject values exceeds max"));
125 }
126 let (pv, new_offset) = BACnetPropertyValue::decode_in_list(data, offset, 1)?;
127 values.push(pv);
128 offset = new_offset;
129 }
130 } else {
131 return Err(Error::decoding(
132 offset,
133 "CreateObject expected opening tag 1",
134 ));
135 }
136 }
137
138 if offset != data.len() {
139 return Err(Error::decoding(offset, "CreateObject has trailing data"));
140 }
141
142 Ok(Self {
143 object_specifier,
144 list_of_initial_values: values,
145 })
146 }
147}
148
149#[derive(Debug, Clone, PartialEq, Eq)]
157pub struct DeleteObjectRequest {
158 pub object_identifier: ObjectIdentifier,
159}
160
161impl DeleteObjectRequest {
162 pub fn encode(&self, buf: &mut BytesMut) {
163 primitives::encode_app_object_id(buf, &self.object_identifier);
164 }
165
166 pub fn decode(data: &[u8]) -> Result<Self, Error> {
167 let (tag, pos) = tags::decode_tag(data, 0)?;
168 let end = pos + tag.length as usize;
169 if end > data.len() {
170 return Err(Error::decoding(pos, "DeleteObject truncated at object-id"));
171 }
172 let object_identifier = ObjectIdentifier::decode(&data[pos..end])?;
173 Ok(Self { object_identifier })
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180 use bacnet_types::enums::{ObjectType, PropertyIdentifier};
181
182 fn create_object_type_bytes(object_type: &[u8]) -> BytesMut {
183 let mut buf = BytesMut::new();
184 tags::encode_opening_tag(&mut buf, 0);
185 primitives::encode_ctx_octet_string(&mut buf, 0, object_type);
186 tags::encode_closing_tag(&mut buf, 0);
187 buf
188 }
189
190 #[test]
191 fn create_object_by_type_round_trip() {
192 let req = CreateObjectRequest {
193 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
194 list_of_initial_values: vec![],
195 };
196 let mut buf = BytesMut::new();
197 req.encode(&mut buf);
198 let decoded = CreateObjectRequest::decode(&buf).unwrap();
199 assert_eq!(req, decoded);
200 }
201
202 #[test]
203 fn create_object_by_id_with_values() {
204 let req = CreateObjectRequest {
205 object_specifier: ObjectSpecifier::Identifier(
206 ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
207 ),
208 list_of_initial_values: vec![BACnetPropertyValue {
209 property_identifier: PropertyIdentifier::OBJECT_NAME,
210 property_array_index: None,
211 value: vec![0x75, 0x06, 0x00, 0x5A, 0x6F, 0x6E, 0x65, 0x31],
212 priority: None,
213 }],
214 };
215 let mut buf = BytesMut::new();
216 req.encode(&mut buf);
217 let decoded = CreateObjectRequest::decode(&buf).unwrap();
218 assert_eq!(req, decoded);
219 }
220
221 #[test]
222 fn create_object_type_must_fit_u32() {
223 let max_with_leading_zero = [0, 0xFF, 0xFF, 0xFF, 0xFF];
224 let decoded =
225 CreateObjectRequest::decode(&create_object_type_bytes(&max_with_leading_zero)).unwrap();
226 assert_eq!(
227 decoded.object_specifier,
228 ObjectSpecifier::Type(ObjectType::from_raw(u32::MAX))
229 );
230
231 for overflow in [u32::MAX as u64 + 1, u64::MAX] {
232 assert!(CreateObjectRequest::decode(&create_object_type_bytes(
233 &overflow.to_be_bytes(),
234 ))
235 .is_err());
236 }
237 }
238
239 #[test]
240 fn create_object_rejects_unexpected_or_trailing_fields() {
241 let encoded = create_object_type_bytes(&[0]);
242
243 let mut empty_values = encoded.clone();
244 tags::encode_opening_tag(&mut empty_values, 1);
245 tags::encode_closing_tag(&mut empty_values, 1);
246 assert!(CreateObjectRequest::decode(&empty_values).is_ok());
247
248 let mut unexpected = encoded.clone();
249 primitives::encode_ctx_unsigned(&mut unexpected, 1, 1);
250 assert!(CreateObjectRequest::decode(&unexpected).is_err());
251
252 let mut trailing = empty_values;
253 primitives::encode_app_null(&mut trailing);
254 assert!(CreateObjectRequest::decode(&trailing).is_err());
255 }
256
257 #[test]
258 fn create_object_accepts_exact_initial_value_limit() {
259 let value = BACnetPropertyValue {
260 property_identifier: PropertyIdentifier::PRESENT_VALUE,
261 property_array_index: None,
262 value: vec![0],
263 priority: None,
264 };
265 let request = CreateObjectRequest {
266 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
267 list_of_initial_values: vec![value.clone(); MAX_DECODED_ITEMS],
268 };
269 let mut encoded = BytesMut::new();
270 request.encode(&mut encoded);
271 assert_eq!(
272 CreateObjectRequest::decode(&encoded)
273 .unwrap()
274 .list_of_initial_values
275 .len(),
276 MAX_DECODED_ITEMS
277 );
278
279 let request = CreateObjectRequest {
280 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
281 list_of_initial_values: vec![value; MAX_DECODED_ITEMS + 1],
282 };
283 encoded.clear();
284 request.encode(&mut encoded);
285 assert!(CreateObjectRequest::decode(&encoded).is_err());
286 }
287
288 #[test]
289 fn delete_object_round_trip() {
290 let req = DeleteObjectRequest {
291 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
292 };
293 let mut buf = BytesMut::new();
294 req.encode(&mut buf);
295 let decoded = DeleteObjectRequest::decode(&buf).unwrap();
296 assert_eq!(req, decoded);
297 }
298
299 #[test]
304 fn test_decode_create_object_empty_input() {
305 assert!(CreateObjectRequest::decode(&[]).is_err());
306 }
307
308 #[test]
309 fn test_decode_create_object_truncated_1_byte() {
310 let req = CreateObjectRequest {
311 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
312 list_of_initial_values: vec![],
313 };
314 let mut buf = BytesMut::new();
315 req.encode(&mut buf);
316 assert!(CreateObjectRequest::decode(&buf[..1]).is_err());
317 }
318
319 #[test]
320 fn test_decode_create_object_truncated_2_bytes() {
321 let req = CreateObjectRequest {
322 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
323 list_of_initial_values: vec![],
324 };
325 let mut buf = BytesMut::new();
326 req.encode(&mut buf);
327 assert!(CreateObjectRequest::decode(&buf[..2]).is_err());
328 }
329
330 #[test]
331 fn test_decode_create_object_truncated_3_bytes() {
332 let req = CreateObjectRequest {
333 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
334 list_of_initial_values: vec![],
335 };
336 let mut buf = BytesMut::new();
337 req.encode(&mut buf);
338 if buf.len() > 3 {
339 assert!(CreateObjectRequest::decode(&buf[..3]).is_err());
340 }
341 }
342
343 #[test]
344 fn test_decode_create_object_invalid_tag() {
345 assert!(CreateObjectRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
347 }
348
349 #[test]
350 fn test_decode_create_object_missing_closing_tag() {
351 assert!(CreateObjectRequest::decode(&[0x0E, 0x09, 0x00]).is_err());
353 }
354
355 #[test]
356 fn test_decode_delete_object_empty_input() {
357 assert!(DeleteObjectRequest::decode(&[]).is_err());
358 }
359
360 #[test]
361 fn test_decode_delete_object_truncated_1_byte() {
362 let req = DeleteObjectRequest {
363 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
364 };
365 let mut buf = BytesMut::new();
366 req.encode(&mut buf);
367 assert!(DeleteObjectRequest::decode(&buf[..1]).is_err());
368 }
369
370 #[test]
371 fn test_decode_delete_object_truncated_2_bytes() {
372 let req = DeleteObjectRequest {
373 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
374 };
375 let mut buf = BytesMut::new();
376 req.encode(&mut buf);
377 assert!(DeleteObjectRequest::decode(&buf[..2]).is_err());
378 }
379
380 #[test]
381 fn test_decode_delete_object_invalid_tag() {
382 assert!(DeleteObjectRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
383 }
384}