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` ist eine Daten-Instanz eines `DynamicType`. Die API
6//! ist Spec-frozen (Methoden-Namen + Signaturen). Gegenueber der Spec
7//! werden zwei Vereinfachungen gemacht:
8//!
9//! 1. Alle 12 typed Getters/Setters kommen ueber ein `DynamicValue`-
10//!    Discriminator-Enum, ueber den die `set_<T>_value`/`get_<T>_value`
11//!    Methoden ihren Type-Check machen. Type-Mismatch → `BadParameter`.
12//! 2. Loans werden ueber einen Reference-counted `DataLoan` modelliert
13//!    (Spec §7.5.6.1 — vollstaendige Loan-API folgt mit 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 — internes Storage-Enum
26// ----------------------------------------------------------------------
27
28/// Sum-Type fuer alle in DynamicData speicherbaren Werte. Spec §7.5.6
29/// spricht von 12 Primitive-Types + Composite + Sequence — diese Enum
30/// kapselt das 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 als Vec<u16>).
64    WString(Vec<u16>),
65    /// Composite oder Sequence — eingebettete `DynamicData`.
66    Complex(Box<DynamicData>),
67    /// Sequence von DynamicData (Spec: Element-Type bekannt aus dem
68    /// containerizing TypeDescriptor).
69    Sequence(Vec<DynamicData>),
70    /// Discard / nicht gesetzt (Default-Wert wird zurueckgegeben).
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 — einfacher RAII-Wrapper
131// ----------------------------------------------------------------------
132
133/// Gibt eine geliehene `DynamicData`-Sicht auf einen Member zurueck.
134/// Lifecycle: `loan_value` → `return_loaned_value` (Spec §7.5.6.1).
135///
136/// In Phase 4 minimal — voller Loan-Refcount kommt mit C4.7.
137#[derive(Debug)]
138pub struct DataLoan {
139    member_id: MemberId,
140    /// Index im internen Loans-Array — wird auf `return` invalidiert.
141    handle: u32,
142}
143
144impl DataLoan {
145    /// Member-Id, fuer den der Loan angelegt wurde.
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/// Daten-Instanz eines `DynamicType`. Der Typ muss bei Konstruktion
159/// uebergeben werden (`DynamicDataFactory::create_data`), danach
160/// koennen Members ueber `set_<T>_value(member_id, value)` / `get_<T>_value`
161/// gelesen/geschrieben werden.
162#[derive(Debug, Clone)]
163pub struct DynamicData {
164    type_: DynamicType,
165    values: BTreeMap<MemberId, DynamicValue>,
166    /// Aktive Loans — fuer Lifecycle-Validierung.
167    active_loans: Vec<u32>,
168    /// Monoton wachsender 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    /// Prueft, ob `member_id` im Type definiert ist.
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    /// Allgemeiner Setter — typgesichert via `DynamicValue` mit
212    /// Spec §7.5.4.1.2 TryConstruct-Apply-Semantik (C4.7): Wenn der
213    /// Member-Type ein `bound` definiert und der Wert die Bound
214    /// verletzt (zu langer String, zu lange Sequence, mismatched
215    /// Array-Length), entscheidet der `try_construct`-Strategy:
216    /// `Discard` laesst den Member unset, `UseDefault` setzt den
217    /// `default_value`, `Trim` truncated auf die 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). Member ohne Bounds:
225        // `apply_try_construct` liefert `Accept(value)` unveraendert.
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 bleibt 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` bei unbekannter Id, `Inconsistent` bei
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 / unbekannte 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 / unbekannte 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 / unbekannte 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    /// Liefert eine Lese-Sicht auf den Composite-Member.
377    ///
378    /// # Errors
379    /// Type-Mismatch oder Member nicht gesetzt.
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 oder Member existiert nicht.
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    /// Liefert die Anzahl Elemente einer Sequence-Member.
405    ///
406    /// # Errors
407    /// Type-Mismatch oder Member nicht gesetzt.
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    /// Setzt eine Sequence komplett.
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    /// Liefert ein Element einer Sequence-Member.
433    ///
434    /// # Errors
435    /// Index-OoB / 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, voller Lifecycle in C4.7
460// ----------------------------------------------------------------------
461
462impl DynamicData {
463    /// Spec §7.5.6.1 `loan_value(member_id)`.
464    ///
465    /// Erzeugt einen `DataLoan`-Handle. Bis zum Aufruf von
466    /// `return_loaned_value` ist eine erneute Loan-Operation auf demselben
467    /// Member nicht erlaubt.
468    ///
469    /// # Errors
470    /// Member nicht gefunden, oder Member bereits geliehen.
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` wenn der Loan unbekannt oder bereits zurueckgegeben.
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        // Handle-Wert wird nicht weiterverwendet — nur als Sanity-Indikator
501        // gehalten, dass `return_loaned_value` einen produktiven Loan
502        // erhaelt.
503        let _ = loan.handle;
504        Ok(())
505    }
506}
507
508/// Validiert: passt der Wert-Discriminator zum Member-Type-Kind?
509fn check_value_kind_matches_type(value: &DynamicValue, kind: TypeKind) -> Result<(), DynamicError> {
510    let ok = match (value, kind) {
511        (DynamicValue::Bool(_), TypeKind::Boolean) => true,
512        (DynamicValue::Byte(_), TypeKind::Byte) => true,
513        (DynamicValue::Int8(_), TypeKind::Int8) => true,
514        (DynamicValue::UInt8(_), TypeKind::UInt8) => true,
515        (DynamicValue::Int16(_), TypeKind::Int16) => true,
516        (DynamicValue::UInt16(_), TypeKind::UInt16) => true,
517        (DynamicValue::Int32(_), TypeKind::Int32 | TypeKind::Enumeration) => true,
518        (DynamicValue::UInt32(_), TypeKind::UInt32) => true,
519        (DynamicValue::Int64(_), TypeKind::Int64) => true,
520        (DynamicValue::UInt64(_), TypeKind::UInt64) => true,
521        (DynamicValue::Float32(_), TypeKind::Float32) => true,
522        (DynamicValue::Float64(_), TypeKind::Float64) => true,
523        (DynamicValue::Char8(_), TypeKind::Char8) => true,
524        (DynamicValue::Char16(_), TypeKind::Char16) => true,
525        (DynamicValue::String(_), TypeKind::String8) => true,
526        (DynamicValue::WString(_), TypeKind::String16) => true,
527        (DynamicValue::Complex(_), k) if k.is_aggregable() => true,
528        (DynamicValue::Sequence(_), TypeKind::Sequence | TypeKind::Array) => true,
529        (DynamicValue::None, _) => true,
530        _ => false,
531    };
532    if !ok {
533        return Err(DynamicError::bad_parameter(alloc::format!(
534            "value kind {} cannot be assigned to member kind {kind:?}",
535            value.kind_str()
536        )));
537    }
538    Ok(())
539}
540
541#[cfg(test)]
542#[allow(clippy::unwrap_used)]
543mod tests {
544    use super::*;
545    use crate::dynamic::DynamicTypeBuilderFactory;
546    use crate::dynamic::descriptor::{MemberDescriptor, TypeDescriptor};
547
548    fn struct_with_int32_member() -> DynamicType {
549        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
550        b.add_struct_member("x", 1, TypeDescriptor::primitive(TypeKind::Int32, "int32"))
551            .unwrap();
552        b.build().unwrap()
553    }
554
555    #[test]
556    fn set_get_int32_roundtrips() {
557        let t = struct_with_int32_member();
558        let mut d = DynamicData::new(t);
559        d.set_int32_value(1, 42).unwrap();
560        assert_eq!(d.get_int32_value(1).unwrap(), 42);
561    }
562
563    #[test]
564    fn set_int32_into_int64_member_rejected() {
565        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
566        b.add_struct_member("x", 1, TypeDescriptor::primitive(TypeKind::Int64, "int64"))
567            .unwrap();
568        let t = b.build().unwrap();
569        let mut d = DynamicData::new(t);
570        let err = d.set_int32_value(1, 5).unwrap_err();
571        assert!(matches!(err, DynamicError::BadParameter(_)));
572    }
573
574    #[test]
575    fn unknown_member_id_rejected() {
576        let t = struct_with_int32_member();
577        let mut d = DynamicData::new(t);
578        let err = d.set_int32_value(999, 0).unwrap_err();
579        assert!(matches!(err, DynamicError::BadParameter(_)));
580    }
581
582    #[test]
583    fn loan_double_rejected() {
584        let t = struct_with_int32_member();
585        let mut d = DynamicData::new(t);
586        let _l = d.loan_value(1).unwrap();
587        let err = d.loan_value(1).unwrap_err();
588        assert!(matches!(err, DynamicError::LoanError(_)));
589    }
590
591    #[test]
592    fn loan_return_then_loan_again_works() {
593        let t = struct_with_int32_member();
594        let mut d = DynamicData::new(t);
595        let l = d.loan_value(1).unwrap();
596        d.return_loaned_value(l).unwrap();
597        let _l2 = d.loan_value(1).unwrap();
598    }
599
600    #[test]
601    fn return_unknown_loan_rejected() {
602        let t = struct_with_int32_member();
603        let mut d = DynamicData::new(t);
604        let fake = DataLoan {
605            member_id: 1,
606            handle: 99,
607        };
608        let err = d.return_loaned_value(fake).unwrap_err();
609        assert!(matches!(err, DynamicError::LoanError(_)));
610    }
611
612    #[test]
613    fn item_count_grows_with_sets() {
614        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
615        b.add_struct_member("a", 1, TypeDescriptor::primitive(TypeKind::Int32, "int32"))
616            .unwrap();
617        b.add_struct_member("b", 2, TypeDescriptor::primitive(TypeKind::Int64, "int64"))
618            .unwrap();
619        let t = b.build().unwrap();
620        let mut d = DynamicData::new(t);
621        assert_eq!(d.item_count(), 0);
622        d.set_int32_value(1, 10).unwrap();
623        assert_eq!(d.item_count(), 1);
624        d.set_int64_value(2, 20).unwrap();
625        assert_eq!(d.item_count(), 2);
626    }
627
628    #[test]
629    fn equals_distinguishes_value_diffs() {
630        let t = struct_with_int32_member();
631        let mut a = DynamicData::new(t.clone());
632        let mut b = DynamicData::new(t);
633        a.set_int32_value(1, 5).unwrap();
634        b.set_int32_value(1, 6).unwrap();
635        assert!(!a.equals(&b));
636        b.set_int32_value(1, 5).unwrap();
637        assert!(a.equals(&b));
638    }
639
640    #[test]
641    fn complex_value_set_get() {
642        // outer struct mit nested struct member.
643        let mut inner = DynamicTypeBuilderFactory::create_struct("::Inner");
644        inner
645            .add_struct_member("v", 1, TypeDescriptor::primitive(TypeKind::Int32, "int32"))
646            .unwrap();
647        let inner_t = inner.build().unwrap();
648
649        let mut outer = DynamicTypeBuilderFactory::create_struct("::Outer");
650        outer
651            .add_member(MemberDescriptor::new(
652                "nested",
653                10,
654                TypeDescriptor::structure("::Inner"),
655            ))
656            .unwrap();
657        let outer_t = outer.build().unwrap();
658
659        let mut inner_data = DynamicData::new(inner_t);
660        inner_data.set_int32_value(1, 99).unwrap();
661
662        let mut outer_data = DynamicData::new(outer_t);
663        outer_data.set_complex_value(10, inner_data).unwrap();
664
665        let got = outer_data.get_complex_value(10).unwrap();
666        assert_eq!(got.get_int32_value(1).unwrap(), 99);
667    }
668
669    #[test]
670    fn sequence_length_and_element_access() {
671        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
672        b.add_struct_member(
673            "items",
674            1,
675            TypeDescriptor::sequence(
676                "items",
677                TypeDescriptor::primitive(TypeKind::Int32, "int32"),
678                100,
679            ),
680        )
681        .unwrap();
682        let t = b.build().unwrap();
683
684        let elem_t = DynamicTypeBuilderFactory::get_primitive_type(TypeKind::Int32).unwrap();
685        let mut e0 = DynamicData::new(elem_t.clone());
686        e0.values.insert(0, DynamicValue::Int32(7));
687        let mut e1 = DynamicData::new(elem_t);
688        e1.values.insert(0, DynamicValue::Int32(8));
689        let mut d = DynamicData::new(t);
690        d.set_sequence_value(1, alloc::vec![e0, e1]).unwrap();
691        assert_eq!(d.get_sequence_length(1).unwrap(), 2);
692        let _ = d.get_sequence_element(1, 0).unwrap();
693        assert!(d.get_sequence_element(1, 9).is_err());
694    }
695
696    #[test]
697    fn all_twelve_primitive_setters_work() {
698        // Diese Liste ist Spec-§7.5.6.3-Pflicht.
699        let cases: alloc::vec::Vec<(&str, MemberId, TypeKind)> = alloc::vec![
700            ("b", 1, TypeKind::Boolean),
701            ("by", 2, TypeKind::Byte),
702            ("i8", 3, TypeKind::Int8),
703            ("u8", 4, TypeKind::UInt8),
704            ("i16", 5, TypeKind::Int16),
705            ("u16", 6, TypeKind::UInt16),
706            ("i32", 7, TypeKind::Int32),
707            ("u32", 8, TypeKind::UInt32),
708            ("i64", 9, TypeKind::Int64),
709            ("u64", 10, TypeKind::UInt64),
710            ("f32", 11, TypeKind::Float32),
711            ("f64", 12, TypeKind::Float64),
712            ("c8", 13, TypeKind::Char8),
713            ("c16", 14, TypeKind::Char16),
714        ];
715        let mut b = DynamicTypeBuilderFactory::create_struct("::S");
716        for (n, id, k) in &cases {
717            b.add_struct_member(
718                *n,
719                *id,
720                TypeDescriptor::primitive(*k, super::super::type_::primitive_name(*k)),
721            )
722            .unwrap();
723        }
724        // Plus String und WString.
725        b.add_struct_member("s", 100, TypeDescriptor::string8(64))
726            .unwrap();
727        b.add_struct_member("w", 101, TypeDescriptor::string16(64))
728            .unwrap();
729        let t = b.build().unwrap();
730        let mut d = DynamicData::new(t);
731        d.set_boolean_value(1, true).unwrap();
732        d.set_byte_value(2, 0xFF).unwrap();
733        d.set_int8_value(3, -1).unwrap();
734        d.set_uint8_value(4, 255).unwrap();
735        d.set_int16_value(5, -2).unwrap();
736        d.set_uint16_value(6, 2).unwrap();
737        d.set_int32_value(7, -3).unwrap();
738        d.set_uint32_value(8, 3).unwrap();
739        d.set_int64_value(9, -4).unwrap();
740        d.set_uint64_value(10, 4).unwrap();
741        d.set_float32_value(11, 1.5).unwrap();
742        d.set_float64_value(12, 2.5).unwrap();
743        d.set_char8_value(13, b'A').unwrap();
744        d.set_char16_value(14, 0x42).unwrap();
745        d.set_string_value(100, "abc").unwrap();
746        d.set_wstring_value(101, alloc::vec![0x41, 0x42]).unwrap();
747
748        assert!(d.get_boolean_value(1).unwrap());
749        assert_eq!(d.get_byte_value(2).unwrap(), 0xFF);
750        assert_eq!(d.get_int8_value(3).unwrap(), -1);
751        assert_eq!(d.get_uint8_value(4).unwrap(), 255);
752        assert_eq!(d.get_int16_value(5).unwrap(), -2);
753        assert_eq!(d.get_uint16_value(6).unwrap(), 2);
754        assert_eq!(d.get_int32_value(7).unwrap(), -3);
755        assert_eq!(d.get_uint32_value(8).unwrap(), 3);
756        assert_eq!(d.get_int64_value(9).unwrap(), -4);
757        assert_eq!(d.get_uint64_value(10).unwrap(), 4);
758        assert!((d.get_float32_value(11).unwrap() - 1.5).abs() < 1e-6);
759        assert!((d.get_float64_value(12).unwrap() - 2.5).abs() < 1e-9);
760        assert_eq!(d.get_char8_value(13).unwrap(), b'A');
761        assert_eq!(d.get_char16_value(14).unwrap(), 0x42);
762        assert_eq!(d.get_string_value(100).unwrap(), "abc");
763        assert_eq!(d.get_wstring_value(101).unwrap(), alloc::vec![0x41, 0x42]);
764    }
765}