Skip to main content

zerodds_types/dynamic/
data.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! DynamicData (XTypes 1.3 §7.5.6).
4//!
5//! `DynamicData` is a data instance of a `DynamicType`. The API
6//! is spec-frozen (method names + signatures). Relative to the spec,
7//! two simplifications are made:
8//!
9//! 1. All 12 typed getters/setters go through a `DynamicValue`
10//!    discriminator enum, by which the `set_<T>_value`/`get_<T>_value`
11//!    methods do their type check. Type mismatch → `BadParameter`.
12//! 2. Loans are modeled via a reference-counted `DataLoan`
13//!    (Spec §7.5.6.1 — the full loan API follows with C4.7).
14
15use alloc::boxed::Box;
16use alloc::collections::BTreeMap;
17use alloc::string::String;
18use alloc::vec::Vec;
19
20use super::descriptor::{MemberId, TypeKind};
21use super::error::DynamicError;
22use super::type_::DynamicType;
23
24// ----------------------------------------------------------------------
25// DynamicValue — internal storage enum
26// ----------------------------------------------------------------------
27
28/// Sum type for all values storable in DynamicData. Spec §7.5.6
29/// speaks of 12 primitive types + composite + sequence — this enum
30/// encapsulates the storage.
31#[derive(Debug, Clone)]
32pub enum DynamicValue {
33    /// `bool`.
34    Bool(bool),
35    /// 8-bit unsigned (`octet`).
36    Byte(u8),
37    /// `int8`.
38    Int8(i8),
39    /// `uint8`.
40    UInt8(u8),
41    /// `int16`.
42    Int16(i16),
43    /// `uint16`.
44    UInt16(u16),
45    /// `int32`.
46    Int32(i32),
47    /// `uint32`.
48    UInt32(u32),
49    /// `int64`.
50    Int64(i64),
51    /// `uint64`.
52    UInt64(u64),
53    /// `float`.
54    Float32(f32),
55    /// `double`.
56    Float64(f64),
57    /// `char`.
58    Char8(u8),
59    /// `wchar`.
60    Char16(u16),
61    /// `string`.
62    String(String),
63    /// `wstring` (UTF-16 as Vec<u16>).
64    WString(Vec<u16>),
65    /// Composite or sequence — embedded `DynamicData`.
66    Complex(Box<DynamicData>),
67    /// Sequence of DynamicData (spec: the element type is known from the
68    /// containerizing TypeDescriptor).
69    Sequence(Vec<DynamicData>),
70    /// Discard / not set (the default value is returned).
71    None,
72}
73
74impl PartialEq for DynamicValue {
75    fn eq(&self, other: &Self) -> bool {
76        match (self, other) {
77            (Self::Bool(a), Self::Bool(b)) => a == b,
78            (Self::Byte(a), Self::Byte(b)) => a == b,
79            (Self::Int8(a), Self::Int8(b)) => a == b,
80            (Self::UInt8(a), Self::UInt8(b)) => a == b,
81            (Self::Int16(a), Self::Int16(b)) => a == b,
82            (Self::UInt16(a), Self::UInt16(b)) => a == b,
83            (Self::Int32(a), Self::Int32(b)) => a == b,
84            (Self::UInt32(a), Self::UInt32(b)) => a == b,
85            (Self::Int64(a), Self::Int64(b)) => a == b,
86            (Self::UInt64(a), Self::UInt64(b)) => a == b,
87            (Self::Float32(a), Self::Float32(b)) => a.to_bits() == b.to_bits(),
88            (Self::Float64(a), Self::Float64(b)) => a.to_bits() == b.to_bits(),
89            (Self::Char8(a), Self::Char8(b)) => a == b,
90            (Self::Char16(a), Self::Char16(b)) => a == b,
91            (Self::String(a), Self::String(b)) => a == b,
92            (Self::WString(a), Self::WString(b)) => a == b,
93            (Self::Complex(a), Self::Complex(b)) => a.equals(b),
94            (Self::Sequence(a), Self::Sequence(b)) => {
95                a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| x.equals(y))
96            }
97            (Self::None, Self::None) => true,
98            _ => false,
99        }
100    }
101}
102
103impl DynamicValue {
104    fn kind_str(&self) -> &'static str {
105        match self {
106            Self::Bool(_) => "bool",
107            Self::Byte(_) => "byte",
108            Self::Int8(_) => "int8",
109            Self::UInt8(_) => "uint8",
110            Self::Int16(_) => "int16",
111            Self::UInt16(_) => "uint16",
112            Self::Int32(_) => "int32",
113            Self::UInt32(_) => "uint32",
114            Self::Int64(_) => "int64",
115            Self::UInt64(_) => "uint64",
116            Self::Float32(_) => "float32",
117            Self::Float64(_) => "float64",
118            Self::Char8(_) => "char8",
119            Self::Char16(_) => "char16",
120            Self::String(_) => "string",
121            Self::WString(_) => "wstring",
122            Self::Complex(_) => "complex",
123            Self::Sequence(_) => "sequence",
124            Self::None => "none",
125        }
126    }
127}
128
129// ----------------------------------------------------------------------
130// DataLoan — simple RAII wrapper
131// ----------------------------------------------------------------------
132
133/// Returns a loaned `DynamicData` view onto a member.
134/// Lifecycle: `loan_value` → `return_loaned_value` (Spec §7.5.6.1).
135///
136/// Minimal in phase 4 — the full loan refcount comes with C4.7.
137#[derive(Debug)]
138pub struct DataLoan {
139    member_id: MemberId,
140    /// Index into the internal loans array — invalidated on `return`.
141    handle: u32,
142}
143
144impl DataLoan {
145    /// Member id for which the loan was created.
146    #[must_use]
147    pub fn member_id(&self) -> MemberId {
148        self.member_id
149    }
150}
151
152// ----------------------------------------------------------------------
153// DynamicData
154// ----------------------------------------------------------------------
155
156/// XTypes 1.3 §7.5.6 DynamicData.
157///
158/// Data instance of a `DynamicType`. The type must be passed at
159/// construction (`DynamicDataFactory::create_data`); afterwards
160/// members can be read/written via `set_<T>_value(member_id, value)` /
161/// `get_<T>_value`.
162#[derive(Debug, Clone)]
163pub struct DynamicData {
164    type_: DynamicType,
165    values: BTreeMap<MemberId, DynamicValue>,
166    /// Active loans — for lifecycle validation.
167    active_loans: Vec<u32>,
168    /// Monotonically growing loan counter.
169    next_loan_handle: u32,
170}
171
172impl DynamicData {
173    pub(super) fn new(type_: DynamicType) -> Self {
174        Self {
175            type_,
176            values: BTreeMap::new(),
177            active_loans: Vec::new(),
178            next_loan_handle: 1,
179        }
180    }
181
182    /// Spec §7.5.6.2.1 `type()`.
183    #[must_use]
184    pub fn dynamic_type(&self) -> &DynamicType {
185        &self.type_
186    }
187
188    /// Spec §7.5.6.2.6 `equals(other)` — Deep.
189    #[must_use]
190    pub fn equals(&self, other: &Self) -> bool {
191        self.type_.equals(&other.type_) && self.values == other.values
192    }
193
194    /// Spec §7.5.6.2.4 `get_item_count()`.
195    #[must_use]
196    pub fn item_count(&self) -> u32 {
197        u32::try_from(self.values.len()).unwrap_or(u32::MAX)
198    }
199
200    /// Checks whether `member_id` is defined in the type.
201    fn validate_member(&self, member_id: MemberId) -> Result<(), DynamicError> {
202        if self.type_.member_by_id(member_id).is_none() {
203            return Err(DynamicError::bad_parameter(alloc::format!(
204                "no member with id {member_id} in type {}",
205                self.type_.name()
206            )));
207        }
208        Ok(())
209    }
210
211    /// General setter — type-safe via `DynamicValue` with
212    /// Spec §7.5.4.1.2 TryConstruct apply semantics (C4.7): if the
213    /// member type defines a `bound` and the value violates the bound
214    /// (too long a string, too long a sequence, mismatched
215    /// array length), the `try_construct` strategy decides:
216    /// `Discard` leaves the member unset, `UseDefault` sets the
217    /// `default_value`, `Trim` truncates to the bound.
218    fn set(&mut self, member_id: MemberId, value: DynamicValue) -> Result<(), DynamicError> {
219        self.validate_member(member_id)?;
220        let member = self.type_.member_by_id(member_id).cloned();
221        if let Some(ref m) = member {
222            check_value_kind_matches_type(&value, m.dynamic_type().kind())?;
223        }
224        // TryConstruct apply (Spec §7.5.4.1.2). Members without bounds:
225        // `apply_try_construct` returns `Accept(value)` unchanged.
226        if let Some(m) = member {
227            match crate::dynamic::try_construct::apply_try_construct(&m, value) {
228                crate::dynamic::TryConstructOutcome::Accept(v)
229                | crate::dynamic::TryConstructOutcome::UseDefault(v)
230                | crate::dynamic::TryConstructOutcome::Trim(v) => {
231                    self.values.insert(member_id, v);
232                }
233                crate::dynamic::TryConstructOutcome::Discard => {
234                    // Member stays unset (Spec §7.5.4.1.2 Discard).
235                    self.values.remove(&member_id);
236                }
237            }
238        } else {
239            self.values.insert(member_id, value);
240        }
241        Ok(())
242    }
243
244    /// Lese-Helper.
245    fn get(&self, member_id: MemberId) -> Result<&DynamicValue, DynamicError> {
246        self.values.get(&member_id).ok_or_else(|| {
247            DynamicError::PreconditionNotMet(alloc::format!(
248                "member {member_id} not set in {}",
249                self.type_.name()
250            ))
251        })
252    }
253
254    /// Generic read of a member's raw [`DynamicValue`], or `None` if unset.
255    ///
256    /// The reflective codec ([`crate::dynamic::codec`]) uses this to walk a
257    /// fully-populated value without the per-kind typed getters.
258    #[must_use]
259    pub fn get_value(&self, member_id: MemberId) -> Option<&DynamicValue> {
260        self.values.get(&member_id)
261    }
262
263    /// Insert a raw [`DynamicValue`] for `member_id` WITHOUT TryConstruct
264    /// bound-application — a faithful set for the reflective decoder, which
265    /// reconstructs exactly what was on the wire. Unknown ids are accepted
266    /// (the decoder only ever passes ids from the type's own members).
267    pub fn set_value_raw(&mut self, member_id: MemberId, value: DynamicValue) {
268        self.values.insert(member_id, value);
269    }
270
271    /// The member ids that currently carry a value, in member (index) order of
272    /// the type. Used by the reflective encoder to emit members deterministically.
273    #[must_use]
274    pub fn set_member_ids(&self) -> alloc::vec::Vec<MemberId> {
275        let mut ids: alloc::vec::Vec<MemberId> = self.values.keys().copied().collect();
276        // Order by the type's declared member index so encode order is stable.
277        ids.sort_by_key(|id| self.type_.member_by_id(*id).map_or(u32::MAX, |m| m.index()));
278        ids
279    }
280}
281
282// ----------------------------------------------------------------------
283// 12 Primitive Getters/Setters (Spec §7.5.6.3)
284// ----------------------------------------------------------------------
285
286macro_rules! primitive_accessor {
287    ($get_name:ident, $set_name:ident, $rust_ty:ty, $variant:ident) => {
288        impl DynamicData {
289            #[doc = concat!("Spec §7.5.6.3 `get_", stringify!($variant), "_value`.")]
290            ///
291            /// # Errors
292            /// `BadParameter` on an unknown id, `Inconsistent` on a
293            /// type mismatch.
294            pub fn $get_name(&self, member_id: MemberId) -> Result<$rust_ty, DynamicError> {
295                let v = self.get(member_id)?;
296                if let DynamicValue::$variant(x) = v {
297                    Ok(x.clone())
298                } else {
299                    Err(DynamicError::bad_parameter(alloc::format!(
300                        "type mismatch: expected {} got {}",
301                        stringify!($variant),
302                        v.kind_str()
303                    )))
304                }
305            }
306
307            #[doc = concat!("Spec §7.5.6.3 `set_", stringify!($variant), "_value`.")]
308            ///
309            /// # Errors
310            /// Type mismatch / unknown id.
311            pub fn $set_name(
312                &mut self,
313                member_id: MemberId,
314                value: $rust_ty,
315            ) -> Result<(), DynamicError> {
316                self.set(member_id, DynamicValue::$variant(value))
317            }
318        }
319    };
320}
321
322primitive_accessor!(get_boolean_value, set_boolean_value, bool, Bool);
323primitive_accessor!(get_byte_value, set_byte_value, u8, Byte);
324primitive_accessor!(get_int8_value, set_int8_value, i8, Int8);
325primitive_accessor!(get_uint8_value, set_uint8_value, u8, UInt8);
326primitive_accessor!(get_int16_value, set_int16_value, i16, Int16);
327primitive_accessor!(get_uint16_value, set_uint16_value, u16, UInt16);
328primitive_accessor!(get_int32_value, set_int32_value, i32, Int32);
329primitive_accessor!(get_uint32_value, set_uint32_value, u32, UInt32);
330primitive_accessor!(get_int64_value, set_int64_value, i64, Int64);
331primitive_accessor!(get_uint64_value, set_uint64_value, u64, UInt64);
332primitive_accessor!(get_float32_value, set_float32_value, f32, Float32);
333primitive_accessor!(get_float64_value, set_float64_value, f64, Float64);
334primitive_accessor!(get_char8_value, set_char8_value, u8, Char8);
335primitive_accessor!(get_char16_value, set_char16_value, u16, Char16);
336
337// String + WString separat — Clone-Semantik vs. Move.
338impl DynamicData {
339    /// Spec §7.5.6.3 `get_string_value`.
340    ///
341    /// # Errors
342    /// Type-Mismatch.
343    pub fn get_string_value(&self, member_id: MemberId) -> Result<String, DynamicError> {
344        let v = self.get(member_id)?;
345        if let DynamicValue::String(s) = v {
346            Ok(s.clone())
347        } else {
348            Err(DynamicError::bad_parameter(alloc::format!(
349                "type mismatch: expected string got {}",
350                v.kind_str()
351            )))
352        }
353    }
354
355    /// Spec §7.5.6.3 `set_string_value`.
356    ///
357    /// # Errors
358    /// Type mismatch / unknown id.
359    pub fn set_string_value(
360        &mut self,
361        member_id: MemberId,
362        value: impl Into<String>,
363    ) -> Result<(), DynamicError> {
364        self.set(member_id, DynamicValue::String(value.into()))
365    }
366
367    /// Spec §7.5.6.3 `get_wstring_value`.
368    ///
369    /// # Errors
370    /// Type-Mismatch.
371    pub fn get_wstring_value(&self, member_id: MemberId) -> Result<Vec<u16>, DynamicError> {
372        let v = self.get(member_id)?;
373        if let DynamicValue::WString(s) = v {
374            Ok(s.clone())
375        } else {
376            Err(DynamicError::bad_parameter(alloc::format!(
377                "type mismatch: expected wstring got {}",
378                v.kind_str()
379            )))
380        }
381    }
382
383    /// Spec §7.5.6.3 `set_wstring_value`.
384    ///
385    /// # Errors
386    /// Type mismatch / unknown id.
387    pub fn set_wstring_value(
388        &mut self,
389        member_id: MemberId,
390        value: Vec<u16>,
391    ) -> Result<(), DynamicError> {
392        self.set(member_id, DynamicValue::WString(value))
393    }
394}
395
396// ----------------------------------------------------------------------
397// Composite + Sequence (Spec §7.5.6.5)
398// ----------------------------------------------------------------------
399
400impl DynamicData {
401    /// Spec §7.5.6.5.1 `get_complex_value(member_id)`.
402    ///
403    /// Returns a read view onto the composite member.
404    ///
405    /// # Errors
406    /// Type mismatch or member not set.
407    pub fn get_complex_value(&self, member_id: MemberId) -> Result<&DynamicData, DynamicError> {
408        let v = self.get(member_id)?;
409        if let DynamicValue::Complex(d) = v {
410            Ok(d)
411        } else {
412            Err(DynamicError::bad_parameter(alloc::format!(
413                "type mismatch: expected complex got {}",
414                v.kind_str()
415            )))
416        }
417    }
418
419    /// Spec §7.5.6.5.2 `set_complex_value(member_id, data)`.
420    ///
421    /// # Errors
422    /// Type mismatch or member does not exist.
423    pub fn set_complex_value(
424        &mut self,
425        member_id: MemberId,
426        value: DynamicData,
427    ) -> Result<(), DynamicError> {
428        self.set(member_id, DynamicValue::Complex(Box::new(value)))
429    }
430
431    /// Returns the number of elements of a sequence member.
432    ///
433    /// # Errors
434    /// Type mismatch or member not set.
435    pub fn get_sequence_length(&self, member_id: MemberId) -> Result<u32, DynamicError> {
436        let v = self.get(member_id)?;
437        if let DynamicValue::Sequence(seq) = v {
438            Ok(u32::try_from(seq.len()).unwrap_or(u32::MAX))
439        } else {
440            Err(DynamicError::bad_parameter(alloc::format!(
441                "type mismatch: expected sequence got {}",
442                v.kind_str()
443            )))
444        }
445    }
446
447    /// Sets a sequence completely.
448    ///
449    /// # Errors
450    /// Type mismatch.
451    pub fn set_sequence_value(
452        &mut self,
453        member_id: MemberId,
454        value: Vec<DynamicData>,
455    ) -> Result<(), DynamicError> {
456        self.set(member_id, DynamicValue::Sequence(value))
457    }
458
459    /// Returns an element of a sequence member.
460    ///
461    /// # Errors
462    /// Index out of bounds / type mismatch.
463    pub fn get_sequence_element(
464        &self,
465        member_id: MemberId,
466        index: u32,
467    ) -> Result<&DynamicData, DynamicError> {
468        let v = self.get(member_id)?;
469        if let DynamicValue::Sequence(seq) = v {
470            seq.get(index as usize).ok_or_else(|| {
471                DynamicError::bad_parameter(alloc::format!(
472                    "sequence index {index} out of range (len={})",
473                    seq.len()
474                ))
475            })
476        } else {
477            Err(DynamicError::bad_parameter(alloc::format!(
478                "type mismatch: expected sequence got {}",
479                v.kind_str()
480            )))
481        }
482    }
483}
484
485// ----------------------------------------------------------------------
486// Loans (Spec §7.5.6.1) — minimal, full lifecycle in C4.7
487// ----------------------------------------------------------------------
488
489impl DynamicData {
490    /// Spec §7.5.6.1 `loan_value(member_id)`.
491    ///
492    /// Creates a `DataLoan` handle. Until `return_loaned_value` is
493    /// called, another loan operation on the same
494    /// member is not allowed.
495    ///
496    /// # Errors
497    /// Member not found, or member already on loan.
498    pub fn loan_value(&mut self, member_id: MemberId) -> Result<DataLoan, DynamicError> {
499        self.validate_member(member_id)?;
500        if self.active_loans.contains(&member_id) {
501            return Err(DynamicError::loan(alloc::format!(
502                "member {member_id} is already on loan"
503            )));
504        }
505        self.active_loans.push(member_id);
506        let handle = self.next_loan_handle;
507        self.next_loan_handle = self.next_loan_handle.saturating_add(1);
508        Ok(DataLoan { member_id, handle })
509    }
510
511    /// Spec §7.5.6.1 `return_loaned_value(loan)`.
512    ///
513    /// # Errors
514    /// `LoanError` if the loan is unknown or already returned.
515    pub fn return_loaned_value(&mut self, loan: DataLoan) -> Result<(), DynamicError> {
516        let pos = self
517            .active_loans
518            .iter()
519            .position(|m| *m == loan.member_id)
520            .ok_or_else(|| {
521                DynamicError::loan(alloc::format!(
522                    "no active loan for member {}",
523                    loan.member_id
524                ))
525            })?;
526        self.active_loans.swap_remove(pos);
527        // The handle value is not used further — only kept as a sanity
528        // indicator that `return_loaned_value` receives a productive loan.
529        let _ = loan.handle;
530        Ok(())
531    }
532}
533
534/// Validates: does the value discriminator match the member type kind?
535fn check_value_kind_matches_type(value: &DynamicValue, kind: TypeKind) -> Result<(), DynamicError> {
536    let ok = match (value, kind) {
537        (DynamicValue::Bool(_), TypeKind::Boolean) => true,
538        (DynamicValue::Byte(_), TypeKind::Byte) => true,
539        (DynamicValue::Int8(_), TypeKind::Int8) => true,
540        (DynamicValue::UInt8(_), TypeKind::UInt8) => true,
541        (DynamicValue::Int16(_), TypeKind::Int16) => true,
542        (DynamicValue::UInt16(_), TypeKind::UInt16) => true,
543        (DynamicValue::Int32(_), TypeKind::Int32 | TypeKind::Enumeration) => true,
544        (DynamicValue::UInt32(_), TypeKind::UInt32) => true,
545        (DynamicValue::Int64(_), TypeKind::Int64) => true,
546        (DynamicValue::UInt64(_), TypeKind::UInt64) => true,
547        (DynamicValue::Float32(_), TypeKind::Float32) => true,
548        (DynamicValue::Float64(_), TypeKind::Float64) => true,
549        (DynamicValue::Char8(_), TypeKind::Char8) => true,
550        (DynamicValue::Char16(_), TypeKind::Char16) => true,
551        (DynamicValue::String(_), TypeKind::String8) => true,
552        (DynamicValue::WString(_), TypeKind::String16) => true,
553        (DynamicValue::Complex(_), k) if k.is_aggregable() => true,
554        (DynamicValue::Sequence(_), TypeKind::Sequence | TypeKind::Array) => true,
555        (DynamicValue::None, _) => true,
556        _ => false,
557    };
558    if !ok {
559        return Err(DynamicError::bad_parameter(alloc::format!(
560            "value kind {} cannot be assigned to member kind {kind:?}",
561            value.kind_str()
562        )));
563    }
564    Ok(())
565}
566
567#[cfg(test)]
568#[allow(clippy::unwrap_used)]
569mod tests {
570    use super::*;
571    use crate::dynamic::DynamicTypeBuilderFactory;
572    use crate::dynamic::descriptor::{MemberDescriptor, TypeDescriptor};
573
574    fn struct_with_int32_member() -> DynamicType {
575        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
576        b.add_struct_member("x", 1, TypeDescriptor::primitive(TypeKind::Int32, "int32"))
577            .unwrap();
578        b.build().unwrap()
579    }
580
581    #[test]
582    fn set_get_int32_roundtrips() {
583        let t = struct_with_int32_member();
584        let mut d = DynamicData::new(t);
585        d.set_int32_value(1, 42).unwrap();
586        assert_eq!(d.get_int32_value(1).unwrap(), 42);
587    }
588
589    #[test]
590    fn set_int32_into_int64_member_rejected() {
591        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
592        b.add_struct_member("x", 1, TypeDescriptor::primitive(TypeKind::Int64, "int64"))
593            .unwrap();
594        let t = b.build().unwrap();
595        let mut d = DynamicData::new(t);
596        let err = d.set_int32_value(1, 5).unwrap_err();
597        assert!(matches!(err, DynamicError::BadParameter(_)));
598    }
599
600    #[test]
601    fn unknown_member_id_rejected() {
602        let t = struct_with_int32_member();
603        let mut d = DynamicData::new(t);
604        let err = d.set_int32_value(999, 0).unwrap_err();
605        assert!(matches!(err, DynamicError::BadParameter(_)));
606    }
607
608    #[test]
609    fn loan_double_rejected() {
610        let t = struct_with_int32_member();
611        let mut d = DynamicData::new(t);
612        let _l = d.loan_value(1).unwrap();
613        let err = d.loan_value(1).unwrap_err();
614        assert!(matches!(err, DynamicError::LoanError(_)));
615    }
616
617    #[test]
618    fn loan_return_then_loan_again_works() {
619        let t = struct_with_int32_member();
620        let mut d = DynamicData::new(t);
621        let l = d.loan_value(1).unwrap();
622        d.return_loaned_value(l).unwrap();
623        let _l2 = d.loan_value(1).unwrap();
624    }
625
626    #[test]
627    fn return_unknown_loan_rejected() {
628        let t = struct_with_int32_member();
629        let mut d = DynamicData::new(t);
630        let fake = DataLoan {
631            member_id: 1,
632            handle: 99,
633        };
634        let err = d.return_loaned_value(fake).unwrap_err();
635        assert!(matches!(err, DynamicError::LoanError(_)));
636    }
637
638    #[test]
639    fn item_count_grows_with_sets() {
640        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
641        b.add_struct_member("a", 1, TypeDescriptor::primitive(TypeKind::Int32, "int32"))
642            .unwrap();
643        b.add_struct_member("b", 2, TypeDescriptor::primitive(TypeKind::Int64, "int64"))
644            .unwrap();
645        let t = b.build().unwrap();
646        let mut d = DynamicData::new(t);
647        assert_eq!(d.item_count(), 0);
648        d.set_int32_value(1, 10).unwrap();
649        assert_eq!(d.item_count(), 1);
650        d.set_int64_value(2, 20).unwrap();
651        assert_eq!(d.item_count(), 2);
652    }
653
654    #[test]
655    fn equals_distinguishes_value_diffs() {
656        let t = struct_with_int32_member();
657        let mut a = DynamicData::new(t.clone());
658        let mut b = DynamicData::new(t);
659        a.set_int32_value(1, 5).unwrap();
660        b.set_int32_value(1, 6).unwrap();
661        assert!(!a.equals(&b));
662        b.set_int32_value(1, 5).unwrap();
663        assert!(a.equals(&b));
664    }
665
666    #[test]
667    fn complex_value_set_get() {
668        // outer struct with a nested struct member.
669        let mut inner = DynamicTypeBuilderFactory::create_struct("::Inner");
670        inner
671            .add_struct_member("v", 1, TypeDescriptor::primitive(TypeKind::Int32, "int32"))
672            .unwrap();
673        let inner_t = inner.build().unwrap();
674
675        let mut outer = DynamicTypeBuilderFactory::create_struct("::Outer");
676        outer
677            .add_member(MemberDescriptor::new(
678                "nested",
679                10,
680                TypeDescriptor::structure("::Inner"),
681            ))
682            .unwrap();
683        let outer_t = outer.build().unwrap();
684
685        let mut inner_data = DynamicData::new(inner_t);
686        inner_data.set_int32_value(1, 99).unwrap();
687
688        let mut outer_data = DynamicData::new(outer_t);
689        outer_data.set_complex_value(10, inner_data).unwrap();
690
691        let got = outer_data.get_complex_value(10).unwrap();
692        assert_eq!(got.get_int32_value(1).unwrap(), 99);
693    }
694
695    #[test]
696    fn sequence_length_and_element_access() {
697        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
698        b.add_struct_member(
699            "items",
700            1,
701            TypeDescriptor::sequence(
702                "items",
703                TypeDescriptor::primitive(TypeKind::Int32, "int32"),
704                100,
705            ),
706        )
707        .unwrap();
708        let t = b.build().unwrap();
709
710        let elem_t = DynamicTypeBuilderFactory::get_primitive_type(TypeKind::Int32).unwrap();
711        let mut e0 = DynamicData::new(elem_t.clone());
712        e0.values.insert(0, DynamicValue::Int32(7));
713        let mut e1 = DynamicData::new(elem_t);
714        e1.values.insert(0, DynamicValue::Int32(8));
715        let mut d = DynamicData::new(t);
716        d.set_sequence_value(1, alloc::vec![e0, e1]).unwrap();
717        assert_eq!(d.get_sequence_length(1).unwrap(), 2);
718        let _ = d.get_sequence_element(1, 0).unwrap();
719        assert!(d.get_sequence_element(1, 9).is_err());
720    }
721
722    #[test]
723    fn all_twelve_primitive_setters_work() {
724        // This list is mandatory per Spec §7.5.6.3.
725        let cases: alloc::vec::Vec<(&str, MemberId, TypeKind)> = alloc::vec![
726            ("b", 1, TypeKind::Boolean),
727            ("by", 2, TypeKind::Byte),
728            ("i8", 3, TypeKind::Int8),
729            ("u8", 4, TypeKind::UInt8),
730            ("i16", 5, TypeKind::Int16),
731            ("u16", 6, TypeKind::UInt16),
732            ("i32", 7, TypeKind::Int32),
733            ("u32", 8, TypeKind::UInt32),
734            ("i64", 9, TypeKind::Int64),
735            ("u64", 10, TypeKind::UInt64),
736            ("f32", 11, TypeKind::Float32),
737            ("f64", 12, TypeKind::Float64),
738            ("c8", 13, TypeKind::Char8),
739            ("c16", 14, TypeKind::Char16),
740        ];
741        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
742        for (n, id, k) in &cases {
743            b.add_struct_member(
744                *n,
745                *id,
746                TypeDescriptor::primitive(*k, super::super::type_::primitive_name(*k)),
747            )
748            .unwrap();
749        }
750        // Plus string and WString.
751        b.add_struct_member("s", 100, TypeDescriptor::string8(64))
752            .unwrap();
753        b.add_struct_member("w", 101, TypeDescriptor::string16(64))
754            .unwrap();
755        let t = b.build().unwrap();
756        let mut d = DynamicData::new(t);
757        d.set_boolean_value(1, true).unwrap();
758        d.set_byte_value(2, 0xFF).unwrap();
759        d.set_int8_value(3, -1).unwrap();
760        d.set_uint8_value(4, 255).unwrap();
761        d.set_int16_value(5, -2).unwrap();
762        d.set_uint16_value(6, 2).unwrap();
763        d.set_int32_value(7, -3).unwrap();
764        d.set_uint32_value(8, 3).unwrap();
765        d.set_int64_value(9, -4).unwrap();
766        d.set_uint64_value(10, 4).unwrap();
767        d.set_float32_value(11, 1.5).unwrap();
768        d.set_float64_value(12, 2.5).unwrap();
769        d.set_char8_value(13, b'A').unwrap();
770        d.set_char16_value(14, 0x42).unwrap();
771        d.set_string_value(100, "abc").unwrap();
772        d.set_wstring_value(101, alloc::vec![0x41, 0x42]).unwrap();
773
774        assert!(d.get_boolean_value(1).unwrap());
775        assert_eq!(d.get_byte_value(2).unwrap(), 0xFF);
776        assert_eq!(d.get_int8_value(3).unwrap(), -1);
777        assert_eq!(d.get_uint8_value(4).unwrap(), 255);
778        assert_eq!(d.get_int16_value(5).unwrap(), -2);
779        assert_eq!(d.get_uint16_value(6).unwrap(), 2);
780        assert_eq!(d.get_int32_value(7).unwrap(), -3);
781        assert_eq!(d.get_uint32_value(8).unwrap(), 3);
782        assert_eq!(d.get_int64_value(9).unwrap(), -4);
783        assert_eq!(d.get_uint64_value(10).unwrap(), 4);
784        assert!((d.get_float32_value(11).unwrap() - 1.5).abs() < 1e-6);
785        assert!((d.get_float64_value(12).unwrap() - 2.5).abs() < 1e-9);
786        assert_eq!(d.get_char8_value(13).unwrap(), b'A');
787        assert_eq!(d.get_char16_value(14).unwrap(), 0x42);
788        assert_eq!(d.get_string_value(100).unwrap(), "abc");
789        assert_eq!(d.get_wstring_value(101).unwrap(), alloc::vec![0x41, 0x42]);
790    }
791}