1use bacnet_types::enums::{ObjectType, PropertyIdentifier};
13use bacnet_types::error::Error;
14use bacnet_types::primitives::{Date, ObjectIdentifier, PropertyValue, StatusFlags, Time};
15use std::borrow::Cow;
16
17use crate::common::{self, read_common_properties};
18use crate::traits::BACnetObject;
19
20pub struct AccessDoorObject {
29 oid: ObjectIdentifier,
30 name: String,
31 description: String,
32 present_value: u32, door_status: u32, lock_status: u32, secured_status: u32, door_alarm_state: u32, door_members: Vec<ObjectIdentifier>,
38 status_flags: StatusFlags,
39 out_of_service: bool,
40 reliability: u32,
41}
42
43impl AccessDoorObject {
44 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
46 let oid = ObjectIdentifier::new(ObjectType::ACCESS_DOOR, instance)?;
47 Ok(Self {
48 oid,
49 name: name.into(),
50 description: String::new(),
51 present_value: 0, door_status: 0, lock_status: 0,
54 secured_status: 0,
55 door_alarm_state: 0,
56 door_members: Vec::new(),
57 status_flags: StatusFlags::empty(),
58 out_of_service: false,
59 reliability: 0,
60 })
61 }
62}
63
64impl BACnetObject for AccessDoorObject {
65 fn object_identifier(&self) -> ObjectIdentifier {
66 self.oid
67 }
68
69 fn object_name(&self) -> &str {
70 &self.name
71 }
72
73 fn read_property(
74 &self,
75 property: PropertyIdentifier,
76 array_index: Option<u32>,
77 ) -> Result<PropertyValue, Error> {
78 if let Some(result) = read_common_properties!(self, property, array_index) {
79 return result;
80 }
81 match property {
82 p if p == PropertyIdentifier::OBJECT_TYPE => {
83 Ok(PropertyValue::Enumerated(ObjectType::ACCESS_DOOR.to_raw()))
84 }
85 p if p == PropertyIdentifier::PRESENT_VALUE => {
86 Ok(PropertyValue::Enumerated(self.present_value))
87 }
88 p if p == PropertyIdentifier::DOOR_STATUS => {
89 Ok(PropertyValue::Enumerated(self.door_status))
90 }
91 p if p == PropertyIdentifier::LOCK_STATUS => {
92 Ok(PropertyValue::Enumerated(self.lock_status))
93 }
94 p if p == PropertyIdentifier::SECURED_STATUS => {
95 Ok(PropertyValue::Enumerated(self.secured_status))
96 }
97 p if p == PropertyIdentifier::DOOR_ALARM_STATE => {
98 Ok(PropertyValue::Enumerated(self.door_alarm_state))
99 }
100 p if p == PropertyIdentifier::DOOR_MEMBERS => Ok(PropertyValue::List(
101 self.door_members
102 .iter()
103 .map(|oid| PropertyValue::ObjectIdentifier(*oid))
104 .collect(),
105 )),
106 _ => Err(common::unknown_property_error()),
107 }
108 }
109
110 fn write_property(
111 &mut self,
112 property: PropertyIdentifier,
113 _array_index: Option<u32>,
114 value: PropertyValue,
115 _priority: Option<u8>,
116 ) -> Result<(), Error> {
117 if let Some(result) =
118 common::write_out_of_service(&mut self.out_of_service, property, &value)
119 {
120 return result;
121 }
122 if let Some(result) = common::write_description(&mut self.description, property, &value) {
123 return result;
124 }
125 match property {
126 p if p == PropertyIdentifier::PRESENT_VALUE => {
127 if !self.out_of_service {
128 return Err(common::write_access_denied_error());
129 }
130 if let PropertyValue::Enumerated(v) = value {
131 self.present_value = v;
132 Ok(())
133 } else {
134 Err(common::invalid_data_type_error())
135 }
136 }
137 _ => Err(common::write_access_denied_error()),
138 }
139 }
140
141 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
142 static PROPS: &[PropertyIdentifier] = &[
143 PropertyIdentifier::OBJECT_IDENTIFIER,
144 PropertyIdentifier::OBJECT_NAME,
145 PropertyIdentifier::DESCRIPTION,
146 PropertyIdentifier::OBJECT_TYPE,
147 PropertyIdentifier::PRESENT_VALUE,
148 PropertyIdentifier::DOOR_STATUS,
149 PropertyIdentifier::LOCK_STATUS,
150 PropertyIdentifier::SECURED_STATUS,
151 PropertyIdentifier::DOOR_ALARM_STATE,
152 PropertyIdentifier::DOOR_MEMBERS,
153 PropertyIdentifier::STATUS_FLAGS,
154 PropertyIdentifier::OUT_OF_SERVICE,
155 PropertyIdentifier::RELIABILITY,
156 ];
157 Cow::Borrowed(PROPS)
158 }
159}
160
161pub struct AccessCredentialObject {
170 oid: ObjectIdentifier,
171 name: String,
172 description: String,
173 present_value: u32, credential_status: u32,
175 assigned_access_rights_count: u32,
176 authentication_factors: Vec<Vec<u8>>,
177 status_flags: StatusFlags,
178 out_of_service: bool,
179 reliability: u32,
180}
181
182impl AccessCredentialObject {
183 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
185 let oid = ObjectIdentifier::new(ObjectType::ACCESS_CREDENTIAL, instance)?;
186 Ok(Self {
187 oid,
188 name: name.into(),
189 description: String::new(),
190 present_value: 0, credential_status: 0,
192 assigned_access_rights_count: 0,
193 authentication_factors: Vec::new(),
194 status_flags: StatusFlags::empty(),
195 out_of_service: false,
196 reliability: 0,
197 })
198 }
199}
200
201impl BACnetObject for AccessCredentialObject {
202 fn object_identifier(&self) -> ObjectIdentifier {
203 self.oid
204 }
205
206 fn object_name(&self) -> &str {
207 &self.name
208 }
209
210 fn read_property(
211 &self,
212 property: PropertyIdentifier,
213 array_index: Option<u32>,
214 ) -> Result<PropertyValue, Error> {
215 if let Some(result) = read_common_properties!(self, property, array_index) {
216 return result;
217 }
218 match property {
219 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
220 ObjectType::ACCESS_CREDENTIAL.to_raw(),
221 )),
222 p if p == PropertyIdentifier::PRESENT_VALUE => {
223 Ok(PropertyValue::Enumerated(self.present_value))
224 }
225 p if p == PropertyIdentifier::CREDENTIAL_STATUS => {
226 Ok(PropertyValue::Enumerated(self.credential_status))
227 }
228 p if p == PropertyIdentifier::ASSIGNED_ACCESS_RIGHTS => Ok(PropertyValue::Unsigned(
229 self.assigned_access_rights_count as u64,
230 )),
231 p if p == PropertyIdentifier::AUTHENTICATION_FACTORS => Ok(PropertyValue::List(
232 self.authentication_factors
233 .iter()
234 .map(|f| PropertyValue::OctetString(f.clone()))
235 .collect(),
236 )),
237 _ => Err(common::unknown_property_error()),
238 }
239 }
240
241 fn write_property(
242 &mut self,
243 property: PropertyIdentifier,
244 _array_index: Option<u32>,
245 value: PropertyValue,
246 _priority: Option<u8>,
247 ) -> Result<(), Error> {
248 if let Some(result) =
249 common::write_out_of_service(&mut self.out_of_service, property, &value)
250 {
251 return result;
252 }
253 if let Some(result) = common::write_description(&mut self.description, property, &value) {
254 return result;
255 }
256 match property {
257 p if p == PropertyIdentifier::PRESENT_VALUE => {
258 if let PropertyValue::Enumerated(v) = value {
259 self.present_value = v;
260 Ok(())
261 } else {
262 Err(common::invalid_data_type_error())
263 }
264 }
265 p if p == PropertyIdentifier::CREDENTIAL_STATUS => {
266 if let PropertyValue::Enumerated(v) = value {
267 self.credential_status = v;
268 Ok(())
269 } else {
270 Err(common::invalid_data_type_error())
271 }
272 }
273 _ => Err(common::write_access_denied_error()),
274 }
275 }
276
277 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
278 static PROPS: &[PropertyIdentifier] = &[
279 PropertyIdentifier::OBJECT_IDENTIFIER,
280 PropertyIdentifier::OBJECT_NAME,
281 PropertyIdentifier::DESCRIPTION,
282 PropertyIdentifier::OBJECT_TYPE,
283 PropertyIdentifier::PRESENT_VALUE,
284 PropertyIdentifier::CREDENTIAL_STATUS,
285 PropertyIdentifier::ASSIGNED_ACCESS_RIGHTS,
286 PropertyIdentifier::AUTHENTICATION_FACTORS,
287 PropertyIdentifier::STATUS_FLAGS,
288 PropertyIdentifier::OUT_OF_SERVICE,
289 PropertyIdentifier::RELIABILITY,
290 ];
291 Cow::Borrowed(PROPS)
292 }
293}
294
295pub struct AccessPointObject {
304 oid: ObjectIdentifier,
305 name: String,
306 description: String,
307 present_value: u32, access_event: u32,
309 access_event_tag: u64,
310 access_event_time: ([u8; 4], [u8; 4]), access_doors: Vec<ObjectIdentifier>,
312 event_state: u32,
313 status_flags: StatusFlags,
314 out_of_service: bool,
315 reliability: u32,
316}
317
318impl AccessPointObject {
319 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
321 let oid = ObjectIdentifier::new(ObjectType::ACCESS_POINT, instance)?;
322 Ok(Self {
323 oid,
324 name: name.into(),
325 description: String::new(),
326 present_value: 0,
327 access_event: 0,
328 access_event_tag: 0,
329 access_event_time: ([0xFF, 0xFF, 0xFF, 0xFF], [0xFF, 0xFF, 0xFF, 0xFF]),
330 access_doors: Vec::new(),
331 event_state: 0,
332 status_flags: StatusFlags::empty(),
333 out_of_service: false,
334 reliability: 0,
335 })
336 }
337}
338
339impl BACnetObject for AccessPointObject {
340 fn object_identifier(&self) -> ObjectIdentifier {
341 self.oid
342 }
343
344 fn object_name(&self) -> &str {
345 &self.name
346 }
347
348 fn read_property(
349 &self,
350 property: PropertyIdentifier,
351 array_index: Option<u32>,
352 ) -> Result<PropertyValue, Error> {
353 if let Some(result) = read_common_properties!(self, property, array_index) {
354 return result;
355 }
356 match property {
357 p if p == PropertyIdentifier::OBJECT_TYPE => {
358 Ok(PropertyValue::Enumerated(ObjectType::ACCESS_POINT.to_raw()))
359 }
360 p if p == PropertyIdentifier::PRESENT_VALUE => {
361 Ok(PropertyValue::Enumerated(self.present_value))
362 }
363 p if p == PropertyIdentifier::ACCESS_EVENT => {
364 Ok(PropertyValue::Enumerated(self.access_event))
365 }
366 p if p == PropertyIdentifier::ACCESS_EVENT_TAG => {
367 Ok(PropertyValue::Unsigned(self.access_event_tag))
368 }
369 p if p == PropertyIdentifier::ACCESS_EVENT_TIME => {
370 let (d, t) = &self.access_event_time;
371 Ok(PropertyValue::List(vec![
372 PropertyValue::Date(Date {
373 year: d[0],
374 month: d[1],
375 day: d[2],
376 day_of_week: d[3],
377 }),
378 PropertyValue::Time(Time {
379 hour: t[0],
380 minute: t[1],
381 second: t[2],
382 hundredths: t[3],
383 }),
384 ]))
385 }
386 p if p == PropertyIdentifier::ACCESS_DOORS => Ok(PropertyValue::List(
387 self.access_doors
388 .iter()
389 .map(|oid| PropertyValue::ObjectIdentifier(*oid))
390 .collect(),
391 )),
392 p if p == PropertyIdentifier::EVENT_STATE => {
393 Ok(PropertyValue::Enumerated(self.event_state))
394 }
395 _ => Err(common::unknown_property_error()),
396 }
397 }
398
399 fn write_property(
400 &mut self,
401 property: PropertyIdentifier,
402 _array_index: Option<u32>,
403 value: PropertyValue,
404 _priority: Option<u8>,
405 ) -> Result<(), Error> {
406 if let Some(result) =
407 common::write_out_of_service(&mut self.out_of_service, property, &value)
408 {
409 return result;
410 }
411 if let Some(result) = common::write_description(&mut self.description, property, &value) {
412 return result;
413 }
414 match property {
415 p if p == PropertyIdentifier::PRESENT_VALUE => {
416 if let PropertyValue::Enumerated(v) = value {
417 self.present_value = v;
418 Ok(())
419 } else {
420 Err(common::invalid_data_type_error())
421 }
422 }
423 _ => Err(common::write_access_denied_error()),
424 }
425 }
426
427 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
428 static PROPS: &[PropertyIdentifier] = &[
429 PropertyIdentifier::OBJECT_IDENTIFIER,
430 PropertyIdentifier::OBJECT_NAME,
431 PropertyIdentifier::DESCRIPTION,
432 PropertyIdentifier::OBJECT_TYPE,
433 PropertyIdentifier::PRESENT_VALUE,
434 PropertyIdentifier::ACCESS_EVENT,
435 PropertyIdentifier::ACCESS_EVENT_TAG,
436 PropertyIdentifier::ACCESS_EVENT_TIME,
437 PropertyIdentifier::ACCESS_DOORS,
438 PropertyIdentifier::EVENT_STATE,
439 PropertyIdentifier::STATUS_FLAGS,
440 PropertyIdentifier::OUT_OF_SERVICE,
441 PropertyIdentifier::RELIABILITY,
442 ];
443 Cow::Borrowed(PROPS)
444 }
445}
446
447pub struct AccessRightsObject {
456 oid: ObjectIdentifier,
457 name: String,
458 description: String,
459 global_identifier: u64,
460 positive_access_rules_count: u32,
461 negative_access_rules_count: u32,
462 status_flags: StatusFlags,
463 out_of_service: bool,
464 reliability: u32,
465}
466
467impl AccessRightsObject {
468 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
470 let oid = ObjectIdentifier::new(ObjectType::ACCESS_RIGHTS, instance)?;
471 Ok(Self {
472 oid,
473 name: name.into(),
474 description: String::new(),
475 global_identifier: 0,
476 positive_access_rules_count: 0,
477 negative_access_rules_count: 0,
478 status_flags: StatusFlags::empty(),
479 out_of_service: false,
480 reliability: 0,
481 })
482 }
483}
484
485impl BACnetObject for AccessRightsObject {
486 fn object_identifier(&self) -> ObjectIdentifier {
487 self.oid
488 }
489
490 fn object_name(&self) -> &str {
491 &self.name
492 }
493
494 fn read_property(
495 &self,
496 property: PropertyIdentifier,
497 array_index: Option<u32>,
498 ) -> Result<PropertyValue, Error> {
499 if let Some(result) = read_common_properties!(self, property, array_index) {
500 return result;
501 }
502 match property {
503 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
504 ObjectType::ACCESS_RIGHTS.to_raw(),
505 )),
506 p if p == PropertyIdentifier::GLOBAL_IDENTIFIER => {
507 Ok(PropertyValue::Unsigned(self.global_identifier))
508 }
509 p if p == PropertyIdentifier::POSITIVE_ACCESS_RULES => Ok(PropertyValue::Unsigned(
510 self.positive_access_rules_count as u64,
511 )),
512 p if p == PropertyIdentifier::NEGATIVE_ACCESS_RULES => Ok(PropertyValue::Unsigned(
513 self.negative_access_rules_count as u64,
514 )),
515 _ => Err(common::unknown_property_error()),
516 }
517 }
518
519 fn write_property(
520 &mut self,
521 property: PropertyIdentifier,
522 _array_index: Option<u32>,
523 value: PropertyValue,
524 _priority: Option<u8>,
525 ) -> Result<(), Error> {
526 if let Some(result) =
527 common::write_out_of_service(&mut self.out_of_service, property, &value)
528 {
529 return result;
530 }
531 if let Some(result) = common::write_description(&mut self.description, property, &value) {
532 return result;
533 }
534 match property {
535 p if p == PropertyIdentifier::GLOBAL_IDENTIFIER => {
536 if let PropertyValue::Unsigned(v) = value {
537 self.global_identifier = v;
538 Ok(())
539 } else {
540 Err(common::invalid_data_type_error())
541 }
542 }
543 _ => Err(common::write_access_denied_error()),
544 }
545 }
546
547 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
548 static PROPS: &[PropertyIdentifier] = &[
549 PropertyIdentifier::OBJECT_IDENTIFIER,
550 PropertyIdentifier::OBJECT_NAME,
551 PropertyIdentifier::DESCRIPTION,
552 PropertyIdentifier::OBJECT_TYPE,
553 PropertyIdentifier::GLOBAL_IDENTIFIER,
554 PropertyIdentifier::POSITIVE_ACCESS_RULES,
555 PropertyIdentifier::NEGATIVE_ACCESS_RULES,
556 PropertyIdentifier::STATUS_FLAGS,
557 PropertyIdentifier::OUT_OF_SERVICE,
558 PropertyIdentifier::RELIABILITY,
559 ];
560 Cow::Borrowed(PROPS)
561 }
562}
563
564pub struct AccessUserObject {
573 oid: ObjectIdentifier,
574 name: String,
575 description: String,
576 present_value: u32, user_type: u32,
578 credentials: Vec<ObjectIdentifier>,
579 assigned_access_rights_count: u32,
580 status_flags: StatusFlags,
581 out_of_service: bool,
582 reliability: u32,
583}
584
585impl AccessUserObject {
586 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
588 let oid = ObjectIdentifier::new(ObjectType::ACCESS_USER, instance)?;
589 Ok(Self {
590 oid,
591 name: name.into(),
592 description: String::new(),
593 present_value: 0,
594 user_type: 0,
595 credentials: Vec::new(),
596 assigned_access_rights_count: 0,
597 status_flags: StatusFlags::empty(),
598 out_of_service: false,
599 reliability: 0,
600 })
601 }
602}
603
604impl BACnetObject for AccessUserObject {
605 fn object_identifier(&self) -> ObjectIdentifier {
606 self.oid
607 }
608
609 fn object_name(&self) -> &str {
610 &self.name
611 }
612
613 fn read_property(
614 &self,
615 property: PropertyIdentifier,
616 array_index: Option<u32>,
617 ) -> Result<PropertyValue, Error> {
618 if let Some(result) = read_common_properties!(self, property, array_index) {
619 return result;
620 }
621 match property {
622 p if p == PropertyIdentifier::OBJECT_TYPE => {
623 Ok(PropertyValue::Enumerated(ObjectType::ACCESS_USER.to_raw()))
624 }
625 p if p == PropertyIdentifier::PRESENT_VALUE => {
626 Ok(PropertyValue::Enumerated(self.present_value))
627 }
628 p if p == PropertyIdentifier::USER_TYPE => {
629 Ok(PropertyValue::Enumerated(self.user_type))
630 }
631 p if p == PropertyIdentifier::CREDENTIALS => Ok(PropertyValue::List(
632 self.credentials
633 .iter()
634 .map(|oid| PropertyValue::ObjectIdentifier(*oid))
635 .collect(),
636 )),
637 p if p == PropertyIdentifier::ASSIGNED_ACCESS_RIGHTS => Ok(PropertyValue::Unsigned(
638 self.assigned_access_rights_count as u64,
639 )),
640 _ => Err(common::unknown_property_error()),
641 }
642 }
643
644 fn write_property(
645 &mut self,
646 property: PropertyIdentifier,
647 _array_index: Option<u32>,
648 value: PropertyValue,
649 _priority: Option<u8>,
650 ) -> Result<(), Error> {
651 if let Some(result) =
652 common::write_out_of_service(&mut self.out_of_service, property, &value)
653 {
654 return result;
655 }
656 if let Some(result) = common::write_description(&mut self.description, property, &value) {
657 return result;
658 }
659 match property {
660 p if p == PropertyIdentifier::PRESENT_VALUE => {
661 if let PropertyValue::Enumerated(v) = value {
662 self.present_value = v;
663 Ok(())
664 } else {
665 Err(common::invalid_data_type_error())
666 }
667 }
668 p if p == PropertyIdentifier::USER_TYPE => {
669 if let PropertyValue::Enumerated(v) = value {
670 self.user_type = v;
671 Ok(())
672 } else {
673 Err(common::invalid_data_type_error())
674 }
675 }
676 _ => Err(common::write_access_denied_error()),
677 }
678 }
679
680 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
681 static PROPS: &[PropertyIdentifier] = &[
682 PropertyIdentifier::OBJECT_IDENTIFIER,
683 PropertyIdentifier::OBJECT_NAME,
684 PropertyIdentifier::DESCRIPTION,
685 PropertyIdentifier::OBJECT_TYPE,
686 PropertyIdentifier::PRESENT_VALUE,
687 PropertyIdentifier::USER_TYPE,
688 PropertyIdentifier::CREDENTIALS,
689 PropertyIdentifier::ASSIGNED_ACCESS_RIGHTS,
690 PropertyIdentifier::STATUS_FLAGS,
691 PropertyIdentifier::OUT_OF_SERVICE,
692 PropertyIdentifier::RELIABILITY,
693 ];
694 Cow::Borrowed(PROPS)
695 }
696}
697
698pub struct AccessZoneObject {
707 oid: ObjectIdentifier,
708 name: String,
709 description: String,
710 present_value: u32, global_identifier: u64,
712 occupancy_count: u64,
713 access_doors: Vec<ObjectIdentifier>,
714 entry_points: Vec<ObjectIdentifier>,
715 exit_points: Vec<ObjectIdentifier>,
716 status_flags: StatusFlags,
717 out_of_service: bool,
718 reliability: u32,
719}
720
721impl AccessZoneObject {
722 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
724 let oid = ObjectIdentifier::new(ObjectType::ACCESS_ZONE, instance)?;
725 Ok(Self {
726 oid,
727 name: name.into(),
728 description: String::new(),
729 present_value: 0,
730 global_identifier: 0,
731 occupancy_count: 0,
732 access_doors: Vec::new(),
733 entry_points: Vec::new(),
734 exit_points: Vec::new(),
735 status_flags: StatusFlags::empty(),
736 out_of_service: false,
737 reliability: 0,
738 })
739 }
740}
741
742impl BACnetObject for AccessZoneObject {
743 fn object_identifier(&self) -> ObjectIdentifier {
744 self.oid
745 }
746
747 fn object_name(&self) -> &str {
748 &self.name
749 }
750
751 fn read_property(
752 &self,
753 property: PropertyIdentifier,
754 array_index: Option<u32>,
755 ) -> Result<PropertyValue, Error> {
756 if let Some(result) = read_common_properties!(self, property, array_index) {
757 return result;
758 }
759 match property {
760 p if p == PropertyIdentifier::OBJECT_TYPE => {
761 Ok(PropertyValue::Enumerated(ObjectType::ACCESS_ZONE.to_raw()))
762 }
763 p if p == PropertyIdentifier::PRESENT_VALUE => {
764 Ok(PropertyValue::Enumerated(self.present_value))
765 }
766 p if p == PropertyIdentifier::GLOBAL_IDENTIFIER => {
767 Ok(PropertyValue::Unsigned(self.global_identifier))
768 }
769 p if p == PropertyIdentifier::OCCUPANCY_COUNT => {
770 Ok(PropertyValue::Unsigned(self.occupancy_count))
771 }
772 p if p == PropertyIdentifier::ACCESS_DOORS => Ok(PropertyValue::List(
773 self.access_doors
774 .iter()
775 .map(|oid| PropertyValue::ObjectIdentifier(*oid))
776 .collect(),
777 )),
778 p if p == PropertyIdentifier::ENTRY_POINTS => Ok(PropertyValue::List(
779 self.entry_points
780 .iter()
781 .map(|oid| PropertyValue::ObjectIdentifier(*oid))
782 .collect(),
783 )),
784 p if p == PropertyIdentifier::EXIT_POINTS => Ok(PropertyValue::List(
785 self.exit_points
786 .iter()
787 .map(|oid| PropertyValue::ObjectIdentifier(*oid))
788 .collect(),
789 )),
790 _ => Err(common::unknown_property_error()),
791 }
792 }
793
794 fn write_property(
795 &mut self,
796 property: PropertyIdentifier,
797 _array_index: Option<u32>,
798 value: PropertyValue,
799 _priority: Option<u8>,
800 ) -> Result<(), Error> {
801 if let Some(result) =
802 common::write_out_of_service(&mut self.out_of_service, property, &value)
803 {
804 return result;
805 }
806 if let Some(result) = common::write_description(&mut self.description, property, &value) {
807 return result;
808 }
809 match property {
810 p if p == PropertyIdentifier::PRESENT_VALUE => {
811 if let PropertyValue::Enumerated(v) = value {
812 self.present_value = v;
813 Ok(())
814 } else {
815 Err(common::invalid_data_type_error())
816 }
817 }
818 p if p == PropertyIdentifier::GLOBAL_IDENTIFIER => {
819 if let PropertyValue::Unsigned(v) = value {
820 self.global_identifier = v;
821 Ok(())
822 } else {
823 Err(common::invalid_data_type_error())
824 }
825 }
826 _ => Err(common::write_access_denied_error()),
827 }
828 }
829
830 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
831 static PROPS: &[PropertyIdentifier] = &[
832 PropertyIdentifier::OBJECT_IDENTIFIER,
833 PropertyIdentifier::OBJECT_NAME,
834 PropertyIdentifier::DESCRIPTION,
835 PropertyIdentifier::OBJECT_TYPE,
836 PropertyIdentifier::PRESENT_VALUE,
837 PropertyIdentifier::GLOBAL_IDENTIFIER,
838 PropertyIdentifier::OCCUPANCY_COUNT,
839 PropertyIdentifier::ACCESS_DOORS,
840 PropertyIdentifier::ENTRY_POINTS,
841 PropertyIdentifier::EXIT_POINTS,
842 PropertyIdentifier::STATUS_FLAGS,
843 PropertyIdentifier::OUT_OF_SERVICE,
844 PropertyIdentifier::RELIABILITY,
845 ];
846 Cow::Borrowed(PROPS)
847 }
848}
849
850pub struct CredentialDataInputObject {
859 oid: ObjectIdentifier,
860 name: String,
861 description: String,
862 present_value: u32, update_time: ([u8; 4], [u8; 4]), supported_formats: Vec<u64>,
865 supported_format_classes: Vec<u64>,
866 status_flags: StatusFlags,
867 out_of_service: bool,
868 reliability: u32,
869}
870
871impl CredentialDataInputObject {
872 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
874 let oid = ObjectIdentifier::new(ObjectType::CREDENTIAL_DATA_INPUT, instance)?;
875 Ok(Self {
876 oid,
877 name: name.into(),
878 description: String::new(),
879 present_value: 0, update_time: ([0xFF, 0xFF, 0xFF, 0xFF], [0xFF, 0xFF, 0xFF, 0xFF]),
881 supported_formats: Vec::new(),
882 supported_format_classes: Vec::new(),
883 status_flags: StatusFlags::empty(),
884 out_of_service: false,
885 reliability: 0,
886 })
887 }
888}
889
890impl BACnetObject for CredentialDataInputObject {
891 fn object_identifier(&self) -> ObjectIdentifier {
892 self.oid
893 }
894
895 fn object_name(&self) -> &str {
896 &self.name
897 }
898
899 fn read_property(
900 &self,
901 property: PropertyIdentifier,
902 array_index: Option<u32>,
903 ) -> Result<PropertyValue, Error> {
904 if let Some(result) = read_common_properties!(self, property, array_index) {
905 return result;
906 }
907 match property {
908 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
909 ObjectType::CREDENTIAL_DATA_INPUT.to_raw(),
910 )),
911 p if p == PropertyIdentifier::PRESENT_VALUE => {
912 Ok(PropertyValue::Enumerated(self.present_value))
913 }
914 p if p == PropertyIdentifier::UPDATE_TIME => {
915 let (d, t) = &self.update_time;
916 Ok(PropertyValue::List(vec![
917 PropertyValue::Date(Date {
918 year: d[0],
919 month: d[1],
920 day: d[2],
921 day_of_week: d[3],
922 }),
923 PropertyValue::Time(Time {
924 hour: t[0],
925 minute: t[1],
926 second: t[2],
927 hundredths: t[3],
928 }),
929 ]))
930 }
931 p if p == PropertyIdentifier::SUPPORTED_FORMATS => Ok(PropertyValue::List(
932 self.supported_formats
933 .iter()
934 .map(|v| PropertyValue::Unsigned(*v))
935 .collect(),
936 )),
937 p if p == PropertyIdentifier::SUPPORTED_FORMAT_CLASSES => Ok(PropertyValue::List(
938 self.supported_format_classes
939 .iter()
940 .map(|v| PropertyValue::Unsigned(*v))
941 .collect(),
942 )),
943 _ => Err(common::unknown_property_error()),
944 }
945 }
946
947 fn write_property(
948 &mut self,
949 property: PropertyIdentifier,
950 _array_index: Option<u32>,
951 value: PropertyValue,
952 _priority: Option<u8>,
953 ) -> Result<(), Error> {
954 if let Some(result) =
955 common::write_out_of_service(&mut self.out_of_service, property, &value)
956 {
957 return result;
958 }
959 if let Some(result) = common::write_description(&mut self.description, property, &value) {
960 return result;
961 }
962 Err(common::write_access_denied_error())
964 }
965
966 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
967 static PROPS: &[PropertyIdentifier] = &[
968 PropertyIdentifier::OBJECT_IDENTIFIER,
969 PropertyIdentifier::OBJECT_NAME,
970 PropertyIdentifier::DESCRIPTION,
971 PropertyIdentifier::OBJECT_TYPE,
972 PropertyIdentifier::PRESENT_VALUE,
973 PropertyIdentifier::UPDATE_TIME,
974 PropertyIdentifier::SUPPORTED_FORMATS,
975 PropertyIdentifier::SUPPORTED_FORMAT_CLASSES,
976 PropertyIdentifier::STATUS_FLAGS,
977 PropertyIdentifier::OUT_OF_SERVICE,
978 PropertyIdentifier::RELIABILITY,
979 ];
980 Cow::Borrowed(PROPS)
981 }
982}
983
984#[cfg(test)]
989mod tests {
990 use super::*;
991
992 #[test]
995 fn access_door_create_and_read_defaults() {
996 let door = AccessDoorObject::new(1, "DOOR-1").unwrap();
997 assert_eq!(door.object_name(), "DOOR-1");
998 assert_eq!(
999 door.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1000 .unwrap(),
1001 PropertyValue::Enumerated(0) );
1003 }
1004
1005 #[test]
1006 fn access_door_object_type() {
1007 let door = AccessDoorObject::new(1, "DOOR-1").unwrap();
1008 assert_eq!(
1009 door.read_property(PropertyIdentifier::OBJECT_TYPE, None)
1010 .unwrap(),
1011 PropertyValue::Enumerated(ObjectType::ACCESS_DOOR.to_raw())
1012 );
1013 }
1014
1015 #[test]
1016 fn access_door_property_list() {
1017 let door = AccessDoorObject::new(1, "DOOR-1").unwrap();
1018 let list = door.property_list();
1019 assert!(list.contains(&PropertyIdentifier::PRESENT_VALUE));
1020 assert!(list.contains(&PropertyIdentifier::DOOR_STATUS));
1021 assert!(list.contains(&PropertyIdentifier::LOCK_STATUS));
1022 assert!(list.contains(&PropertyIdentifier::SECURED_STATUS));
1023 assert!(list.contains(&PropertyIdentifier::DOOR_ALARM_STATE));
1024 assert!(list.contains(&PropertyIdentifier::DOOR_MEMBERS));
1025 }
1026
1027 #[test]
1028 fn access_door_read_door_members_empty() {
1029 let door = AccessDoorObject::new(1, "DOOR-1").unwrap();
1030 assert_eq!(
1031 door.read_property(PropertyIdentifier::DOOR_MEMBERS, None)
1032 .unwrap(),
1033 PropertyValue::List(vec![])
1034 );
1035 }
1036
1037 #[test]
1038 fn access_door_write_present_value() {
1039 let mut door = AccessDoorObject::new(1, "DOOR-1").unwrap();
1040 door.write_property(
1042 PropertyIdentifier::OUT_OF_SERVICE,
1043 None,
1044 PropertyValue::Boolean(true),
1045 None,
1046 )
1047 .unwrap();
1048 door.write_property(
1049 PropertyIdentifier::PRESENT_VALUE,
1050 None,
1051 PropertyValue::Enumerated(1), None,
1053 )
1054 .unwrap();
1055 assert_eq!(
1056 door.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1057 .unwrap(),
1058 PropertyValue::Enumerated(1)
1059 );
1060 }
1061
1062 #[test]
1063 fn access_door_write_present_value_not_out_of_service() {
1064 let mut door = AccessDoorObject::new(1, "DOOR-1").unwrap();
1065 let result = door.write_property(
1067 PropertyIdentifier::PRESENT_VALUE,
1068 None,
1069 PropertyValue::Enumerated(1),
1070 None,
1071 );
1072 assert!(result.is_err());
1073 }
1074
1075 #[test]
1076 fn access_door_write_present_value_wrong_type() {
1077 let mut door = AccessDoorObject::new(1, "DOOR-1").unwrap();
1078 door.write_property(
1079 PropertyIdentifier::OUT_OF_SERVICE,
1080 None,
1081 PropertyValue::Boolean(true),
1082 None,
1083 )
1084 .unwrap();
1085 let result = door.write_property(
1086 PropertyIdentifier::PRESENT_VALUE,
1087 None,
1088 PropertyValue::Real(1.0),
1089 None,
1090 );
1091 assert!(result.is_err());
1092 }
1093
1094 #[test]
1097 fn access_credential_create_and_read_defaults() {
1098 let cred = AccessCredentialObject::new(1, "CRED-1").unwrap();
1099 assert_eq!(cred.object_name(), "CRED-1");
1100 assert_eq!(
1101 cred.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1102 .unwrap(),
1103 PropertyValue::Enumerated(0) );
1105 }
1106
1107 #[test]
1108 fn access_credential_object_type() {
1109 let cred = AccessCredentialObject::new(1, "CRED-1").unwrap();
1110 assert_eq!(
1111 cred.read_property(PropertyIdentifier::OBJECT_TYPE, None)
1112 .unwrap(),
1113 PropertyValue::Enumerated(ObjectType::ACCESS_CREDENTIAL.to_raw())
1114 );
1115 }
1116
1117 #[test]
1118 fn access_credential_property_list() {
1119 let cred = AccessCredentialObject::new(1, "CRED-1").unwrap();
1120 let list = cred.property_list();
1121 assert!(list.contains(&PropertyIdentifier::PRESENT_VALUE));
1122 assert!(list.contains(&PropertyIdentifier::CREDENTIAL_STATUS));
1123 assert!(list.contains(&PropertyIdentifier::ASSIGNED_ACCESS_RIGHTS));
1124 assert!(list.contains(&PropertyIdentifier::AUTHENTICATION_FACTORS));
1125 }
1126
1127 #[test]
1128 fn access_credential_read_assigned_access_rights() {
1129 let cred = AccessCredentialObject::new(1, "CRED-1").unwrap();
1130 assert_eq!(
1131 cred.read_property(PropertyIdentifier::ASSIGNED_ACCESS_RIGHTS, None)
1132 .unwrap(),
1133 PropertyValue::Unsigned(0)
1134 );
1135 }
1136
1137 #[test]
1138 fn access_credential_read_authentication_factors() {
1139 let cred = AccessCredentialObject::new(1, "CRED-1").unwrap();
1140 assert_eq!(
1141 cred.read_property(PropertyIdentifier::AUTHENTICATION_FACTORS, None)
1142 .unwrap(),
1143 PropertyValue::List(vec![])
1144 );
1145 }
1146
1147 #[test]
1150 fn access_point_create_and_read_defaults() {
1151 let point = AccessPointObject::new(1, "AP-1").unwrap();
1152 assert_eq!(point.object_name(), "AP-1");
1153 assert_eq!(
1154 point
1155 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
1156 .unwrap(),
1157 PropertyValue::Enumerated(0)
1158 );
1159 }
1160
1161 #[test]
1162 fn access_point_object_type() {
1163 let point = AccessPointObject::new(1, "AP-1").unwrap();
1164 assert_eq!(
1165 point
1166 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1167 .unwrap(),
1168 PropertyValue::Enumerated(ObjectType::ACCESS_POINT.to_raw())
1169 );
1170 }
1171
1172 #[test]
1173 fn access_point_property_list() {
1174 let point = AccessPointObject::new(1, "AP-1").unwrap();
1175 let list = point.property_list();
1176 assert!(list.contains(&PropertyIdentifier::PRESENT_VALUE));
1177 assert!(list.contains(&PropertyIdentifier::ACCESS_EVENT));
1178 assert!(list.contains(&PropertyIdentifier::ACCESS_EVENT_TAG));
1179 assert!(list.contains(&PropertyIdentifier::ACCESS_EVENT_TIME));
1180 assert!(list.contains(&PropertyIdentifier::ACCESS_DOORS));
1181 assert!(list.contains(&PropertyIdentifier::EVENT_STATE));
1182 }
1183
1184 #[test]
1185 fn access_point_read_access_event_time() {
1186 let point = AccessPointObject::new(1, "AP-1").unwrap();
1187 let val = point
1188 .read_property(PropertyIdentifier::ACCESS_EVENT_TIME, None)
1189 .unwrap();
1190 match val {
1191 PropertyValue::List(items) => {
1192 assert_eq!(items.len(), 2);
1193 }
1194 other => panic!("expected List, got {other:?}"),
1195 }
1196 }
1197
1198 #[test]
1199 fn access_point_read_access_doors_empty() {
1200 let point = AccessPointObject::new(1, "AP-1").unwrap();
1201 assert_eq!(
1202 point
1203 .read_property(PropertyIdentifier::ACCESS_DOORS, None)
1204 .unwrap(),
1205 PropertyValue::List(vec![])
1206 );
1207 }
1208
1209 #[test]
1212 fn access_rights_create_and_read_defaults() {
1213 let rights = AccessRightsObject::new(1, "AR-1").unwrap();
1214 assert_eq!(rights.object_name(), "AR-1");
1215 assert_eq!(
1216 rights
1217 .read_property(PropertyIdentifier::GLOBAL_IDENTIFIER, None)
1218 .unwrap(),
1219 PropertyValue::Unsigned(0)
1220 );
1221 }
1222
1223 #[test]
1224 fn access_rights_object_type() {
1225 let rights = AccessRightsObject::new(1, "AR-1").unwrap();
1226 assert_eq!(
1227 rights
1228 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
1229 .unwrap(),
1230 PropertyValue::Enumerated(ObjectType::ACCESS_RIGHTS.to_raw())
1231 );
1232 }
1233
1234 #[test]
1235 fn access_rights_property_list() {
1236 let rights = AccessRightsObject::new(1, "AR-1").unwrap();
1237 let list = rights.property_list();
1238 assert!(list.contains(&PropertyIdentifier::GLOBAL_IDENTIFIER));
1239 assert!(list.contains(&PropertyIdentifier::POSITIVE_ACCESS_RULES));
1240 assert!(list.contains(&PropertyIdentifier::NEGATIVE_ACCESS_RULES));
1241 }
1242
1243 #[test]
1244 fn access_rights_read_rules_counts() {
1245 let rights = AccessRightsObject::new(1, "AR-1").unwrap();
1246 assert_eq!(
1247 rights
1248 .read_property(PropertyIdentifier::POSITIVE_ACCESS_RULES, None)
1249 .unwrap(),
1250 PropertyValue::Unsigned(0)
1251 );
1252 assert_eq!(
1253 rights
1254 .read_property(PropertyIdentifier::NEGATIVE_ACCESS_RULES, None)
1255 .unwrap(),
1256 PropertyValue::Unsigned(0)
1257 );
1258 }
1259
1260 #[test]
1261 fn access_rights_write_global_identifier() {
1262 let mut rights = AccessRightsObject::new(1, "AR-1").unwrap();
1263 rights
1264 .write_property(
1265 PropertyIdentifier::GLOBAL_IDENTIFIER,
1266 None,
1267 PropertyValue::Unsigned(42),
1268 None,
1269 )
1270 .unwrap();
1271 assert_eq!(
1272 rights
1273 .read_property(PropertyIdentifier::GLOBAL_IDENTIFIER, None)
1274 .unwrap(),
1275 PropertyValue::Unsigned(42)
1276 );
1277 }
1278
1279 #[test]
1282 fn access_user_create_and_read_defaults() {
1283 let user = AccessUserObject::new(1, "USER-1").unwrap();
1284 assert_eq!(user.object_name(), "USER-1");
1285 assert_eq!(
1286 user.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1287 .unwrap(),
1288 PropertyValue::Enumerated(0)
1289 );
1290 }
1291
1292 #[test]
1293 fn access_user_object_type() {
1294 let user = AccessUserObject::new(1, "USER-1").unwrap();
1295 assert_eq!(
1296 user.read_property(PropertyIdentifier::OBJECT_TYPE, None)
1297 .unwrap(),
1298 PropertyValue::Enumerated(ObjectType::ACCESS_USER.to_raw())
1299 );
1300 }
1301
1302 #[test]
1303 fn access_user_property_list() {
1304 let user = AccessUserObject::new(1, "USER-1").unwrap();
1305 let list = user.property_list();
1306 assert!(list.contains(&PropertyIdentifier::PRESENT_VALUE));
1307 assert!(list.contains(&PropertyIdentifier::USER_TYPE));
1308 assert!(list.contains(&PropertyIdentifier::CREDENTIALS));
1309 assert!(list.contains(&PropertyIdentifier::ASSIGNED_ACCESS_RIGHTS));
1310 }
1311
1312 #[test]
1313 fn access_user_read_credentials_empty() {
1314 let user = AccessUserObject::new(1, "USER-1").unwrap();
1315 assert_eq!(
1316 user.read_property(PropertyIdentifier::CREDENTIALS, None)
1317 .unwrap(),
1318 PropertyValue::List(vec![])
1319 );
1320 }
1321
1322 #[test]
1323 fn access_user_write_user_type() {
1324 let mut user = AccessUserObject::new(1, "USER-1").unwrap();
1325 user.write_property(
1326 PropertyIdentifier::USER_TYPE,
1327 None,
1328 PropertyValue::Enumerated(1),
1329 None,
1330 )
1331 .unwrap();
1332 assert_eq!(
1333 user.read_property(PropertyIdentifier::USER_TYPE, None)
1334 .unwrap(),
1335 PropertyValue::Enumerated(1)
1336 );
1337 }
1338
1339 #[test]
1342 fn access_zone_create_and_read_defaults() {
1343 let zone = AccessZoneObject::new(1, "ZONE-1").unwrap();
1344 assert_eq!(zone.object_name(), "ZONE-1");
1345 assert_eq!(
1346 zone.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1347 .unwrap(),
1348 PropertyValue::Enumerated(0)
1349 );
1350 }
1351
1352 #[test]
1353 fn access_zone_object_type() {
1354 let zone = AccessZoneObject::new(1, "ZONE-1").unwrap();
1355 assert_eq!(
1356 zone.read_property(PropertyIdentifier::OBJECT_TYPE, None)
1357 .unwrap(),
1358 PropertyValue::Enumerated(ObjectType::ACCESS_ZONE.to_raw())
1359 );
1360 }
1361
1362 #[test]
1363 fn access_zone_property_list() {
1364 let zone = AccessZoneObject::new(1, "ZONE-1").unwrap();
1365 let list = zone.property_list();
1366 assert!(list.contains(&PropertyIdentifier::PRESENT_VALUE));
1367 assert!(list.contains(&PropertyIdentifier::GLOBAL_IDENTIFIER));
1368 assert!(list.contains(&PropertyIdentifier::OCCUPANCY_COUNT));
1369 assert!(list.contains(&PropertyIdentifier::ACCESS_DOORS));
1370 assert!(list.contains(&PropertyIdentifier::ENTRY_POINTS));
1371 assert!(list.contains(&PropertyIdentifier::EXIT_POINTS));
1372 }
1373
1374 #[test]
1375 fn access_zone_read_lists_empty() {
1376 let zone = AccessZoneObject::new(1, "ZONE-1").unwrap();
1377 assert_eq!(
1378 zone.read_property(PropertyIdentifier::ACCESS_DOORS, None)
1379 .unwrap(),
1380 PropertyValue::List(vec![])
1381 );
1382 assert_eq!(
1383 zone.read_property(PropertyIdentifier::ENTRY_POINTS, None)
1384 .unwrap(),
1385 PropertyValue::List(vec![])
1386 );
1387 assert_eq!(
1388 zone.read_property(PropertyIdentifier::EXIT_POINTS, None)
1389 .unwrap(),
1390 PropertyValue::List(vec![])
1391 );
1392 }
1393
1394 #[test]
1395 fn access_zone_read_occupancy_count() {
1396 let zone = AccessZoneObject::new(1, "ZONE-1").unwrap();
1397 assert_eq!(
1398 zone.read_property(PropertyIdentifier::OCCUPANCY_COUNT, None)
1399 .unwrap(),
1400 PropertyValue::Unsigned(0)
1401 );
1402 }
1403
1404 #[test]
1405 fn access_zone_write_global_identifier() {
1406 let mut zone = AccessZoneObject::new(1, "ZONE-1").unwrap();
1407 zone.write_property(
1408 PropertyIdentifier::GLOBAL_IDENTIFIER,
1409 None,
1410 PropertyValue::Unsigned(99),
1411 None,
1412 )
1413 .unwrap();
1414 assert_eq!(
1415 zone.read_property(PropertyIdentifier::GLOBAL_IDENTIFIER, None)
1416 .unwrap(),
1417 PropertyValue::Unsigned(99)
1418 );
1419 }
1420
1421 #[test]
1424 fn credential_data_input_create_and_read_defaults() {
1425 let cdi = CredentialDataInputObject::new(1, "CDI-1").unwrap();
1426 assert_eq!(cdi.object_name(), "CDI-1");
1427 assert_eq!(
1428 cdi.read_property(PropertyIdentifier::PRESENT_VALUE, None)
1429 .unwrap(),
1430 PropertyValue::Enumerated(0) );
1432 }
1433
1434 #[test]
1435 fn credential_data_input_object_type() {
1436 let cdi = CredentialDataInputObject::new(1, "CDI-1").unwrap();
1437 assert_eq!(
1438 cdi.read_property(PropertyIdentifier::OBJECT_TYPE, None)
1439 .unwrap(),
1440 PropertyValue::Enumerated(ObjectType::CREDENTIAL_DATA_INPUT.to_raw())
1441 );
1442 }
1443
1444 #[test]
1445 fn credential_data_input_property_list() {
1446 let cdi = CredentialDataInputObject::new(1, "CDI-1").unwrap();
1447 let list = cdi.property_list();
1448 assert!(list.contains(&PropertyIdentifier::PRESENT_VALUE));
1449 assert!(list.contains(&PropertyIdentifier::UPDATE_TIME));
1450 assert!(list.contains(&PropertyIdentifier::SUPPORTED_FORMATS));
1451 assert!(list.contains(&PropertyIdentifier::SUPPORTED_FORMAT_CLASSES));
1452 }
1453
1454 #[test]
1455 fn credential_data_input_read_update_time() {
1456 let cdi = CredentialDataInputObject::new(1, "CDI-1").unwrap();
1457 let val = cdi
1458 .read_property(PropertyIdentifier::UPDATE_TIME, None)
1459 .unwrap();
1460 match val {
1461 PropertyValue::List(items) => {
1462 assert_eq!(items.len(), 2);
1463 }
1464 other => panic!("expected List, got {other:?}"),
1465 }
1466 }
1467
1468 #[test]
1469 fn credential_data_input_read_supported_formats_empty() {
1470 let cdi = CredentialDataInputObject::new(1, "CDI-1").unwrap();
1471 assert_eq!(
1472 cdi.read_property(PropertyIdentifier::SUPPORTED_FORMATS, None)
1473 .unwrap(),
1474 PropertyValue::List(vec![])
1475 );
1476 }
1477
1478 #[test]
1479 fn credential_data_input_write_denied() {
1480 let mut cdi = CredentialDataInputObject::new(1, "CDI-1").unwrap();
1481 let result = cdi.write_property(
1482 PropertyIdentifier::PRESENT_VALUE,
1483 None,
1484 PropertyValue::Enumerated(1),
1485 None,
1486 );
1487 assert!(result.is_err());
1488 }
1489}