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