Skip to main content

bacnet_objects/binary/
mod.rs

1//! Binary Input (type 3), Binary Output (type 4), and Binary Value (type 5)
2//! objects per ASHRAE 135-2020 Clauses 12.4, 12.5, 12.6.
3
4use bacnet_types::enums::{ObjectType, PropertyIdentifier};
5use bacnet_types::error::Error;
6use bacnet_types::primitives::{BACnetTimeStamp, ObjectIdentifier, PropertyValue, StatusFlags};
7use std::borrow::Cow;
8
9use crate::common::{self, read_common_properties};
10use crate::event::{ChangeOfStateDetector, EventStateChange};
11use crate::traits::BACnetObject;
12
13// ---------------------------------------------------------------------------
14// BinaryInput (type 3)
15// ---------------------------------------------------------------------------
16
17/// BACnet Binary Input object.
18///
19/// Read-only binary point. Present_Value is writable only when out-of-service.
20/// Uses Enumerated values: 0 = inactive, 1 = active.
21pub struct BinaryInputObject {
22    oid: ObjectIdentifier,
23    name: String,
24    description: String,
25    present_value: u32,
26    out_of_service: bool,
27    status_flags: StatusFlags,
28    /// Polarity: 0 = normal, 1 = reverse.
29    polarity: u32,
30    /// Reliability: 0 = NO_FAULT_DETECTED.
31    reliability: u32,
32    active_text: String,
33    inactive_text: String,
34    /// CHANGE_OF_STATE event detector.
35    event_detector: ChangeOfStateDetector,
36}
37
38impl BinaryInputObject {
39    pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
40        let oid = ObjectIdentifier::new(ObjectType::BINARY_INPUT, instance)?;
41        Ok(Self {
42            oid,
43            name: name.into(),
44            description: String::new(),
45            present_value: 0,
46            out_of_service: false,
47            status_flags: StatusFlags::empty(),
48            polarity: 0,
49            reliability: 0,
50            active_text: "Active".into(),
51            inactive_text: "Inactive".into(),
52            event_detector: ChangeOfStateDetector::default(),
53        })
54    }
55
56    /// Set the present value (used by application to update input state).
57    pub fn set_present_value(&mut self, value: u32) {
58        self.present_value = value;
59    }
60
61    /// Set the description string.
62    pub fn set_description(&mut self, desc: impl Into<String>) {
63        self.description = desc.into();
64    }
65}
66
67impl BACnetObject for BinaryInputObject {
68    fn object_identifier(&self) -> ObjectIdentifier {
69        self.oid
70    }
71
72    fn object_name(&self) -> &str {
73        &self.name
74    }
75
76    fn supports_cov(&self) -> bool {
77        true
78    }
79
80    fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
81        self.event_detector.evaluate(self.present_value)
82    }
83
84    fn read_property(
85        &self,
86        property: PropertyIdentifier,
87        array_index: Option<u32>,
88    ) -> Result<PropertyValue, Error> {
89        if property == PropertyIdentifier::STATUS_FLAGS {
90            return Ok(common::compute_status_flags(
91                self.status_flags,
92                self.reliability,
93                self.out_of_service,
94                self.event_detector.event_state.to_raw(),
95            ));
96        }
97        if let Some(result) = read_common_properties!(self, property, array_index) {
98            return result;
99        }
100        match property {
101            p if p == PropertyIdentifier::OBJECT_TYPE => {
102                Ok(PropertyValue::Enumerated(ObjectType::BINARY_INPUT.to_raw()))
103            }
104            p if p == PropertyIdentifier::PRESENT_VALUE => {
105                Ok(PropertyValue::Enumerated(self.present_value))
106            }
107            p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(
108                self.event_detector.event_state.to_raw(),
109            )),
110            p if p == PropertyIdentifier::POLARITY => Ok(PropertyValue::Enumerated(self.polarity)),
111            p if p == PropertyIdentifier::ACTIVE_TEXT => {
112                Ok(PropertyValue::CharacterString(self.active_text.clone()))
113            }
114            p if p == PropertyIdentifier::INACTIVE_TEXT => {
115                Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
116            }
117            p if p == PropertyIdentifier::ALARM_VALUES => Ok(PropertyValue::List(
118                self.event_detector
119                    .alarm_values
120                    .iter()
121                    .map(|v| PropertyValue::Enumerated(*v))
122                    .collect(),
123            )),
124            p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
125                unused_bits: 5,
126                data: vec![self.event_detector.event_enable << 5],
127            }),
128            p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
129                unused_bits: 5,
130                data: vec![self.event_detector.acked_transitions << 5],
131            }),
132            p if p == PropertyIdentifier::NOTIFICATION_CLASS => Ok(PropertyValue::Unsigned(
133                self.event_detector.notification_class as u64,
134            )),
135            p if p == PropertyIdentifier::EVENT_TIME_STAMPS => Ok(PropertyValue::List(vec![
136                PropertyValue::Unsigned(0),
137                PropertyValue::Unsigned(0),
138                PropertyValue::Unsigned(0),
139            ])),
140            _ => Err(common::unknown_property_error()),
141        }
142    }
143
144    fn write_property(
145        &mut self,
146        property: PropertyIdentifier,
147        _array_index: Option<u32>,
148        value: PropertyValue,
149        _priority: Option<u8>,
150    ) -> Result<(), Error> {
151        if property == PropertyIdentifier::PRESENT_VALUE {
152            if !self.out_of_service {
153                return Err(common::write_access_denied_error());
154            }
155            if let PropertyValue::Enumerated(v) = value {
156                if v > 1 {
157                    return Err(common::value_out_of_range_error());
158                }
159                self.present_value = v;
160                return Ok(());
161            }
162            return Err(common::invalid_data_type_error());
163        }
164        if property == PropertyIdentifier::ACTIVE_TEXT {
165            if let PropertyValue::CharacterString(s) = value {
166                self.active_text = s;
167                return Ok(());
168            }
169            return Err(common::invalid_data_type_error());
170        }
171        if property == PropertyIdentifier::INACTIVE_TEXT {
172            if let PropertyValue::CharacterString(s) = value {
173                self.inactive_text = s;
174                return Ok(());
175            }
176            return Err(common::invalid_data_type_error());
177        }
178        if let Some(result) =
179            common::write_out_of_service(&mut self.out_of_service, property, &value)
180        {
181            return result;
182        }
183        if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
184            return result;
185        }
186        if let Some(result) = common::write_description(&mut self.description, property, &value) {
187            return result;
188        }
189        Err(common::write_access_denied_error())
190    }
191
192    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
193        static PROPS: &[PropertyIdentifier] = &[
194            PropertyIdentifier::OBJECT_IDENTIFIER,
195            PropertyIdentifier::OBJECT_NAME,
196            PropertyIdentifier::DESCRIPTION,
197            PropertyIdentifier::OBJECT_TYPE,
198            PropertyIdentifier::PRESENT_VALUE,
199            PropertyIdentifier::STATUS_FLAGS,
200            PropertyIdentifier::EVENT_STATE,
201            PropertyIdentifier::OUT_OF_SERVICE,
202            PropertyIdentifier::POLARITY,
203            PropertyIdentifier::RELIABILITY,
204            PropertyIdentifier::ACTIVE_TEXT,
205            PropertyIdentifier::INACTIVE_TEXT,
206        ];
207        Cow::Borrowed(PROPS)
208    }
209}
210
211// ---------------------------------------------------------------------------
212// BinaryOutput (type 4)
213// ---------------------------------------------------------------------------
214
215/// BACnet Binary Output object.
216///
217/// Commandable binary output with 16-level priority array.
218/// Uses Enumerated values: 0 = inactive, 1 = active.
219pub struct BinaryOutputObject {
220    oid: ObjectIdentifier,
221    name: String,
222    description: String,
223    present_value: u32,
224    out_of_service: bool,
225    status_flags: StatusFlags,
226    priority_array: [Option<u32>; 16],
227    relinquish_default: u32,
228    /// Polarity: 0 = normal, 1 = reverse.
229    polarity: u32,
230    /// Reliability: 0 = NO_FAULT_DETECTED.
231    reliability: u32,
232    active_text: String,
233    inactive_text: String,
234    /// COMMAND_FAILURE event detector.
235    event_detector: ChangeOfStateDetector,
236    /// Value source tracking (optional per spec — exposed via VALUE_SOURCE property).
237    value_source: common::ValueSourceTracking,
238}
239
240impl BinaryOutputObject {
241    pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
242        let oid = ObjectIdentifier::new(ObjectType::BINARY_OUTPUT, instance)?;
243        Ok(Self {
244            oid,
245            name: name.into(),
246            description: String::new(),
247            present_value: 0,
248            out_of_service: false,
249            status_flags: StatusFlags::empty(),
250            priority_array: [None; 16],
251            relinquish_default: 0,
252            polarity: 0,
253            reliability: 0,
254            active_text: "Active".into(),
255            inactive_text: "Inactive".into(),
256            event_detector: ChangeOfStateDetector::default(),
257            value_source: common::ValueSourceTracking::default(),
258        })
259    }
260
261    /// Set the description string.
262    pub fn set_description(&mut self, desc: impl Into<String>) {
263        self.description = desc.into();
264    }
265
266    fn recalculate_present_value(&mut self) {
267        self.present_value =
268            common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
269    }
270}
271
272impl BACnetObject for BinaryOutputObject {
273    fn object_identifier(&self) -> ObjectIdentifier {
274        self.oid
275    }
276
277    fn object_name(&self) -> &str {
278        &self.name
279    }
280
281    fn supports_cov(&self) -> bool {
282        true
283    }
284
285    fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
286        self.event_detector.evaluate(self.present_value)
287    }
288
289    fn read_property(
290        &self,
291        property: PropertyIdentifier,
292        array_index: Option<u32>,
293    ) -> Result<PropertyValue, Error> {
294        if property == PropertyIdentifier::STATUS_FLAGS {
295            return Ok(common::compute_status_flags(
296                self.status_flags,
297                self.reliability,
298                self.out_of_service,
299                self.event_detector.event_state.to_raw(),
300            ));
301        }
302        if let Some(result) = read_common_properties!(self, property, array_index) {
303            return result;
304        }
305        match property {
306            p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
307                ObjectType::BINARY_OUTPUT.to_raw(),
308            )),
309            p if p == PropertyIdentifier::PRESENT_VALUE => {
310                Ok(PropertyValue::Enumerated(self.present_value))
311            }
312            p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(
313                self.event_detector.event_state.to_raw(),
314            )),
315            p if p == PropertyIdentifier::PRIORITY_ARRAY => {
316                common::read_priority_array!(self, array_index, PropertyValue::Enumerated)
317            }
318            p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
319                Ok(PropertyValue::Enumerated(self.relinquish_default))
320            }
321            p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
322                Ok(common::current_command_priority(&self.priority_array))
323            }
324            p if p == PropertyIdentifier::VALUE_SOURCE => {
325                Ok(self.value_source.value_source.clone())
326            }
327            p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
328                match self.value_source.last_command_time {
329                    BACnetTimeStamp::SequenceNumber(n) => n,
330                    _ => 0,
331                },
332            )),
333            p if p == PropertyIdentifier::POLARITY => Ok(PropertyValue::Enumerated(self.polarity)),
334            p if p == PropertyIdentifier::ACTIVE_TEXT => {
335                Ok(PropertyValue::CharacterString(self.active_text.clone()))
336            }
337            p if p == PropertyIdentifier::INACTIVE_TEXT => {
338                Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
339            }
340            p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
341                unused_bits: 5,
342                data: vec![self.event_detector.event_enable << 5],
343            }),
344            p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
345                unused_bits: 5,
346                data: vec![self.event_detector.acked_transitions << 5],
347            }),
348            p if p == PropertyIdentifier::NOTIFICATION_CLASS => Ok(PropertyValue::Unsigned(
349                self.event_detector.notification_class as u64,
350            )),
351            p if p == PropertyIdentifier::EVENT_TIME_STAMPS => Ok(PropertyValue::List(vec![
352                PropertyValue::Unsigned(0),
353                PropertyValue::Unsigned(0),
354                PropertyValue::Unsigned(0),
355            ])),
356            _ => Err(common::unknown_property_error()),
357        }
358    }
359
360    fn write_property(
361        &mut self,
362        property: PropertyIdentifier,
363        array_index: Option<u32>,
364        value: PropertyValue,
365        priority: Option<u8>,
366    ) -> Result<(), Error> {
367        common::write_priority_array_direct!(self, property, array_index, value, |v| {
368            if let PropertyValue::Enumerated(e) = v {
369                if e > 1 {
370                    Err(common::value_out_of_range_error())
371                } else {
372                    Ok(e)
373                }
374            } else {
375                Err(common::invalid_data_type_error())
376            }
377        });
378        if property == PropertyIdentifier::PRESENT_VALUE {
379            return common::write_priority_array!(self, value, priority, |v| {
380                if let PropertyValue::Enumerated(e) = v {
381                    if e > 1 {
382                        Err(common::value_out_of_range_error())
383                    } else {
384                        Ok(e)
385                    }
386                } else {
387                    Err(common::invalid_data_type_error())
388                }
389            });
390        }
391        if property == PropertyIdentifier::ACTIVE_TEXT {
392            if let PropertyValue::CharacterString(s) = value {
393                self.active_text = s;
394                return Ok(());
395            }
396            return Err(common::invalid_data_type_error());
397        }
398        if property == PropertyIdentifier::INACTIVE_TEXT {
399            if let PropertyValue::CharacterString(s) = value {
400                self.inactive_text = s;
401                return Ok(());
402            }
403            return Err(common::invalid_data_type_error());
404        }
405        if let Some(result) =
406            common::write_out_of_service(&mut self.out_of_service, property, &value)
407        {
408            return result;
409        }
410        if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
411            return result;
412        }
413        if let Some(result) = common::write_description(&mut self.description, property, &value) {
414            return result;
415        }
416        Err(common::write_access_denied_error())
417    }
418
419    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
420        static PROPS: &[PropertyIdentifier] = &[
421            PropertyIdentifier::OBJECT_IDENTIFIER,
422            PropertyIdentifier::OBJECT_NAME,
423            PropertyIdentifier::DESCRIPTION,
424            PropertyIdentifier::OBJECT_TYPE,
425            PropertyIdentifier::PRESENT_VALUE,
426            PropertyIdentifier::STATUS_FLAGS,
427            PropertyIdentifier::EVENT_STATE,
428            PropertyIdentifier::OUT_OF_SERVICE,
429            PropertyIdentifier::PRIORITY_ARRAY,
430            PropertyIdentifier::RELINQUISH_DEFAULT,
431            PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
432            PropertyIdentifier::POLARITY,
433            PropertyIdentifier::RELIABILITY,
434            PropertyIdentifier::ACTIVE_TEXT,
435            PropertyIdentifier::INACTIVE_TEXT,
436        ];
437        Cow::Borrowed(PROPS)
438    }
439}
440
441// ---------------------------------------------------------------------------
442// BinaryValue (type 5)
443// ---------------------------------------------------------------------------
444
445/// BACnet Binary Value object.
446///
447/// Commandable binary value with 16-level priority array.
448/// Uses Enumerated values: 0 = inactive, 1 = active.
449pub struct BinaryValueObject {
450    oid: ObjectIdentifier,
451    name: String,
452    description: String,
453    present_value: u32, // 0 = inactive, 1 = active
454    out_of_service: bool,
455    status_flags: StatusFlags,
456    priority_array: [Option<u32>; 16],
457    relinquish_default: u32,
458    /// Reliability: 0 = NO_FAULT_DETECTED.
459    reliability: u32,
460    active_text: String,
461    inactive_text: String,
462    /// CHANGE_OF_STATE event detector.
463    event_detector: ChangeOfStateDetector,
464    /// Value source tracking (optional per spec — exposed via VALUE_SOURCE property).
465    value_source: common::ValueSourceTracking,
466}
467
468impl BinaryValueObject {
469    /// Create a new Binary Value object.
470    pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
471        let oid = ObjectIdentifier::new(ObjectType::BINARY_VALUE, instance)?;
472        Ok(Self {
473            oid,
474            name: name.into(),
475            description: String::new(),
476            present_value: 0, // inactive
477            out_of_service: false,
478            status_flags: StatusFlags::empty(),
479            priority_array: [None; 16],
480            relinquish_default: 0,
481            reliability: 0,
482            active_text: "Active".into(),
483            inactive_text: "Inactive".into(),
484            event_detector: ChangeOfStateDetector::default(),
485            value_source: common::ValueSourceTracking::default(),
486        })
487    }
488
489    /// Set the description string.
490    pub fn set_description(&mut self, desc: impl Into<String>) {
491        self.description = desc.into();
492    }
493
494    fn recalculate_present_value(&mut self) {
495        self.present_value =
496            common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
497    }
498}
499
500impl BACnetObject for BinaryValueObject {
501    fn object_identifier(&self) -> ObjectIdentifier {
502        self.oid
503    }
504
505    fn object_name(&self) -> &str {
506        &self.name
507    }
508
509    fn supports_cov(&self) -> bool {
510        true
511    }
512
513    fn evaluate_intrinsic_reporting(&mut self) -> Option<EventStateChange> {
514        self.event_detector.evaluate(self.present_value)
515    }
516
517    fn read_property(
518        &self,
519        property: PropertyIdentifier,
520        array_index: Option<u32>,
521    ) -> Result<PropertyValue, Error> {
522        if property == PropertyIdentifier::STATUS_FLAGS {
523            return Ok(common::compute_status_flags(
524                self.status_flags,
525                self.reliability,
526                self.out_of_service,
527                self.event_detector.event_state.to_raw(),
528            ));
529        }
530        if let Some(result) = read_common_properties!(self, property, array_index) {
531            return result;
532        }
533        match property {
534            p if p == PropertyIdentifier::OBJECT_TYPE => {
535                Ok(PropertyValue::Enumerated(ObjectType::BINARY_VALUE.to_raw()))
536            }
537            p if p == PropertyIdentifier::PRESENT_VALUE => {
538                Ok(PropertyValue::Enumerated(self.present_value))
539            }
540            p if p == PropertyIdentifier::EVENT_STATE => Ok(PropertyValue::Enumerated(
541                self.event_detector.event_state.to_raw(),
542            )),
543            p if p == PropertyIdentifier::PRIORITY_ARRAY => {
544                common::read_priority_array!(self, array_index, PropertyValue::Enumerated)
545            }
546            p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
547                Ok(PropertyValue::Enumerated(self.relinquish_default))
548            }
549            p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
550                Ok(common::current_command_priority(&self.priority_array))
551            }
552            p if p == PropertyIdentifier::VALUE_SOURCE => {
553                Ok(self.value_source.value_source.clone())
554            }
555            p if p == PropertyIdentifier::LAST_COMMAND_TIME => Ok(PropertyValue::Unsigned(
556                match self.value_source.last_command_time {
557                    BACnetTimeStamp::SequenceNumber(n) => n,
558                    _ => 0,
559                },
560            )),
561            p if p == PropertyIdentifier::ACTIVE_TEXT => {
562                Ok(PropertyValue::CharacterString(self.active_text.clone()))
563            }
564            p if p == PropertyIdentifier::INACTIVE_TEXT => {
565                Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
566            }
567            p if p == PropertyIdentifier::EVENT_ENABLE => Ok(PropertyValue::BitString {
568                unused_bits: 5,
569                data: vec![self.event_detector.event_enable << 5],
570            }),
571            p if p == PropertyIdentifier::ACKED_TRANSITIONS => Ok(PropertyValue::BitString {
572                unused_bits: 5,
573                data: vec![self.event_detector.acked_transitions << 5],
574            }),
575            p if p == PropertyIdentifier::NOTIFICATION_CLASS => Ok(PropertyValue::Unsigned(
576                self.event_detector.notification_class as u64,
577            )),
578            p if p == PropertyIdentifier::EVENT_TIME_STAMPS => Ok(PropertyValue::List(vec![
579                PropertyValue::Unsigned(0),
580                PropertyValue::Unsigned(0),
581                PropertyValue::Unsigned(0),
582            ])),
583            _ => Err(common::unknown_property_error()),
584        }
585    }
586
587    fn write_property(
588        &mut self,
589        property: PropertyIdentifier,
590        array_index: Option<u32>,
591        value: PropertyValue,
592        priority: Option<u8>,
593    ) -> Result<(), Error> {
594        common::write_priority_array_direct!(self, property, array_index, value, |v| {
595            if let PropertyValue::Enumerated(e) = v {
596                if e > 1 {
597                    Err(common::value_out_of_range_error())
598                } else {
599                    Ok(e)
600                }
601            } else {
602                Err(common::invalid_data_type_error())
603            }
604        });
605        if property == PropertyIdentifier::PRESENT_VALUE {
606            return common::write_priority_array!(self, value, priority, |v| {
607                if let PropertyValue::Enumerated(e) = v {
608                    if e > 1 {
609                        Err(common::value_out_of_range_error())
610                    } else {
611                        Ok(e)
612                    }
613                } else {
614                    Err(common::invalid_data_type_error())
615                }
616            });
617        }
618        if property == PropertyIdentifier::ACTIVE_TEXT {
619            if let PropertyValue::CharacterString(s) = value {
620                self.active_text = s;
621                return Ok(());
622            }
623            return Err(common::invalid_data_type_error());
624        }
625        if property == PropertyIdentifier::INACTIVE_TEXT {
626            if let PropertyValue::CharacterString(s) = value {
627                self.inactive_text = s;
628                return Ok(());
629            }
630            return Err(common::invalid_data_type_error());
631        }
632        if let Some(result) =
633            common::write_out_of_service(&mut self.out_of_service, property, &value)
634        {
635            return result;
636        }
637        if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
638            return result;
639        }
640        if let Some(result) = common::write_description(&mut self.description, property, &value) {
641            return result;
642        }
643        Err(common::write_access_denied_error())
644    }
645
646    fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
647        static PROPS: &[PropertyIdentifier] = &[
648            PropertyIdentifier::OBJECT_IDENTIFIER,
649            PropertyIdentifier::OBJECT_NAME,
650            PropertyIdentifier::DESCRIPTION,
651            PropertyIdentifier::OBJECT_TYPE,
652            PropertyIdentifier::PRESENT_VALUE,
653            PropertyIdentifier::STATUS_FLAGS,
654            PropertyIdentifier::EVENT_STATE,
655            PropertyIdentifier::OUT_OF_SERVICE,
656            PropertyIdentifier::PRIORITY_ARRAY,
657            PropertyIdentifier::RELINQUISH_DEFAULT,
658            PropertyIdentifier::CURRENT_COMMAND_PRIORITY,
659            PropertyIdentifier::RELIABILITY,
660            PropertyIdentifier::ACTIVE_TEXT,
661            PropertyIdentifier::INACTIVE_TEXT,
662        ];
663        Cow::Borrowed(PROPS)
664    }
665}
666
667#[cfg(test)]
668mod tests;