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