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
255// ----------------------------------------------------------------------
256// 12 Primitive Getters/Setters (Spec §7.5.6.3)
257// ----------------------------------------------------------------------
258
259macro_rules! primitive_accessor {
260    ($get_name:ident, $set_name:ident, $rust_ty:ty, $variant:ident) => {
261        impl DynamicData {
262            #[doc = concat!("Spec §7.5.6.3 `get_", stringify!($variant), "_value`.")]
263            ///
264            /// # Errors
265            /// `BadParameter` on an unknown id, `Inconsistent` on a
266            /// type mismatch.
267            pub fn $get_name(&self, member_id: MemberId) -> Result<$rust_ty, DynamicError> {
268                let v = self.get(member_id)?;
269                if let DynamicValue::$variant(x) = v {
270                    Ok(x.clone())
271                } else {
272                    Err(DynamicError::bad_parameter(alloc::format!(
273                        "type mismatch: expected {} got {}",
274                        stringify!($variant),
275                        v.kind_str()
276                    )))
277                }
278            }
279
280            #[doc = concat!("Spec §7.5.6.3 `set_", stringify!($variant), "_value`.")]
281            ///
282            /// # Errors
283            /// Type mismatch / unknown id.
284            pub fn $set_name(
285                &mut self,
286                member_id: MemberId,
287                value: $rust_ty,
288            ) -> Result<(), DynamicError> {
289                self.set(member_id, DynamicValue::$variant(value))
290            }
291        }
292    };
293}
294
295primitive_accessor!(get_boolean_value, set_boolean_value, bool, Bool);
296primitive_accessor!(get_byte_value, set_byte_value, u8, Byte);
297primitive_accessor!(get_int8_value, set_int8_value, i8, Int8);
298primitive_accessor!(get_uint8_value, set_uint8_value, u8, UInt8);
299primitive_accessor!(get_int16_value, set_int16_value, i16, Int16);
300primitive_accessor!(get_uint16_value, set_uint16_value, u16, UInt16);
301primitive_accessor!(get_int32_value, set_int32_value, i32, Int32);
302primitive_accessor!(get_uint32_value, set_uint32_value, u32, UInt32);
303primitive_accessor!(get_int64_value, set_int64_value, i64, Int64);
304primitive_accessor!(get_uint64_value, set_uint64_value, u64, UInt64);
305primitive_accessor!(get_float32_value, set_float32_value, f32, Float32);
306primitive_accessor!(get_float64_value, set_float64_value, f64, Float64);
307primitive_accessor!(get_char8_value, set_char8_value, u8, Char8);
308primitive_accessor!(get_char16_value, set_char16_value, u16, Char16);
309
310// String + WString separat — Clone-Semantik vs. Move.
311impl DynamicData {
312    /// Spec §7.5.6.3 `get_string_value`.
313    ///
314    /// # Errors
315    /// Type-Mismatch.
316    pub fn get_string_value(&self, member_id: MemberId) -> Result<String, DynamicError> {
317        let v = self.get(member_id)?;
318        if let DynamicValue::String(s) = v {
319            Ok(s.clone())
320        } else {
321            Err(DynamicError::bad_parameter(alloc::format!(
322                "type mismatch: expected string got {}",
323                v.kind_str()
324            )))
325        }
326    }
327
328    /// Spec §7.5.6.3 `set_string_value`.
329    ///
330    /// # Errors
331    /// Type mismatch / unknown id.
332    pub fn set_string_value(
333        &mut self,
334        member_id: MemberId,
335        value: impl Into<String>,
336    ) -> Result<(), DynamicError> {
337        self.set(member_id, DynamicValue::String(value.into()))
338    }
339
340    /// Spec §7.5.6.3 `get_wstring_value`.
341    ///
342    /// # Errors
343    /// Type-Mismatch.
344    pub fn get_wstring_value(&self, member_id: MemberId) -> Result<Vec<u16>, DynamicError> {
345        let v = self.get(member_id)?;
346        if let DynamicValue::WString(s) = v {
347            Ok(s.clone())
348        } else {
349            Err(DynamicError::bad_parameter(alloc::format!(
350                "type mismatch: expected wstring got {}",
351                v.kind_str()
352            )))
353        }
354    }
355
356    /// Spec §7.5.6.3 `set_wstring_value`.
357    ///
358    /// # Errors
359    /// Type mismatch / unknown id.
360    pub fn set_wstring_value(
361        &mut self,
362        member_id: MemberId,
363        value: Vec<u16>,
364    ) -> Result<(), DynamicError> {
365        self.set(member_id, DynamicValue::WString(value))
366    }
367}
368
369// ----------------------------------------------------------------------
370// Composite + Sequence (Spec §7.5.6.5)
371// ----------------------------------------------------------------------
372
373impl DynamicData {
374    /// Spec §7.5.6.5.1 `get_complex_value(member_id)`.
375    ///
376    /// Returns a read view onto the composite member.
377    ///
378    /// # Errors
379    /// Type mismatch or member not set.
380    pub fn get_complex_value(&self, member_id: MemberId) -> Result<&DynamicData, DynamicError> {
381        let v = self.get(member_id)?;
382        if let DynamicValue::Complex(d) = v {
383            Ok(d)
384        } else {
385            Err(DynamicError::bad_parameter(alloc::format!(
386                "type mismatch: expected complex got {}",
387                v.kind_str()
388            )))
389        }
390    }
391
392    /// Spec §7.5.6.5.2 `set_complex_value(member_id, data)`.
393    ///
394    /// # Errors
395    /// Type mismatch or member does not exist.
396    pub fn set_complex_value(
397        &mut self,
398        member_id: MemberId,
399        value: DynamicData,
400    ) -> Result<(), DynamicError> {
401        self.set(member_id, DynamicValue::Complex(Box::new(value)))
402    }
403
404    /// Returns the number of elements of a sequence member.
405    ///
406    /// # Errors
407    /// Type mismatch or member not set.
408    pub fn get_sequence_length(&self, member_id: MemberId) -> Result<u32, DynamicError> {
409        let v = self.get(member_id)?;
410        if let DynamicValue::Sequence(seq) = v {
411            Ok(u32::try_from(seq.len()).unwrap_or(u32::MAX))
412        } else {
413            Err(DynamicError::bad_parameter(alloc::format!(
414                "type mismatch: expected sequence got {}",
415                v.kind_str()
416            )))
417        }
418    }
419
420    /// Sets a sequence completely.
421    ///
422    /// # Errors
423    /// Type mismatch.
424    pub fn set_sequence_value(
425        &mut self,
426        member_id: MemberId,
427        value: Vec<DynamicData>,
428    ) -> Result<(), DynamicError> {
429        self.set(member_id, DynamicValue::Sequence(value))
430    }
431
432    /// Returns an element of a sequence member.
433    ///
434    /// # Errors
435    /// Index out of bounds / type mismatch.
436    pub fn get_sequence_element(
437        &self,
438        member_id: MemberId,
439        index: u32,
440    ) -> Result<&DynamicData, DynamicError> {
441        let v = self.get(member_id)?;
442        if let DynamicValue::Sequence(seq) = v {
443            seq.get(index as usize).ok_or_else(|| {
444                DynamicError::bad_parameter(alloc::format!(
445                    "sequence index {index} out of range (len={})",
446                    seq.len()
447                ))
448            })
449        } else {
450            Err(DynamicError::bad_parameter(alloc::format!(
451                "type mismatch: expected sequence got {}",
452                v.kind_str()
453            )))
454        }
455    }
456}
457
458// ----------------------------------------------------------------------
459// Loans (Spec §7.5.6.1) — minimal, full lifecycle in C4.7
460// ----------------------------------------------------------------------
461
462impl DynamicData {
463    /// Spec §7.5.6.1 `loan_value(member_id)`.
464    ///
465    /// Creates a `DataLoan` handle. Until `return_loaned_value` is
466    /// called, another loan operation on the same
467    /// member is not allowed.
468    ///
469    /// # Errors
470    /// Member not found, or member already on loan.
471    pub fn loan_value(&mut self, member_id: MemberId) -> Result<DataLoan, DynamicError> {
472        self.validate_member(member_id)?;
473        if self.active_loans.contains(&member_id) {
474            return Err(DynamicError::loan(alloc::format!(
475                "member {member_id} is already on loan"
476            )));
477        }
478        self.active_loans.push(member_id);
479        let handle = self.next_loan_handle;
480        self.next_loan_handle = self.next_loan_handle.saturating_add(1);
481        Ok(DataLoan { member_id, handle })
482    }
483
484    /// Spec §7.5.6.1 `return_loaned_value(loan)`.
485    ///
486    /// # Errors
487    /// `LoanError` if the loan is unknown or already returned.
488    pub fn return_loaned_value(&mut self, loan: DataLoan) -> Result<(), DynamicError> {
489        let pos = self
490            .active_loans
491            .iter()
492            .position(|m| *m == loan.member_id)
493            .ok_or_else(|| {
494                DynamicError::loan(alloc::format!(
495                    "no active loan for member {}",
496                    loan.member_id
497                ))
498            })?;
499        self.active_loans.swap_remove(pos);
500        // The handle value is not used further — only kept as a sanity
501        // indicator that `return_loaned_value` receives a productive loan.
502        let _ = loan.handle;
503        Ok(())
504    }
505}
506
507/// Validates: does the value discriminator match the member type kind?
508fn check_value_kind_matches_type(value: &DynamicValue, kind: TypeKind) -> Result<(), DynamicError> {
509    let ok = match (value, kind) {
510        (DynamicValue::Bool(_), TypeKind::Boolean) => true,
511        (DynamicValue::Byte(_), TypeKind::Byte) => true,
512        (DynamicValue::Int8(_), TypeKind::Int8) => true,
513        (DynamicValue::UInt8(_), TypeKind::UInt8) => true,
514        (DynamicValue::Int16(_), TypeKind::Int16) => true,
515        (DynamicValue::UInt16(_), TypeKind::UInt16) => true,
516        (DynamicValue::Int32(_), TypeKind::Int32 | TypeKind::Enumeration) => true,
517        (DynamicValue::UInt32(_), TypeKind::UInt32) => true,
518        (DynamicValue::Int64(_), TypeKind::Int64) => true,
519        (DynamicValue::UInt64(_), TypeKind::UInt64) => true,
520        (DynamicValue::Float32(_), TypeKind::Float32) => true,
521        (DynamicValue::Float64(_), TypeKind::Float64) => true,
522        (DynamicValue::Char8(_), TypeKind::Char8) => true,
523        (DynamicValue::Char16(_), TypeKind::Char16) => true,
524        (DynamicValue::String(_), TypeKind::String8) => true,
525        (DynamicValue::WString(_), TypeKind::String16) => true,
526        (DynamicValue::Complex(_), k) if k.is_aggregable() => true,
527        (DynamicValue::Sequence(_), TypeKind::Sequence | TypeKind::Array) => true,
528        (DynamicValue::None, _) => true,
529        _ => false,
530    };
531    if !ok {
532        return Err(DynamicError::bad_parameter(alloc::format!(
533            "value kind {} cannot be assigned to member kind {kind:?}",
534            value.kind_str()
535        )));
536    }
537    Ok(())
538}
539
540#[cfg(test)]
541#[allow(clippy::unwrap_used)]
542mod tests {
543    use super::*;
544    use crate::dynamic::DynamicTypeBuilderFactory;
545    use crate::dynamic::descriptor::{MemberDescriptor, TypeDescriptor};
546
547    fn struct_with_int32_member() -> DynamicType {
548        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
549        b.add_struct_member("x", 1, TypeDescriptor::primitive(TypeKind::Int32, "int32"))
550            .unwrap();
551        b.build().unwrap()
552    }
553
554    #[test]
555    fn set_get_int32_roundtrips() {
556        let t = struct_with_int32_member();
557        let mut d = DynamicData::new(t);
558        d.set_int32_value(1, 42).unwrap();
559        assert_eq!(d.get_int32_value(1).unwrap(), 42);
560    }
561
562    #[test]
563    fn set_int32_into_int64_member_rejected() {
564        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
565        b.add_struct_member("x", 1, TypeDescriptor::primitive(TypeKind::Int64, "int64"))
566            .unwrap();
567        let t = b.build().unwrap();
568        let mut d = DynamicData::new(t);
569        let err = d.set_int32_value(1, 5).unwrap_err();
570        assert!(matches!(err, DynamicError::BadParameter(_)));
571    }
572
573    #[test]
574    fn unknown_member_id_rejected() {
575        let t = struct_with_int32_member();
576        let mut d = DynamicData::new(t);
577        let err = d.set_int32_value(999, 0).unwrap_err();
578        assert!(matches!(err, DynamicError::BadParameter(_)));
579    }
580
581    #[test]
582    fn loan_double_rejected() {
583        let t = struct_with_int32_member();
584        let mut d = DynamicData::new(t);
585        let _l = d.loan_value(1).unwrap();
586        let err = d.loan_value(1).unwrap_err();
587        assert!(matches!(err, DynamicError::LoanError(_)));
588    }
589
590    #[test]
591    fn loan_return_then_loan_again_works() {
592        let t = struct_with_int32_member();
593        let mut d = DynamicData::new(t);
594        let l = d.loan_value(1).unwrap();
595        d.return_loaned_value(l).unwrap();
596        let _l2 = d.loan_value(1).unwrap();
597    }
598
599    #[test]
600    fn return_unknown_loan_rejected() {
601        let t = struct_with_int32_member();
602        let mut d = DynamicData::new(t);
603        let fake = DataLoan {
604            member_id: 1,
605            handle: 99,
606        };
607        let err = d.return_loaned_value(fake).unwrap_err();
608        assert!(matches!(err, DynamicError::LoanError(_)));
609    }
610
611    #[test]
612    fn item_count_grows_with_sets() {
613        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
614        b.add_struct_member("a", 1, TypeDescriptor::primitive(TypeKind::Int32, "int32"))
615            .unwrap();
616        b.add_struct_member("b", 2, TypeDescriptor::primitive(TypeKind::Int64, "int64"))
617            .unwrap();
618        let t = b.build().unwrap();
619        let mut d = DynamicData::new(t);
620        assert_eq!(d.item_count(), 0);
621        d.set_int32_value(1, 10).unwrap();
622        assert_eq!(d.item_count(), 1);
623        d.set_int64_value(2, 20).unwrap();
624        assert_eq!(d.item_count(), 2);
625    }
626
627    #[test]
628    fn equals_distinguishes_value_diffs() {
629        let t = struct_with_int32_member();
630        let mut a = DynamicData::new(t.clone());
631        let mut b = DynamicData::new(t);
632        a.set_int32_value(1, 5).unwrap();
633        b.set_int32_value(1, 6).unwrap();
634        assert!(!a.equals(&b));
635        b.set_int32_value(1, 5).unwrap();
636        assert!(a.equals(&b));
637    }
638
639    #[test]
640    fn complex_value_set_get() {
641        // outer struct with a nested struct member.
642        let mut inner = DynamicTypeBuilderFactory::create_struct("::Inner");
643        inner
644            .add_struct_member("v", 1, TypeDescriptor::primitive(TypeKind::Int32, "int32"))
645            .unwrap();
646        let inner_t = inner.build().unwrap();
647
648        let mut outer = DynamicTypeBuilderFactory::create_struct("::Outer");
649        outer
650            .add_member(MemberDescriptor::new(
651                "nested",
652                10,
653                TypeDescriptor::structure("::Inner"),
654            ))
655            .unwrap();
656        let outer_t = outer.build().unwrap();
657
658        let mut inner_data = DynamicData::new(inner_t);
659        inner_data.set_int32_value(1, 99).unwrap();
660
661        let mut outer_data = DynamicData::new(outer_t);
662        outer_data.set_complex_value(10, inner_data).unwrap();
663
664        let got = outer_data.get_complex_value(10).unwrap();
665        assert_eq!(got.get_int32_value(1).unwrap(), 99);
666    }
667
668    #[test]
669    fn sequence_length_and_element_access() {
670        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
671        b.add_struct_member(
672            "items",
673            1,
674            TypeDescriptor::sequence(
675                "items",
676                TypeDescriptor::primitive(TypeKind::Int32, "int32"),
677                100,
678            ),
679        )
680        .unwrap();
681        let t = b.build().unwrap();
682
683        let elem_t = DynamicTypeBuilderFactory::get_primitive_type(TypeKind::Int32).unwrap();
684        let mut e0 = DynamicData::new(elem_t.clone());
685        e0.values.insert(0, DynamicValue::Int32(7));
686        let mut e1 = DynamicData::new(elem_t);
687        e1.values.insert(0, DynamicValue::Int32(8));
688        let mut d = DynamicData::new(t);
689        d.set_sequence_value(1, alloc::vec![e0, e1]).unwrap();
690        assert_eq!(d.get_sequence_length(1).unwrap(), 2);
691        let _ = d.get_sequence_element(1, 0).unwrap();
692        assert!(d.get_sequence_element(1, 9).is_err());
693    }
694
695    #[test]
696    fn all_twelve_primitive_setters_work() {
697        // This list is mandatory per Spec §7.5.6.3.
698        let cases: alloc::vec::Vec<(&str, MemberId, TypeKind)> = alloc::vec![
699            ("b", 1, TypeKind::Boolean),
700            ("by", 2, TypeKind::Byte),
701            ("i8", 3, TypeKind::Int8),
702            ("u8", 4, TypeKind::UInt8),
703            ("i16", 5, TypeKind::Int16),
704            ("u16", 6, TypeKind::UInt16),
705            ("i32", 7, TypeKind::Int32),
706            ("u32", 8, TypeKind::UInt32),
707            ("i64", 9, TypeKind::Int64),
708            ("u64", 10, TypeKind::UInt64),
709            ("f32", 11, TypeKind::Float32),
710            ("f64", 12, TypeKind::Float64),
711            ("c8", 13, TypeKind::Char8),
712            ("c16", 14, TypeKind::Char16),
713        ];
714        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
715        for (n, id, k) in &cases {
716            b.add_struct_member(
717                *n,
718                *id,
719                TypeDescriptor::primitive(*k, super::super::type_::primitive_name(*k)),
720            )
721            .unwrap();
722        }
723        // Plus string and WString.
724        b.add_struct_member("s", 100, TypeDescriptor::string8(64))
725            .unwrap();
726        b.add_struct_member("w", 101, TypeDescriptor::string16(64))
727            .unwrap();
728        let t = b.build().unwrap();
729        let mut d = DynamicData::new(t);
730        d.set_boolean_value(1, true).unwrap();
731        d.set_byte_value(2, 0xFF).unwrap();
732        d.set_int8_value(3, -1).unwrap();
733        d.set_uint8_value(4, 255).unwrap();
734        d.set_int16_value(5, -2).unwrap();
735        d.set_uint16_value(6, 2).unwrap();
736        d.set_int32_value(7, -3).unwrap();
737        d.set_uint32_value(8, 3).unwrap();
738        d.set_int64_value(9, -4).unwrap();
739        d.set_uint64_value(10, 4).unwrap();
740        d.set_float32_value(11, 1.5).unwrap();
741        d.set_float64_value(12, 2.5).unwrap();
742        d.set_char8_value(13, b'A').unwrap();
743        d.set_char16_value(14, 0x42).unwrap();
744        d.set_string_value(100, "abc").unwrap();
745        d.set_wstring_value(101, alloc::vec![0x41, 0x42]).unwrap();
746
747        assert!(d.get_boolean_value(1).unwrap());
748        assert_eq!(d.get_byte_value(2).unwrap(), 0xFF);
749        assert_eq!(d.get_int8_value(3).unwrap(), -1);
750        assert_eq!(d.get_uint8_value(4).unwrap(), 255);
751        assert_eq!(d.get_int16_value(5).unwrap(), -2);
752        assert_eq!(d.get_uint16_value(6).unwrap(), 2);
753        assert_eq!(d.get_int32_value(7).unwrap(), -3);
754        assert_eq!(d.get_uint32_value(8).unwrap(), 3);
755        assert_eq!(d.get_int64_value(9).unwrap(), -4);
756        assert_eq!(d.get_uint64_value(10).unwrap(), 4);
757        assert!((d.get_float32_value(11).unwrap() - 1.5).abs() < 1e-6);
758        assert!((d.get_float64_value(12).unwrap() - 2.5).abs() < 1e-9);
759        assert_eq!(d.get_char8_value(13).unwrap(), b'A');
760        assert_eq!(d.get_char16_value(14).unwrap(), 0x42);
761        assert_eq!(d.get_string_value(100).unwrap(), "abc");
762        assert_eq!(d.get_wstring_value(101).unwrap(), alloc::vec![0x41, 0x42]);
763    }
764}