Skip to main content

bacnet_types/
primitives.rs

1//! BACnet primitive data types per ASHRAE 135-2020 Clause 20.2.
2//!
3//! Core types used throughout the protocol: [`ObjectIdentifier`], [`Date`],
4//! [`Time`], and the [`PropertyValue`] sum type.
5
6#[cfg(not(feature = "std"))]
7use alloc::{string::String, vec::Vec};
8
9use crate::enums::ObjectType;
10use crate::error::Error;
11
12// ---------------------------------------------------------------------------
13// ObjectIdentifier (Clause 20.2.14)
14// ---------------------------------------------------------------------------
15
16/// BACnet Object Identifier: 10-bit type + 22-bit instance number.
17///
18/// Uniquely identifies a BACnet object within a device. Encoded as a
19/// 4-byte big-endian value: `(object_type << 22) | instance_number`.
20///
21/// The all-ones instance value [`WILDCARD_INSTANCE`](Self::WILDCARD_INSTANCE)
22/// is still a valid 22-bit wire value, and some service contexts give it
23/// wildcard meaning. Use [`ObjectIdentifier::new`] or [`ObjectIdentifier::decode`]
24/// when preserving wire-level values. Use [`ObjectIdentifier::new_addressable`]
25/// when constructing identifiers for concrete objects that can be stored,
26/// addressed, or compared as actual object instances.
27#[derive(Clone, Copy, PartialEq, Eq, Hash)]
28pub struct ObjectIdentifier {
29    object_type: ObjectType,
30    instance_number: u32,
31}
32
33impl ObjectIdentifier {
34    /// Maximum encodable instance number (2^22 - 1 = 4,194,303).
35    ///
36    /// This is also [`WILDCARD_INSTANCE`](Self::WILDCARD_INSTANCE). It is
37    /// accepted by [`ObjectIdentifier::new`] and [`ObjectIdentifier::decode`]
38    /// for wire compatibility, but callers that need a concrete addressable
39    /// object should use [`MAX_ADDRESSABLE_INSTANCE`](Self::MAX_ADDRESSABLE_INSTANCE)
40    /// or [`ObjectIdentifier::new_addressable`].
41    pub const MAX_INSTANCE: u32 = 0x3F_FFFF;
42
43    /// The reserved wildcard instance number (4,194,303).
44    ///
45    /// BACnet service clauses assign this value special "any/local instance"
46    /// meaning in selected contexts, for example Who-Is/Who-Am-I discovery and
47    /// Device/Network Port object access. It is not a concrete object instance
48    /// for addressable-object storage.
49    pub const WILDCARD_INSTANCE: u32 = Self::MAX_INSTANCE;
50
51    /// Maximum instance number for a concrete addressable object (4,194,302).
52    ///
53    /// This excludes [`WILDCARD_INSTANCE`](Self::WILDCARD_INSTANCE).
54    pub const MAX_ADDRESSABLE_INSTANCE: u32 = Self::WILDCARD_INSTANCE - 1;
55
56    /// Create a new ObjectIdentifier from any valid 22-bit wire instance.
57    ///
58    /// This constructor preserves [`WILDCARD_INSTANCE`](Self::WILDCARD_INSTANCE)
59    /// because it is a valid encoded value in BACnet service payloads. Use
60    /// [`ObjectIdentifier::new_addressable`] when the identifier must name a
61    /// concrete object instance.
62    ///
63    /// # Errors
64    /// Returns `Err` if `instance_number` exceeds [`MAX_INSTANCE`](Self::MAX_INSTANCE).
65    pub fn new(object_type: ObjectType, instance_number: u32) -> Result<Self, Error> {
66        if instance_number > Self::MAX_INSTANCE {
67            return Err(Error::OutOfRange(alloc_or_std_format!(
68                "instance number {} exceeds max {}",
69                instance_number,
70                Self::MAX_INSTANCE
71            )));
72        }
73        Ok(Self {
74            object_type,
75            instance_number,
76        })
77    }
78
79    /// Create a new ObjectIdentifier for a concrete addressable object.
80    ///
81    /// This rejects [`WILDCARD_INSTANCE`](Self::WILDCARD_INSTANCE), while
82    /// [`ObjectIdentifier::new`] keeps accepting that value for wire-level
83    /// round trips and BACnet service contexts where it is meaningful.
84    ///
85    /// # Errors
86    /// Returns `Err` if `instance_number` exceeds
87    /// [`MAX_INSTANCE`](Self::MAX_INSTANCE) or equals
88    /// [`WILDCARD_INSTANCE`](Self::WILDCARD_INSTANCE).
89    pub fn new_addressable(object_type: ObjectType, instance_number: u32) -> Result<Self, Error> {
90        if instance_number > Self::MAX_INSTANCE {
91            return Err(Error::OutOfRange(alloc_or_std_format!(
92                "instance number {} exceeds max {}",
93                instance_number,
94                Self::MAX_INSTANCE
95            )));
96        }
97        if instance_number == Self::WILDCARD_INSTANCE {
98            return Err(Error::OutOfRange(alloc_or_std_format!(
99                "instance number {} is reserved as the wildcard value; maximum addressable instance is {}",
100                instance_number,
101                Self::MAX_ADDRESSABLE_INSTANCE
102            )));
103        }
104        Ok(Self {
105            object_type,
106            instance_number,
107        })
108    }
109
110    /// Create without validation. Caller must ensure instance <= MAX_INSTANCE.
111    pub const fn new_unchecked(object_type: ObjectType, instance_number: u32) -> Self {
112        Self {
113            object_type,
114            instance_number,
115        }
116    }
117
118    /// The object type.
119    pub const fn object_type(&self) -> ObjectType {
120        self.object_type
121    }
122
123    /// The instance number (0 to 4,194,303).
124    ///
125    /// Values returned from wire decoding may equal
126    /// [`WILDCARD_INSTANCE`](Self::WILDCARD_INSTANCE). Treat that value as a
127    /// concrete addressable object only after the relevant service context has
128    /// resolved its wildcard semantics.
129    pub const fn instance_number(&self) -> u32 {
130        self.instance_number
131    }
132
133    /// Encode to the 4-byte BACnet wire format (big-endian).
134    pub fn encode(&self) -> [u8; 4] {
135        debug_assert!(
136            self.object_type.to_raw() <= 0x3FF,
137            "ObjectType {} exceeds 10-bit field",
138            self.object_type.to_raw()
139        );
140        debug_assert!(
141            self.instance_number <= Self::MAX_INSTANCE,
142            "Instance {} exceeds MAX_INSTANCE",
143            self.instance_number
144        );
145        let value = ((self.object_type.to_raw() & 0x3FF) << 22)
146            | (self.instance_number & Self::MAX_INSTANCE);
147        value.to_be_bytes()
148    }
149
150    /// Decode from the 4-byte BACnet wire format (big-endian).
151    pub fn decode(data: &[u8]) -> Result<Self, Error> {
152        if data.len() != 4 {
153            return Err(Error::decoding(
154                0,
155                format!(
156                    "ObjectIdentifier expects exactly 4 bytes, got {}",
157                    data.len()
158                ),
159            ));
160        }
161        let value = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
162        let type_raw = (value >> 22) & 0x3FF;
163        let instance = value & Self::MAX_INSTANCE;
164        Ok(Self {
165            object_type: ObjectType::from_raw(type_raw),
166            instance_number: instance,
167        })
168    }
169}
170
171impl core::fmt::Debug for ObjectIdentifier {
172    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
173        write!(
174            f,
175            "ObjectIdentifier({:?}, {})",
176            self.object_type, self.instance_number
177        )
178    }
179}
180
181impl core::fmt::Display for ObjectIdentifier {
182    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
183        write!(f, "{},{}", self.object_type, self.instance_number)
184    }
185}
186
187// ---------------------------------------------------------------------------
188// Date (Clause 20.2.12)
189// ---------------------------------------------------------------------------
190
191/// BACnet Date: year, month, day, day-of-week.
192///
193/// - Year: 0-254 relative to 1900 (0xFF = unspecified)
194/// - Month: 1-14 (13=odd, 14=even, 0xFF=unspecified)
195/// - Day: 1-34 (32=last, 33=odd, 34=even, 0xFF=unspecified)
196/// - Day of week: 1=Monday..7=Sunday (0xFF=unspecified)
197#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
198pub struct Date {
199    /// Year minus 1900 (0-254, or 0xFF for unspecified).
200    pub year: u8,
201    /// Month (1-14, or 0xFF for unspecified).
202    pub month: u8,
203    /// Day of month (1-34, or 0xFF for unspecified).
204    pub day: u8,
205    /// Day of week (1=Monday..7=Sunday, or 0xFF for unspecified).
206    pub day_of_week: u8,
207}
208
209impl Date {
210    /// Value indicating "unspecified" for any date field.
211    pub const UNSPECIFIED: u8 = 0xFF;
212
213    /// Encode to 4 bytes.
214    pub fn encode(&self) -> [u8; 4] {
215        [self.year, self.month, self.day, self.day_of_week]
216    }
217
218    /// Decode from 4 bytes.
219    pub fn decode(data: &[u8]) -> Result<Self, Error> {
220        if data.len() != 4 {
221            return Err(Error::decoding(
222                0,
223                format!("Date expects exactly 4 bytes, got {}", data.len()),
224            ));
225        }
226        Ok(Self {
227            year: data[0],
228            month: data[1],
229            day: data[2],
230            day_of_week: data[3],
231        })
232    }
233
234    /// Get the actual year (1900 + year field), or None if unspecified.
235    pub fn actual_year(&self) -> Option<u16> {
236        if self.year == Self::UNSPECIFIED {
237            None
238        } else {
239            Some(1900 + self.year as u16)
240        }
241    }
242}
243
244// ---------------------------------------------------------------------------
245// Time (Clause 20.2.13)
246// ---------------------------------------------------------------------------
247
248/// BACnet Time: hour, minute, second, hundredths.
249///
250/// Each field can be 0xFF for "unspecified".
251#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
252pub struct Time {
253    /// Hour (0-23, or 0xFF for unspecified).
254    pub hour: u8,
255    /// Minute (0-59, or 0xFF for unspecified).
256    pub minute: u8,
257    /// Second (0-59, or 0xFF for unspecified).
258    pub second: u8,
259    /// Hundredths of a second (0-99, or 0xFF for unspecified).
260    pub hundredths: u8,
261}
262
263impl Time {
264    /// Value indicating "unspecified" for any time field.
265    pub const UNSPECIFIED: u8 = 0xFF;
266
267    /// Encode to 4 bytes.
268    pub fn encode(&self) -> [u8; 4] {
269        [self.hour, self.minute, self.second, self.hundredths]
270    }
271
272    /// Decode from 4 bytes.
273    pub fn decode(data: &[u8]) -> Result<Self, Error> {
274        if data.len() != 4 {
275            return Err(Error::decoding(
276                0,
277                format!("Time expects exactly 4 bytes, got {}", data.len()),
278            ));
279        }
280        Ok(Self {
281            hour: data[0],
282            minute: data[1],
283            second: data[2],
284            hundredths: data[3],
285        })
286    }
287}
288
289// ---------------------------------------------------------------------------
290// BACnetTimeStamp (Clause 20.2.1.5)
291// ---------------------------------------------------------------------------
292
293/// BACnet timestamp -- a CHOICE of Time, sequence number, or DateTime.
294#[derive(Debug, Clone, PartialEq)]
295pub enum BACnetTimeStamp {
296    /// Context tag 0: Time
297    Time(Time),
298    /// Context tag 1: Unsigned (sequence number)
299    SequenceNumber(u64),
300    /// Context tag 2: BACnetDateTime (Date + Time)
301    DateTime { date: Date, time: Time },
302}
303
304// ---------------------------------------------------------------------------
305// StatusFlags (Clause 12.X -- used by many object types)
306// ---------------------------------------------------------------------------
307
308bitflags::bitflags! {
309    /// BACnet StatusFlags -- 4-bit bitstring present on most objects.
310    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
311    pub struct StatusFlags: u8 {
312        const IN_ALARM = 0b1000;
313        const FAULT = 0b0100;
314        const OVERRIDDEN = 0b0010;
315        const OUT_OF_SERVICE = 0b0001;
316    }
317}
318
319bitflags::bitflags! {
320    /// BACnet DaysOfWeek -- 7-bit bitstring (Clause 21).
321    ///
322    /// Bit 0 (MSB=0x40) = Monday, Bit 6 (0x01) = Sunday.
323    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
324    pub struct DaysOfWeek: u8 {
325        const MONDAY    = 0b0100_0000;
326        const TUESDAY   = 0b0010_0000;
327        const WEDNESDAY = 0b0001_0000;
328        const THURSDAY  = 0b0000_1000;
329        const FRIDAY    = 0b0000_0100;
330        const SATURDAY  = 0b0000_0010;
331        const SUNDAY    = 0b0000_0001;
332        const ALL       = 0b0111_1111;
333    }
334}
335
336// ---------------------------------------------------------------------------
337// PropertyValue -- sum type for BACnet property values
338// ---------------------------------------------------------------------------
339
340/// A BACnet application-layer value.
341///
342/// This enum covers all primitive value types that can appear as property
343/// values in BACnet objects. Constructed types (lists, sequences) are
344/// represented as nested structures.
345#[derive(Debug, Clone, PartialEq)]
346pub enum PropertyValue {
347    /// Null value.
348    Null,
349    /// Boolean value.
350    Boolean(bool),
351    /// Unsigned integer (up to 64-bit for BACnet Unsigned64).
352    Unsigned(u64),
353    /// Signed integer.
354    Signed(i32),
355    /// IEEE 754 single-precision float.
356    Real(f32),
357    /// IEEE 754 double-precision float.
358    Double(f64),
359    /// Octet string (raw bytes).
360    OctetString(Vec<u8>),
361    /// Character string (UTF-8).
362    CharacterString(String),
363    /// Bit string (variable length).
364    BitString {
365        /// Number of unused bits in the last byte.
366        unused_bits: u8,
367        /// The bit data bytes.
368        data: Vec<u8>,
369    },
370    /// Enumerated value.
371    Enumerated(u32),
372    /// Date value.
373    Date(Date),
374    /// Time value.
375    Time(Time),
376    /// Object identifier.
377    ObjectIdentifier(ObjectIdentifier),
378    /// A sequence (array) of property values.
379    ///
380    /// Used when reading an entire array property with `arrayIndex` absent
381    /// (Clause 15.5.1). Each element is encoded as its own application-tagged
382    /// value, concatenated in order.
383    List(Vec<PropertyValue>),
384}
385
386// ---------------------------------------------------------------------------
387// Formatting helper macro (works in both std and no_std+alloc)
388// ---------------------------------------------------------------------------
389
390/// Format a string using either std or alloc.
391#[cfg(feature = "std")]
392macro_rules! alloc_or_std_format {
393    ($($arg:tt)*) => { format!($($arg)*) }
394}
395
396#[cfg(not(feature = "std"))]
397macro_rules! alloc_or_std_format {
398    ($($arg:tt)*) => { alloc::format!($($arg)*) }
399}
400
401use alloc_or_std_format;
402
403// ---------------------------------------------------------------------------
404// Tests
405// ---------------------------------------------------------------------------
406
407#[cfg(test)]
408mod tests {
409    use super::*;
410
411    #[test]
412    fn object_identifier_encode_decode_round_trip() {
413        let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
414        let bytes = oid.encode();
415        let decoded = ObjectIdentifier::decode(&bytes).unwrap();
416        assert_eq!(oid, decoded);
417    }
418
419    #[test]
420    fn object_identifier_wire_format() {
421        // AnalogInput (type=0) instance=1: (0 << 22) | 1 = 0x00000001
422        let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
423        assert_eq!(oid.encode(), [0x00, 0x00, 0x00, 0x01]);
424
425        // Device (type=8) instance=1234: (8 << 22) | 1234 = 0x020004D2
426        let oid = ObjectIdentifier::new(ObjectType::DEVICE, 1234).unwrap();
427        let expected = ((8u32 << 22) | 1234u32).to_be_bytes();
428        assert_eq!(oid.encode(), expected);
429    }
430
431    #[test]
432    fn object_identifier_max_instance() {
433        let oid =
434            ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE).unwrap();
435        assert_eq!(oid.instance_number(), 0x3F_FFFF);
436        let bytes = oid.encode();
437        let decoded = ObjectIdentifier::decode(&bytes).unwrap();
438        assert_eq!(decoded.instance_number(), 0x3F_FFFF);
439    }
440
441    #[test]
442    fn object_identifier_invalid_instance() {
443        let result = ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE + 1);
444        assert!(result.is_err());
445    }
446
447    #[test]
448    fn object_identifier_buffer_too_short() {
449        let result = ObjectIdentifier::decode(&[0x00, 0x00]);
450        assert!(result.is_err());
451    }
452
453    #[test]
454    fn object_identifier_overlong_errors() {
455        assert!(ObjectIdentifier::decode(&[0x00, 0x00, 0x00, 0x01, 0xFF]).is_err());
456    }
457
458    #[test]
459    fn date_encode_decode_round_trip() {
460        let date = Date {
461            year: 124, // 2024
462            month: 6,
463            day: 15,
464            day_of_week: 6, // Saturday
465        };
466        let bytes = date.encode();
467        let decoded = Date::decode(&bytes).unwrap();
468        assert_eq!(date, decoded);
469        assert_eq!(decoded.actual_year(), Some(2024));
470    }
471
472    #[test]
473    fn date_unspecified_year() {
474        let date = Date {
475            year: Date::UNSPECIFIED,
476            month: 1,
477            day: 1,
478            day_of_week: Date::UNSPECIFIED,
479        };
480        assert_eq!(date.actual_year(), None);
481    }
482
483    #[test]
484    fn date_overlong_errors() {
485        assert!(Date::decode(&[124, 6, 15, 6, 0]).is_err());
486    }
487
488    #[test]
489    fn time_encode_decode_round_trip() {
490        let time = Time {
491            hour: 14,
492            minute: 30,
493            second: 45,
494            hundredths: 50,
495        };
496        let bytes = time.encode();
497        let decoded = Time::decode(&bytes).unwrap();
498        assert_eq!(time, decoded);
499    }
500
501    #[test]
502    fn time_overlong_errors() {
503        assert!(Time::decode(&[14, 30, 45, 50, 0]).is_err());
504    }
505
506    #[test]
507    fn status_flags_operations() {
508        let flags = StatusFlags::IN_ALARM | StatusFlags::OUT_OF_SERVICE;
509        assert!(flags.contains(StatusFlags::IN_ALARM));
510        assert!(flags.contains(StatusFlags::OUT_OF_SERVICE));
511        assert!(!flags.contains(StatusFlags::FAULT));
512        assert!(!flags.contains(StatusFlags::OVERRIDDEN));
513    }
514
515    // --- OID edge cases ---
516
517    #[test]
518    fn object_identifier_instance_zero() {
519        // Instance 0 is valid and commonly used (e.g., Device,0)
520        let oid = ObjectIdentifier::new(ObjectType::DEVICE, 0).unwrap();
521        assert_eq!(oid.instance_number(), 0);
522        let bytes = oid.encode();
523        let decoded = ObjectIdentifier::decode(&bytes).unwrap();
524        assert_eq!(decoded.instance_number(), 0);
525        assert_eq!(decoded.object_type(), ObjectType::DEVICE);
526    }
527
528    #[test]
529    fn object_identifier_all_types_instance_zero() {
530        // Instance 0 for each common type should round-trip correctly
531        for type_raw in [0u32, 1, 2, 3, 4, 5, 6, 8, 10, 13, 14, 17, 19] {
532            let obj_type = ObjectType::from_raw(type_raw);
533            let oid = ObjectIdentifier::new(obj_type, 0).unwrap();
534            let bytes = oid.encode();
535            let decoded = ObjectIdentifier::decode(&bytes).unwrap();
536            assert_eq!(decoded.object_type(), obj_type, "type {type_raw} failed");
537            assert_eq!(
538                decoded.instance_number(),
539                0,
540                "type {type_raw} instance failed"
541            );
542        }
543    }
544
545    #[test]
546    fn object_identifier_wildcard_instance() {
547        let oid =
548            ObjectIdentifier::new(ObjectType::DEVICE, ObjectIdentifier::WILDCARD_INSTANCE).unwrap();
549        assert_eq!(oid.instance_number(), ObjectIdentifier::MAX_INSTANCE);
550        let bytes = oid.encode();
551        let decoded = ObjectIdentifier::decode(&bytes).unwrap();
552        assert_eq!(decoded.instance_number(), ObjectIdentifier::MAX_INSTANCE);
553
554        let raw_wire_value = ((ObjectType::DEVICE.to_raw() << 22)
555            | ObjectIdentifier::WILDCARD_INSTANCE)
556            .to_be_bytes();
557        let decoded = ObjectIdentifier::decode(&raw_wire_value).unwrap();
558        assert_eq!(decoded.object_type(), ObjectType::DEVICE);
559        assert_eq!(
560            decoded.instance_number(),
561            ObjectIdentifier::WILDCARD_INSTANCE
562        );
563    }
564
565    #[test]
566    fn object_identifier_new_addressable_rejects_wildcard() {
567        assert_eq!(
568            ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
569            ObjectIdentifier::WILDCARD_INSTANCE - 1
570        );
571
572        let oid = ObjectIdentifier::new_addressable(
573            ObjectType::DEVICE,
574            ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
575        )
576        .unwrap();
577        assert_eq!(
578            oid.instance_number(),
579            ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE
580        );
581
582        let analog_input = ObjectIdentifier::new_addressable(
583            ObjectType::ANALOG_INPUT,
584            ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE,
585        )
586        .unwrap();
587        assert_eq!(analog_input.object_type(), ObjectType::ANALOG_INPUT);
588        assert_eq!(
589            analog_input.instance_number(),
590            ObjectIdentifier::MAX_ADDRESSABLE_INSTANCE
591        );
592
593        let wildcard =
594            ObjectIdentifier::new_addressable(ObjectType::DEVICE, ObjectIdentifier::MAX_INSTANCE)
595                .unwrap_err();
596        assert!(wildcard.to_string().contains("wildcard"));
597
598        let too_large = ObjectIdentifier::new_addressable(
599            ObjectType::DEVICE,
600            ObjectIdentifier::MAX_INSTANCE + 1,
601        );
602        assert!(too_large.is_err());
603    }
604
605    #[test]
606    fn object_identifier_decode_extra_bytes_errors() {
607        let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 42).unwrap();
608        let mut bytes = oid.encode().to_vec();
609        bytes.extend_from_slice(&[0xFF, 0xFF]);
610        assert!(ObjectIdentifier::decode(&bytes).is_err());
611    }
612
613    #[test]
614    #[cfg_attr(debug_assertions, should_panic(expected = "exceeds 10-bit field"))]
615    fn object_identifier_type_overflow_round_trip() {
616        // In debug builds, encode() asserts type <= 1023.
617        // In release builds, types > 1023 are silently masked to 10 bits.
618        let oid = ObjectIdentifier::new_unchecked(ObjectType::from_raw(1024), 0);
619        let bytes = oid.encode();
620        let decoded = ObjectIdentifier::decode(&bytes).unwrap();
621        assert_eq!(decoded.object_type(), ObjectType::from_raw(0));
622    }
623
624    #[test]
625    fn property_value_variants() {
626        let null = PropertyValue::Null;
627        let boolean = PropertyValue::Boolean(true);
628        let real = PropertyValue::Real(72.5);
629        let string = PropertyValue::CharacterString("test".into());
630
631        assert_eq!(null, PropertyValue::Null);
632        assert_eq!(boolean, PropertyValue::Boolean(true));
633        assert_ne!(real, PropertyValue::Real(73.0));
634        assert_eq!(string, PropertyValue::CharacterString("test".into()));
635    }
636}