bacnet_emb/application_protocol/services/
read_property_multiple.rs

1use core::fmt::Display;
2
3use crate::{
4    application_protocol::{
5        confirmed::{ComplexAck, ComplexAckService, ConfirmedServiceChoice},
6        primitives::data_value::ApplicationDataValue,
7    },
8    common::{
9        daily_schedule::WeeklySchedule,
10        error::Error,
11        helper::{
12            decode_context_object_id, decode_context_property_id, decode_unsigned,
13            encode_closing_tag, encode_context_enumerated, encode_context_object_id,
14            encode_context_unsigned, encode_opening_tag, get_tagged_body, get_tagged_body_for_tag,
15        },
16        io::{Reader, Writer},
17        object_id::{ObjectId, ObjectType},
18        property_id::PropertyId,
19        spec::{ErrorClass, ErrorCode, BACNET_ARRAY_ALL},
20        tag::{ApplicationTagNumber, Tag, TagNumber},
21    },
22    network_protocol::data_link::DataLink,
23};
24
25#[cfg(feature = "alloc")]
26use {
27    crate::common::spooky::Phantom,
28    alloc::{string::String, vec::Vec},
29};
30
31#[cfg(not(feature = "alloc"))]
32#[derive(Debug, Clone)]
33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
34pub struct ReadPropertyMultipleAck<'a> {
35    pub objects_with_results: &'a [ObjectWithResults<'a>],
36    buf: &'a [u8],
37}
38
39#[cfg(feature = "alloc")]
40#[derive(Debug, Clone)]
41#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub struct ReadPropertyMultipleAck<'a> {
43    pub objects_with_results: Vec<ObjectWithResults<'a>>,
44}
45
46#[cfg(not(feature = "alloc"))]
47impl<'a> IntoIterator for &'_ ReadPropertyMultipleAck<'a> {
48    type Item = Result<ObjectWithResults<'a>, Error>;
49
50    type IntoIter = ObjectWithResultsIter<'a>;
51
52    fn into_iter(self) -> Self::IntoIter {
53        ObjectWithResultsIter {
54            buf: self.buf,
55            reader: Reader::new_with_len(self.buf.len()),
56        }
57    }
58}
59
60impl<'a> TryFrom<DataLink<'a>> for ReadPropertyMultipleAck<'a> {
61    type Error = Error;
62
63    fn try_from(value: DataLink<'a>) -> Result<Self, Self::Error> {
64        let ack: ComplexAck = value.try_into()?;
65        match ack.service {
66            ComplexAckService::ReadPropertyMultiple(ack) => Ok(ack),
67            _ => Err(Error::ConvertDataLink(
68                "apdu message is not a ComplexAckService ReadPropertyMultipleAck",
69            )),
70        }
71    }
72}
73
74#[cfg(not(feature = "alloc"))]
75#[derive(Debug, Clone)]
76#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77pub struct ObjectWithResults<'a> {
78    pub object_id: ObjectId,
79    pub property_results: PropertyResultList<'a>,
80}
81
82#[cfg(feature = "alloc")]
83#[derive(Debug, Clone)]
84#[cfg_attr(feature = "defmt", derive(defmt::Format))]
85pub struct ObjectWithResults<'a> {
86    pub object_id: ObjectId,
87    pub property_results: Vec<PropertyResult<'a>>,
88}
89
90impl<'a> ObjectWithResults<'a> {
91    #[cfg(feature = "alloc")]
92    pub fn new(object_id: ObjectId, property_results: Vec<PropertyResult<'a>>) -> Self {
93        Self {
94            object_id,
95            property_results,
96        }
97    }
98
99    #[cfg(not(feature = "alloc"))]
100    pub fn encode(&self, writer: &mut Writer) {
101        encode_context_object_id(writer, 0, &self.object_id);
102        encode_opening_tag(writer, 1);
103        self.property_results.encode(writer);
104        encode_closing_tag(writer, 1);
105    }
106
107    #[cfg(feature = "alloc")]
108    pub fn encode(&self, writer: &mut Writer) {
109        encode_context_object_id(writer, 0, &self.object_id);
110        encode_opening_tag(writer, 1);
111        for item in self.property_results.iter() {
112            item.encode(writer);
113        }
114        encode_closing_tag(writer, 1);
115    }
116
117    #[cfg(not(feature = "alloc"))]
118    pub fn decode(reader: &mut Reader, buf: &'a [u8]) -> Result<Self, Error> {
119        let object_id =
120            decode_context_object_id(reader, buf, 0, "ObjectWithResults decode object_id")?;
121        let buf =
122            get_tagged_body_for_tag(reader, buf, 1, "ObjectWithResults decode list of results")?;
123
124        let property_results = PropertyResultList {
125            object_id,
126            buf,
127            property_results: &[],
128        };
129
130        Ok(ObjectWithResults {
131            object_id,
132            property_results,
133        })
134    }
135
136    #[cfg(feature = "alloc")]
137    pub fn decode(reader: &mut Reader, buf: &[u8]) -> Result<Self, Error> {
138        let object_id =
139            decode_context_object_id(reader, buf, 0, "ObjectWithResults decode object_id")?;
140        let inner_buf =
141            get_tagged_body_for_tag(reader, buf, 1, "ObjectWithResults decode list of results")?;
142        let mut inner_reader = Reader::new_with_len(inner_buf.len());
143
144        let mut property_results = Vec::new();
145        while !inner_reader.eof() {
146            let property_result = PropertyResult::decode(&mut inner_reader, inner_buf, &object_id)?;
147            property_results.push(property_result);
148        }
149
150        Ok(Self::new(object_id, property_results))
151    }
152}
153
154impl<'a> IntoIterator for &'_ PropertyResultList<'a> {
155    type Item = Result<PropertyResult<'a>, Error>;
156    type IntoIter = PropertyResultIter<'a>;
157
158    fn into_iter(self) -> Self::IntoIter {
159        PropertyResultIter {
160            buf: self.buf,
161            reader: Reader::new_with_len(self.buf.len()),
162            object_id: self.object_id,
163        }
164    }
165}
166
167impl<'a> Iterator for PropertyResultIter<'a> {
168    type Item = Result<PropertyResult<'a>, Error>;
169
170    fn next(&mut self) -> Option<Self::Item> {
171        if self.reader.eof() {
172            return None;
173        }
174
175        Some(PropertyResult::decode(
176            &mut self.reader,
177            self.buf,
178            &self.object_id,
179        ))
180    }
181}
182
183fn read_error(reader: &mut Reader, buf: &[u8]) -> Result<PropertyAccessError, Error> {
184    // error class enumerated
185    let tag = Tag::decode_expected(
186        reader,
187        buf,
188        TagNumber::Application(ApplicationTagNumber::Enumerated),
189        "read_error error_class",
190    )?;
191    let value = decode_unsigned(tag.value, reader, buf)? as u32;
192    let error_class = value
193        .try_into()
194        .map_err(|x| Error::InvalidVariant(("ErrorClass", x)))?;
195
196    // error code enumerated
197    let tag = Tag::decode_expected(
198        reader,
199        buf,
200        TagNumber::Application(ApplicationTagNumber::Enumerated),
201        "read_error error code",
202    )?;
203    let value = decode_unsigned(tag.value, reader, buf)? as u32;
204    let error_code = value
205        .try_into()
206        .map_err(|x| Error::InvalidVariant(("ErrorCode", x)))?;
207
208    Ok(PropertyAccessError {
209        error_class,
210        error_code,
211    })
212}
213
214#[derive(Debug, Clone)]
215#[cfg_attr(feature = "defmt", derive(defmt::Format))]
216pub struct PropertyResultList<'a> {
217    pub property_results: &'a [PropertyResult<'a>],
218    object_id: ObjectId,
219    buf: &'a [u8],
220}
221
222#[derive(Debug, Clone)]
223#[cfg_attr(feature = "defmt", derive(defmt::Format))]
224pub struct PropertyResultIter<'a> {
225    object_id: ObjectId,
226    reader: Reader,
227    buf: &'a [u8],
228}
229
230impl<'a> PropertyResultList<'a> {
231    pub fn new(property_results: &'a [PropertyResult<'a>]) -> Self {
232        Self {
233            property_results,
234            object_id: ObjectId::new(ObjectType::Invalid, 0),
235            buf: &[],
236        }
237    }
238
239    pub fn encode(&self, writer: &mut Writer) {
240        for item in self.property_results {
241            item.encode(writer);
242        }
243    }
244}
245
246#[derive(Debug, Clone)]
247#[cfg_attr(feature = "defmt", derive(defmt::Format))]
248pub struct PropertyResult<'a> {
249    pub id: PropertyId,
250    pub value: PropertyValue<'a>,
251}
252
253impl<'a> PropertyResult<'a> {
254    const PROPERTY_ID_TAG: u8 = 2;
255    const PROPERTY_VALUE_TAG: u8 = 4;
256    const PROPERTY_VALUE_ERROR_TAG: u8 = 5;
257
258    pub fn encode(&self, writer: &mut Writer) {
259        encode_context_unsigned(writer, Self::PROPERTY_ID_TAG, self.id as u32);
260        match &self.value {
261            PropertyValue::PropValue(val) => {
262                encode_opening_tag(writer, Self::PROPERTY_VALUE_TAG);
263                val.encode(writer);
264                encode_closing_tag(writer, Self::PROPERTY_VALUE_TAG);
265            }
266            PropertyValue::PropError(_) => todo!(),
267            PropertyValue::PropObjectName(_) => todo!(),
268            PropertyValue::PropDescription(_) => todo!(),
269        }
270    }
271
272    #[cfg_attr(feature = "alloc", bacnet_macros::remove_lifetimes_from_fn_args)]
273    pub fn decode(reader: &mut Reader, buf: &'a [u8], object_id: &ObjectId) -> Result<Self, Error> {
274        let property_id = decode_context_property_id(
275            reader,
276            buf,
277            Self::PROPERTY_ID_TAG,
278            "PropertyResultList next property_id",
279        )?;
280
281        let (inner_buf, tag_number) = get_tagged_body(reader, buf)?;
282        let mut inner_reader = Reader {
283            index: 0,
284            end: inner_buf.len(),
285        };
286
287        let property_value = Self::decode_property_value(
288            &mut inner_reader,
289            inner_buf,
290            tag_number,
291            &property_id,
292            object_id,
293        )?;
294
295        Ok(PropertyResult {
296            id: property_id,
297            value: property_value,
298        })
299    }
300
301    #[cfg_attr(feature = "alloc", bacnet_macros::remove_lifetimes_from_fn_args)]
302    fn decode_property_value(
303        reader: &mut Reader,
304        buf: &'a [u8],
305        tag_number: u8,
306        property_id: &PropertyId,
307        object_id: &ObjectId,
308    ) -> Result<PropertyValue<'a>, Error> {
309        if tag_number == Self::PROPERTY_VALUE_TAG {
310            match property_id {
311                PropertyId::PropEventTimeStamps => {
312                    // ignore for now
313                    Ok(PropertyValue::PropValue(ApplicationDataValue::Boolean(
314                        false,
315                    )))
316                }
317                PropertyId::PropWeeklySchedule => {
318                    let weekly_schedule = WeeklySchedule::decode(reader, buf)?;
319                    Ok(PropertyValue::PropValue(
320                        ApplicationDataValue::WeeklySchedule(weekly_schedule),
321                    ))
322                }
323                property_id => {
324                    let tag = Tag::decode(reader, buf)?;
325                    let value =
326                        ApplicationDataValue::decode(&tag, object_id, property_id, reader, buf)?;
327                    Ok(PropertyValue::PropValue(value))
328                }
329            }
330        } else if tag_number == Self::PROPERTY_VALUE_ERROR_TAG {
331            // property read error
332            let error = read_error(reader, buf)?;
333            Ok(PropertyValue::PropError(error))
334        } else {
335            Err(Error::TagNotSupported((
336                "PropertyResultList next",
337                TagNumber::ContextSpecificOpening(tag_number),
338            )))
339        }
340    }
341}
342
343#[cfg(not(feature = "alloc"))]
344#[derive(Debug, Clone)]
345#[cfg_attr(feature = "defmt", derive(defmt::Format))]
346pub enum PropertyValue<'a> {
347    PropValue(ApplicationDataValue<'a>),
348    PropError(PropertyAccessError),
349    // TODO: figure out if we need these
350    PropDescription(&'a str),
351    PropObjectName(&'a str),
352}
353
354#[cfg(feature = "alloc")]
355#[derive(Debug, Clone)]
356#[cfg_attr(feature = "defmt", derive(defmt::Format))]
357pub enum PropertyValue<'a> {
358    PropValue(ApplicationDataValue<'a>),
359    PropError(PropertyAccessError),
360    // TODO: figure out if we need these
361    PropDescription(String),
362    PropObjectName(String),
363}
364
365#[derive(Debug, Clone)]
366#[cfg_attr(feature = "defmt", derive(defmt::Format))]
367pub struct PropertyAccessError {
368    pub error_class: ErrorClass,
369    pub error_code: ErrorCode,
370}
371
372impl<'a> Display for PropertyValue<'a> {
373    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
374        match &self {
375            Self::PropValue(x) => write!(f, "{}", x),
376            _ => write!(f, "property value unprintable",),
377        }
378    }
379}
380
381impl<'a> ReadPropertyMultipleAck<'a> {
382    #[cfg(not(feature = "alloc"))]
383    pub fn new(objects_with_results: &'a [ObjectWithResults<'a>]) -> Self {
384        Self {
385            objects_with_results,
386            buf: &[],
387        }
388    }
389
390    #[cfg(not(feature = "alloc"))]
391    pub fn new_from_buf(buf: &'a [u8]) -> Self {
392        Self {
393            buf,
394            objects_with_results: &[],
395        }
396    }
397
398    #[cfg(feature = "alloc")]
399    pub fn new(objects_with_results: Vec<ObjectWithResults<'a>>) -> Self {
400        Self {
401            objects_with_results,
402        }
403    }
404
405    pub fn encode(&self, writer: &mut Writer) {
406        writer.push(ConfirmedServiceChoice::ReadPropMultiple as u8);
407        for item in self.objects_with_results.iter() {
408            item.encode(writer);
409        }
410    }
411
412    #[cfg(feature = "alloc")]
413    pub fn decode(reader: &mut Reader, buf: &[u8]) -> Result<Self, Error> {
414        let mut objects_with_results = Vec::new();
415
416        while !reader.eof() {
417            let object_with_results = ObjectWithResults::decode(reader, buf)?;
418            objects_with_results.push(object_with_results);
419        }
420
421        Ok(Self::new(objects_with_results))
422    }
423
424    #[cfg(not(feature = "alloc"))]
425    pub fn decode(reader: &mut Reader, buf: &'a [u8]) -> Result<Self, Error> {
426        let buf = &buf[reader.index..reader.end];
427        Ok(Self {
428            buf,
429            objects_with_results: &[],
430        })
431    }
432}
433
434pub struct ObjectWithResultsIter<'a> {
435    buf: &'a [u8],
436    reader: Reader,
437}
438
439impl<'a> Iterator for ObjectWithResultsIter<'a> {
440    type Item = Result<ObjectWithResults<'a>, Error>;
441
442    fn next(&mut self) -> Option<Self::Item> {
443        if self.reader.eof() {
444            return None;
445        }
446
447        let object_with_results = ObjectWithResults::decode(&mut self.reader, self.buf);
448        Some(object_with_results)
449    }
450}
451
452#[cfg(not(feature = "alloc"))]
453#[derive(Debug, Clone)]
454#[cfg_attr(feature = "defmt", derive(defmt::Format))]
455pub struct ReadPropertyMultiple<'a> {
456    _array_index: u32, // use BACNET_ARRAY_ALL for all
457    objects: &'a [ReadPropertyMultipleObject<'a>],
458    buf: &'a [u8],
459}
460
461#[cfg(feature = "alloc")]
462#[derive(Debug, Clone)]
463#[cfg_attr(feature = "defmt", derive(defmt::Format))]
464pub struct ReadPropertyMultiple<'a> {
465    _array_index: u32, // use BACNET_ARRAY_ALL for all
466    pub objects: Vec<ReadPropertyMultipleObject<'a>>,
467}
468
469#[derive(Debug, Clone)]
470#[cfg_attr(feature = "defmt", derive(defmt::Format))]
471pub struct PropertyIdList<'a> {
472    pub property_ids: &'a [PropertyId],
473    buf: &'a [u8],
474}
475
476impl<'a> IntoIterator for &'_ PropertyIdList<'a> {
477    type Item = Result<PropertyId, Error>;
478    type IntoIter = PropertyIdIter<'a>;
479
480    fn into_iter(self) -> Self::IntoIter {
481        PropertyIdIter {
482            buf: self.buf,
483            reader: Reader::new_with_len(self.buf.len()),
484        }
485    }
486}
487
488pub struct PropertyIdIter<'a> {
489    reader: Reader,
490    buf: &'a [u8],
491}
492
493impl<'a> Iterator for PropertyIdIter<'a> {
494    type Item = Result<PropertyId, Error>;
495
496    fn next(&mut self) -> Option<Self::Item> {
497        if self.reader.eof() {
498            None
499        } else {
500            match decode_context_property_id(
501                &mut self.reader,
502                self.buf,
503                0,
504                "PropertyIdList next property_id",
505            ) {
506                Ok(property_id) => Some(Ok(property_id)),
507                Err(e) => Some(Err(e)),
508            }
509        }
510    }
511}
512
513impl<'a> PropertyIdList<'a> {
514    pub fn new(property_ids: &'a [PropertyId]) -> Self {
515        Self {
516            property_ids,
517            buf: &[],
518        }
519    }
520
521    pub fn encode(&self, writer: &mut Writer) {
522        encode_opening_tag(writer, 1);
523
524        for property_id in self.property_ids {
525            // property_id
526            encode_context_enumerated(writer, 0, property_id);
527
528            // array_index
529            //if self.array_index != BACNET_ARRAY_ALL {
530            //    encode_context_unsigned(writer, 1, self.array_index);
531            //}
532        }
533
534        encode_closing_tag(writer, 1);
535    }
536}
537
538#[cfg(not(feature = "alloc"))]
539#[derive(Debug, Clone)]
540#[cfg_attr(feature = "defmt", derive(defmt::Format))]
541pub struct ReadPropertyMultipleObject<'a> {
542    pub object_id: ObjectId, // e.g ObjectDevice:20088
543    pub property_ids: PropertyIdList<'a>,
544}
545
546#[cfg(feature = "alloc")]
547#[derive(Debug, Clone)]
548#[cfg_attr(feature = "defmt", derive(defmt::Format))]
549pub struct ReadPropertyMultipleObject<'a> {
550    pub object_id: ObjectId, // e.g ObjectDevice:20088
551    pub property_ids: Vec<PropertyId>,
552    pub _phantom: &'a Phantom,
553}
554
555impl<'a> ReadPropertyMultipleObject<'a> {
556    #[cfg(not(feature = "alloc"))]
557    pub fn new(object_id: ObjectId, property_ids: &'a [PropertyId]) -> Self {
558        let property_ids = PropertyIdList::new(property_ids);
559        Self {
560            object_id,
561            property_ids,
562        }
563    }
564
565    #[cfg(feature = "alloc")]
566    pub fn new(object_id: ObjectId, property_ids: Vec<PropertyId>) -> Self {
567        use crate::common::spooky::PHANTOM;
568
569        Self {
570            object_id,
571            property_ids,
572            _phantom: &PHANTOM,
573        }
574    }
575
576    #[cfg(feature = "alloc")]
577    pub fn encode(&self, writer: &mut Writer) {
578        // object_id
579        encode_context_object_id(writer, 0, &self.object_id);
580
581        encode_opening_tag(writer, 1);
582
583        for property_id in self.property_ids.iter() {
584            // property_id
585            encode_context_enumerated(writer, 0, property_id);
586
587            // array_index
588            //if self.array_index != BACNET_ARRAY_ALL {
589            //    encode_context_unsigned(writer, 1, self.array_index);
590            //}
591        }
592
593        encode_closing_tag(writer, 1);
594    }
595
596    #[cfg(not(feature = "alloc"))]
597    pub fn encode(&self, writer: &mut Writer) {
598        // object_id
599        encode_context_object_id(writer, 0, &self.object_id);
600
601        encode_opening_tag(writer, 1);
602
603        for property_id in self.property_ids.property_ids {
604            // property_id
605            encode_context_enumerated(writer, 0, property_id);
606
607            // array_index
608            //if self.array_index != BACNET_ARRAY_ALL {
609            //    encode_context_unsigned(writer, 1, self.array_index);
610            //}
611        }
612
613        encode_closing_tag(writer, 1);
614    }
615
616    #[cfg(not(feature = "alloc"))]
617    pub fn decode(reader: &mut Reader, buf: &'a [u8]) -> Result<Self, Error> {
618        let object_id =
619            decode_context_object_id(reader, buf, 0, "ReadPropertyMultiple next object_id")?;
620
621        let buf =
622            get_tagged_body_for_tag(reader, buf, 1, "ReadPropertyMultiple next list of results")?;
623        let property_ids = PropertyIdList {
624            property_ids: &[],
625            buf,
626        };
627
628        Ok(ReadPropertyMultipleObject {
629            object_id,
630            property_ids,
631        })
632    }
633
634    #[cfg(feature = "alloc")]
635    pub fn decode(reader: &mut Reader, buf: &[u8]) -> Result<Self, Error> {
636        let object_id =
637            decode_context_object_id(reader, buf, 0, "ReadPropertyMultiple next object_id")?;
638
639        let body_buf =
640            get_tagged_body_for_tag(reader, buf, 1, "ReadPropertyMultiple next list of results")?;
641        let mut property_ids = Vec::new();
642        let mut inner_reader = Reader::new_with_len(body_buf.len());
643
644        while !inner_reader.eof() {
645            let property_id = decode_context_property_id(
646                &mut inner_reader,
647                body_buf,
648                0,
649                "ReadPropertyMultipleObject decode property_id",
650            )?;
651            property_ids.push(property_id);
652        }
653
654        Ok(ReadPropertyMultipleObject::new(object_id, property_ids))
655    }
656}
657
658impl<'a> ReadPropertyMultiple<'a> {
659    #[cfg(not(feature = "alloc"))]
660    pub fn new(objects: &'a [ReadPropertyMultipleObject]) -> Self {
661        Self {
662            objects,
663            _array_index: BACNET_ARRAY_ALL,
664            buf: &[],
665        }
666    }
667
668    #[cfg(not(feature = "alloc"))]
669    pub fn new_from_buf(buf: &'a [u8]) -> Self {
670        Self {
671            objects: &[],
672            _array_index: BACNET_ARRAY_ALL,
673            buf,
674        }
675    }
676
677    #[cfg(feature = "alloc")]
678    pub fn new(objects: Vec<ReadPropertyMultipleObject<'a>>) -> Self {
679        Self {
680            objects,
681            _array_index: BACNET_ARRAY_ALL,
682        }
683    }
684
685    pub fn encode(&self, writer: &mut Writer) {
686        for object in self.objects.iter() {
687            object.encode(writer)
688        }
689    }
690
691    #[cfg(not(feature = "alloc"))]
692    pub fn decode(reader: &mut Reader, buf: &'a [u8]) -> Result<Self, Error> {
693        let buf = &buf[reader.index..reader.end];
694        Ok(Self {
695            buf,
696            _array_index: BACNET_ARRAY_ALL,
697            objects: &[],
698        })
699    }
700
701    #[cfg(feature = "alloc")]
702    pub fn decode(reader: &mut Reader, buf: &[u8]) -> Result<Self, Error> {
703        let inner_buf = &buf[reader.index..reader.end];
704        let mut inner_reader = Reader::new_with_len(inner_buf.len());
705        let mut objects = Vec::new();
706
707        while !inner_reader.eof() {
708            let object_with_property_ids =
709                ReadPropertyMultipleObject::decode(&mut inner_reader, inner_buf)?;
710            objects.push(object_with_property_ids);
711        }
712
713        Ok(Self::new(objects))
714    }
715}
716
717#[cfg(not(feature = "alloc"))]
718impl<'a> IntoIterator for &'_ ReadPropertyMultiple<'a> {
719    type Item = Result<ReadPropertyMultipleObject<'a>, Error>;
720
721    type IntoIter = ReadPropertyMultipleIter<'a>;
722
723    fn into_iter(self) -> Self::IntoIter {
724        ReadPropertyMultipleIter {
725            buf: self.buf,
726            reader: Reader::new_with_len(self.buf.len()),
727        }
728    }
729}
730
731pub struct ReadPropertyMultipleIter<'a> {
732    buf: &'a [u8],
733    reader: Reader,
734}
735
736impl<'a> Iterator for ReadPropertyMultipleIter<'a> {
737    type Item = Result<ReadPropertyMultipleObject<'a>, Error>;
738
739    fn next(&mut self) -> Option<Self::Item> {
740        if self.reader.eof() {
741            return None;
742        }
743
744        let object_with_property_ids =
745            ReadPropertyMultipleObject::decode(&mut self.reader, self.buf);
746        Some(object_with_property_ids)
747    }
748}