Skip to main content

zerodds_types/dynamic/
bridge.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! DynamicType ↔ TypeObject Bridge (XTypes 1.3 §7.6.3 + §7.6.4).
4//!
5//! This bridge is the bracket between the **wire representation**
6//! (`TypeObject`, with `MinimalTypeObject` + `CompleteTypeObject` as the
7//! discrimination) and the **runtime API** (`DynamicType`).
8//!
9//! Implements all 10 TypeObject kinds: struct + union + enum + bitmask +
10//! bitset + annotation + alias plus the collection kinds sequence/array/map
11//! (XTypes §7.3.4.4: `CompleteSequenceType` / `CompleteArrayType` /
12//! `CompleteMapType`). Anonymous plain collections are additionally
13//! referenced inline via `TypeIdentifier` (PlainCollection); `to_type_object`
14//! returns the full collection `CompleteTypeObject` variant here. Forward
15//! mapping of composite member types via the name — TypeRegistry lookup on
16//! EquivalenceHash TypeIdentifiers is the task of C4.2.
17
18use alloc::format;
19use alloc::string::{String, ToString};
20use alloc::vec::Vec;
21
22use crate::type_identifier::{PrimitiveKind, TypeIdentifier};
23use crate::type_object::common::{CommonStructMember, CommonUnionMember};
24use crate::type_object::complete::{
25    CompleteAliasBody, CompleteAliasHeader, CompleteAliasType, CompleteAnnotationParameter,
26    CompleteAnnotationType, CompleteArrayType, CompleteBitfield, CompleteBitflag,
27    CompleteBitmaskType, CompleteBitsetType, CompleteCollectionElement,
28    CompleteDiscriminatorMember, CompleteEnumeratedHeader, CompleteEnumeratedLiteral,
29    CompleteEnumeratedType, CompleteMapType, CompleteSequenceType, CompleteStructHeader,
30    CompleteStructMember, CompleteStructType, CompleteUnionHeader, CompleteUnionMember,
31    CompleteUnionType,
32};
33use crate::type_object::flags::{
34    AliasMemberFlag, AliasTypeFlag, AnnotationParameterFlag, AnnotationTypeFlag, BitfieldFlag,
35    BitflagFlag, BitmaskTypeFlag, BitsetTypeFlag, CollectionElementFlag, CollectionTypeFlag,
36    EnumLiteralFlag, EnumTypeFlag, StructMemberFlag, StructTypeFlag, UnionDiscriminatorFlag,
37    UnionMemberFlag, UnionTypeFlag,
38};
39use crate::type_object::minimal::CommonDiscriminatorMember;
40use crate::type_object::minimal::{
41    CommonBitfield, CommonBitflag, CommonCollectionElement, CommonEnumeratedHeader,
42    CommonEnumeratedLiteral,
43};
44use crate::type_object::{CompleteTypeObject, TypeObject};
45
46use super::builder::{DynamicTypeBuilder, DynamicTypeBuilderFactory};
47use super::descriptor::{ExtensibilityKind, MemberDescriptor, TypeDescriptor, TypeKind};
48use super::error::DynamicError;
49use super::type_::DynamicType;
50
51// ----------------------------------------------------------------------
52// DynamicType → TypeObject (spec §7.6.3 — for discovery + TypeLookup)
53// ----------------------------------------------------------------------
54
55impl DynamicType {
56    /// Spec §7.6.3 — converts this DynamicType into a
57    /// `CompleteTypeObject`. Always returns complete (not minimal),
58    /// because DynamicType carries the names + annotations.
59    ///
60    /// Scope:
61    /// - Struct with primitive + string + sequence/array members.
62    /// - Union, enum, alias as their own helper methods (see module doc).
63    ///
64    /// # Errors
65    /// `Unsupported` for not-yet-implemented kinds,
66    /// `Inconsistent` if the type is malformed.
67    pub fn to_type_object(&self) -> Result<TypeObject, DynamicError> {
68        match self.kind() {
69            TypeKind::Structure => Ok(TypeObject::Complete(CompleteTypeObject::Struct(
70                self.to_complete_struct()?,
71            ))),
72            TypeKind::Union => Ok(TypeObject::Complete(CompleteTypeObject::Union(
73                self.to_complete_union()?,
74            ))),
75            TypeKind::Enumeration => Ok(TypeObject::Complete(CompleteTypeObject::Enumerated(
76                self.to_complete_enum()?,
77            ))),
78            TypeKind::Bitmask => Ok(TypeObject::Complete(CompleteTypeObject::Bitmask(
79                self.to_complete_bitmask()?,
80            ))),
81            TypeKind::Alias => Ok(TypeObject::Complete(CompleteTypeObject::Alias(
82                self.to_complete_alias()?,
83            ))),
84            TypeKind::Sequence => Ok(TypeObject::Complete(CompleteTypeObject::Sequence(
85                self.to_complete_sequence()?,
86            ))),
87            TypeKind::Array => Ok(TypeObject::Complete(CompleteTypeObject::Array(
88                self.to_complete_array()?,
89            ))),
90            TypeKind::Map => Ok(TypeObject::Complete(CompleteTypeObject::Map(
91                self.to_complete_map()?,
92            ))),
93            TypeKind::Bitset => Ok(TypeObject::Complete(CompleteTypeObject::Bitset(
94                self.to_complete_bitset()?,
95            ))),
96            TypeKind::Annotation => Ok(TypeObject::Complete(CompleteTypeObject::Annotation(
97                self.to_complete_annotation()?,
98            ))),
99            other => Err(DynamicError::unsupported(format!(
100                "to_type_object: {other:?} is a primitive/no-type kind without TypeObject",
101            ))),
102        }
103    }
104
105    /// XTypes §7.3.4.4 — `sequence<T>` → `CompleteSequenceType`.
106    /// `bound = 0` bedeutet unbounded.
107    fn to_complete_sequence(&self) -> Result<CompleteSequenceType, DynamicError> {
108        use crate::type_object::common::{
109            AppliedBuiltinTypeAnnotations, CompleteTypeDetail, OptionalAppliedAnnotationSeq,
110        };
111        let element = self
112            .descriptor()
113            .element_type
114            .as_ref()
115            .ok_or_else(|| DynamicError::inconsistent("sequence type missing element_type"))?;
116        let bound = self.descriptor().bound.first().copied().unwrap_or(0);
117        Ok(CompleteSequenceType {
118            collection_flag: CollectionTypeFlag(0),
119            bound,
120            detail: CompleteTypeDetail {
121                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
122                ann_custom: OptionalAppliedAnnotationSeq::default(),
123                type_name: self.descriptor().name.clone(),
124            },
125            element: complete_collection_element(element)?,
126        })
127    }
128
129    /// XTypes §7.3.4.4 — `T[N]` → `CompleteArrayType`.
130    /// `bound_seq` traegt alle Array-Dimensionen.
131    fn to_complete_array(&self) -> Result<CompleteArrayType, DynamicError> {
132        use crate::type_object::common::{
133            AppliedBuiltinTypeAnnotations, CompleteTypeDetail, OptionalAppliedAnnotationSeq,
134        };
135        let element = self
136            .descriptor()
137            .element_type
138            .as_ref()
139            .ok_or_else(|| DynamicError::inconsistent("array type missing element_type"))?;
140        let bound_seq = self.descriptor().bound.clone();
141        if bound_seq.is_empty() {
142            return Err(DynamicError::inconsistent(
143                "array type missing dimensions (bound)",
144            ));
145        }
146        Ok(CompleteArrayType {
147            collection_flag: CollectionTypeFlag(0),
148            bound_seq,
149            detail: CompleteTypeDetail {
150                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
151                ann_custom: OptionalAppliedAnnotationSeq::default(),
152                type_name: self.descriptor().name.clone(),
153            },
154            element: complete_collection_element(element)?,
155        })
156    }
157
158    /// XTypes §7.3.4.4 — `map<K, V>` → `CompleteMapType`.
159    /// `element_type` traegt den Value-, `key_element_type` den Key-Typ.
160    fn to_complete_map(&self) -> Result<CompleteMapType, DynamicError> {
161        use crate::type_object::common::{
162            AppliedBuiltinTypeAnnotations, CompleteTypeDetail, OptionalAppliedAnnotationSeq,
163        };
164        let value =
165            self.descriptor().element_type.as_ref().ok_or_else(|| {
166                DynamicError::inconsistent("map type missing element_type (value)")
167            })?;
168        let key = self
169            .descriptor()
170            .key_element_type
171            .as_ref()
172            .ok_or_else(|| DynamicError::inconsistent("map type missing key_element_type"))?;
173        let bound = self.descriptor().bound.first().copied().unwrap_or(0);
174        Ok(CompleteMapType {
175            collection_flag: CollectionTypeFlag(0),
176            bound,
177            detail: CompleteTypeDetail {
178                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
179                ann_custom: OptionalAppliedAnnotationSeq::default(),
180                type_name: self.descriptor().name.clone(),
181            },
182            key: complete_collection_element(key)?,
183            element: complete_collection_element(value)?,
184        })
185    }
186
187    fn to_complete_alias(&self) -> Result<CompleteAliasType, DynamicError> {
188        use crate::type_object::common::{
189            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteTypeDetail,
190            OptionalAppliedAnnotationSeq,
191        };
192        let header = CompleteAliasHeader {
193            detail: CompleteTypeDetail {
194                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
195                ann_custom: OptionalAppliedAnnotationSeq::default(),
196                type_name: self.descriptor().name.clone(),
197            },
198        };
199        let element = self.descriptor().element_type.as_ref().ok_or_else(|| {
200            DynamicError::inconsistent("alias type missing element_type (target)")
201        })?;
202        let related_type = descriptor_to_type_identifier(element)?;
203        let body = CompleteAliasBody {
204            related_flags: AliasMemberFlag(0),
205            related_type,
206            ann_builtin: AppliedBuiltinMemberAnnotations::default(),
207            ann_custom: OptionalAppliedAnnotationSeq::default(),
208        };
209        Ok(CompleteAliasType {
210            alias_flags: AliasTypeFlag(0),
211            header,
212            body,
213        })
214    }
215
216    fn to_complete_enum(&self) -> Result<CompleteEnumeratedType, DynamicError> {
217        use crate::type_object::common::{
218            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
219            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
220        };
221        let bit_bound = self.descriptor().bound.first().copied().unwrap_or(32);
222        let bit_bound_u16 = u16::try_from(bit_bound).unwrap_or(32);
223        let header = CompleteEnumeratedHeader {
224            common: CommonEnumeratedHeader {
225                bit_bound: bit_bound_u16,
226            },
227            detail: CompleteTypeDetail {
228                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
229                ann_custom: OptionalAppliedAnnotationSeq::default(),
230                type_name: self.descriptor().name.clone(),
231            },
232        };
233        let mut literal_seq: Vec<CompleteEnumeratedLiteral> =
234            Vec::with_capacity(self.member_count() as usize);
235        for m in self.members() {
236            let mut flags_bits: u16 = 0;
237            if m.descriptor().is_default_label {
238                flags_bits |= EnumLiteralFlag::IS_DEFAULT_LITERAL;
239            }
240            // Enum-Ordinalwert = MemberId (Convention).
241            let value = i32::try_from(m.id()).map_err(|_| {
242                DynamicError::inconsistent(format!("enum literal id {} exceeds i32 range", m.id()))
243            })?;
244            literal_seq.push(CompleteEnumeratedLiteral {
245                common: CommonEnumeratedLiteral {
246                    value,
247                    flags: EnumLiteralFlag(flags_bits),
248                },
249                detail: CompleteMemberDetail {
250                    name: m.name().to_string(),
251                    ann_builtin: AppliedBuiltinMemberAnnotations::default(),
252                    ann_custom: OptionalAppliedAnnotationSeq::default(),
253                },
254            });
255        }
256        Ok(CompleteEnumeratedType {
257            enum_flags: EnumTypeFlag(0),
258            header,
259            literal_seq,
260        })
261    }
262
263    fn to_complete_bitmask(&self) -> Result<CompleteBitmaskType, DynamicError> {
264        use crate::type_object::common::{
265            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
266            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
267        };
268        let bit_bound = self.descriptor().bound.first().copied().unwrap_or(32);
269        let bit_bound_u16 = u16::try_from(bit_bound).unwrap_or(32);
270        let detail = CompleteTypeDetail {
271            ann_builtin: AppliedBuiltinTypeAnnotations::default(),
272            ann_custom: OptionalAppliedAnnotationSeq::default(),
273            type_name: self.descriptor().name.clone(),
274        };
275        let mut flag_seq: Vec<CompleteBitflag> = Vec::with_capacity(self.member_count() as usize);
276        for m in self.members() {
277            let position = u16::try_from(m.id()).map_err(|_| {
278                DynamicError::inconsistent(format!(
279                    "bitmask flag position {} exceeds u16 range",
280                    m.id()
281                ))
282            })?;
283            flag_seq.push(CompleteBitflag {
284                common: CommonBitflag {
285                    position,
286                    flags: BitflagFlag(0),
287                },
288                detail: CompleteMemberDetail {
289                    name: m.name().to_string(),
290                    ann_builtin: AppliedBuiltinMemberAnnotations::default(),
291                    ann_custom: OptionalAppliedAnnotationSeq::default(),
292                },
293            });
294        }
295        Ok(CompleteBitmaskType {
296            bitmask_flags: BitmaskTypeFlag(0),
297            bit_bound: bit_bound_u16,
298            detail,
299            flag_seq,
300        })
301    }
302
303    /// Spec §7.3.4.4 — Bitset: ein `CompleteBitfield` je Member, mit
304    /// Bit-Startposition (`MemberDescriptor.id`), Breite
305    /// (`MemberDescriptor.bit_bound`) und Holding-Type (TypeKind-Byte des
306    /// Member-Typs, der ein primitiver Integer-Typ sein muss).
307    fn to_complete_bitset(&self) -> Result<CompleteBitsetType, DynamicError> {
308        use crate::type_object::common::{
309            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
310            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
311        };
312        let detail = CompleteTypeDetail {
313            ann_builtin: AppliedBuiltinTypeAnnotations::default(),
314            ann_custom: OptionalAppliedAnnotationSeq::default(),
315            type_name: self.descriptor().name.clone(),
316        };
317        let mut field_seq: Vec<CompleteBitfield> = Vec::with_capacity(self.member_count() as usize);
318        for m in self.members() {
319            let position = u16::try_from(m.id()).map_err(|_| {
320                DynamicError::inconsistent(format!(
321                    "bitset field position {} exceeds u16 range",
322                    m.id()
323                ))
324            })?;
325            let holder_type = match descriptor_to_type_identifier(
326                m.descriptor().member_type.as_ref(),
327            )? {
328                TypeIdentifier::Primitive(pk) => pk.to_u8(),
329                other => {
330                    return Err(DynamicError::inconsistent(format!(
331                        "bitset field {:?} holder must be a primitive integer type, got {other:?}",
332                        m.name()
333                    )));
334                }
335            };
336            let bitcount = m.descriptor().bit_bound.ok_or_else(|| {
337                DynamicError::inconsistent(format!(
338                    "bitset field {:?} missing bit_bound (bitfield width)",
339                    m.name()
340                ))
341            })?;
342            field_seq.push(CompleteBitfield {
343                common: CommonBitfield {
344                    position,
345                    flags: BitfieldFlag(0),
346                    bitcount,
347                    holder_type,
348                },
349                detail: CompleteMemberDetail {
350                    name: m.name().to_string(),
351                    ann_builtin: AppliedBuiltinMemberAnnotations::default(),
352                    ann_custom: OptionalAppliedAnnotationSeq::default(),
353                },
354            });
355        }
356        Ok(CompleteBitsetType {
357            bitset_flags: BitsetTypeFlag(0),
358            detail,
359            field_seq,
360        })
361    }
362
363    /// Spec §7.3.4.4 — Annotation: ein `CompleteAnnotationParameter` je
364    /// Member (Parameter), mit Member-Id, Parameter-Typ, Name und dem
365    /// (opaque als UTF-8-Bytes abgelegten) Default-Value aus
366    /// `MemberDescriptor.default_value`.
367    fn to_complete_annotation(&self) -> Result<CompleteAnnotationType, DynamicError> {
368        use crate::type_object::common::{
369            AppliedBuiltinTypeAnnotations, CompleteTypeDetail, OptionalAppliedAnnotationSeq,
370        };
371        let detail = CompleteTypeDetail {
372            ann_builtin: AppliedBuiltinTypeAnnotations::default(),
373            ann_custom: OptionalAppliedAnnotationSeq::default(),
374            type_name: self.descriptor().name.clone(),
375        };
376        let mut member_seq: Vec<CompleteAnnotationParameter> =
377            Vec::with_capacity(self.member_count() as usize);
378        for m in self.members() {
379            let member_type_id =
380                descriptor_to_type_identifier(m.descriptor().member_type.as_ref())?;
381            let default_value = m
382                .descriptor()
383                .default_value
384                .as_deref()
385                .map(|s| s.as_bytes().to_vec())
386                .unwrap_or_default();
387            member_seq.push(CompleteAnnotationParameter {
388                member_id: m.id(),
389                member_flags: AnnotationParameterFlag(0),
390                member_type_id,
391                name: m.name().to_string(),
392                default_value,
393            });
394        }
395        Ok(CompleteAnnotationType {
396            annotation_flag: AnnotationTypeFlag(0),
397            detail,
398            member_seq,
399        })
400    }
401
402    fn to_complete_union(&self) -> Result<CompleteUnionType, DynamicError> {
403        use crate::type_object::common::{
404            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
405            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
406        };
407        let union_flags = UnionTypeFlag(extensibility_to_flag_bits(
408            self.descriptor().extensibility_kind,
409        ));
410        let header = CompleteUnionHeader {
411            detail: CompleteTypeDetail {
412                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
413                ann_custom: OptionalAppliedAnnotationSeq::default(),
414                type_name: self.descriptor().name.clone(),
415            },
416        };
417        let disc_descriptor = self
418            .descriptor()
419            .discriminator_type
420            .as_ref()
421            .ok_or_else(|| DynamicError::inconsistent("union type missing discriminator_type"))?;
422        let disc_type = descriptor_to_type_identifier(disc_descriptor)?;
423        let discriminator = CompleteDiscriminatorMember {
424            common: CommonDiscriminatorMember {
425                member_flags: UnionDiscriminatorFlag(0),
426                type_id: disc_type,
427            },
428            ann_builtin: AppliedBuiltinTypeAnnotations::default(),
429            ann_custom: OptionalAppliedAnnotationSeq::default(),
430        };
431        let mut member_seq: Vec<CompleteUnionMember> =
432            Vec::with_capacity(self.member_count() as usize);
433        for m in self.members() {
434            let mut flags_bits: u16 = 0;
435            if m.descriptor().is_default_label {
436                flags_bits |= UnionMemberFlag::IS_DEFAULT;
437            }
438            // Spec §7.3.4.5.3.2 — case labels are `long` (i32).
439            let label_seq: Vec<i32> = m
440                .descriptor()
441                .label
442                .iter()
443                .map(|&v| i32::try_from(v).unwrap_or_default())
444                .collect();
445            let common = CommonUnionMember {
446                member_id: m.id(),
447                member_flags: UnionMemberFlag(flags_bits),
448                type_id: descriptor_to_type_identifier(m.descriptor().member_type.as_ref())?,
449                label_seq,
450            };
451            let detail = CompleteMemberDetail {
452                name: m.name().to_string(),
453                ann_builtin: AppliedBuiltinMemberAnnotations::default(),
454                ann_custom: OptionalAppliedAnnotationSeq::default(),
455            };
456            member_seq.push(CompleteUnionMember { common, detail });
457        }
458        Ok(CompleteUnionType {
459            union_flags,
460            header,
461            discriminator,
462            member_seq,
463        })
464    }
465
466    fn to_complete_struct(&self) -> Result<CompleteStructType, DynamicError> {
467        use crate::type_object::common::{
468            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
469            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
470        };
471        let struct_flags = StructTypeFlag(extensibility_to_flag_bits(
472            self.descriptor().extensibility_kind,
473        ));
474        let header = CompleteStructHeader {
475            base_type: TypeIdentifier::None,
476            detail: CompleteTypeDetail {
477                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
478                ann_custom: OptionalAppliedAnnotationSeq::default(),
479                type_name: self.descriptor().name.clone(),
480            },
481        };
482        let mut member_seq: Vec<CompleteStructMember> =
483            Vec::with_capacity(self.member_count() as usize);
484        for m in self.members() {
485            let mut flags_bits: u16 = 0;
486            if m.descriptor().is_key {
487                flags_bits |= StructMemberFlag::IS_KEY;
488            }
489            if m.descriptor().is_optional {
490                flags_bits |= StructMemberFlag::IS_OPTIONAL;
491            }
492            if m.descriptor().is_must_understand {
493                flags_bits |= StructMemberFlag::IS_MUST_UNDERSTAND;
494            }
495            if m.descriptor().is_shared {
496                flags_bits |= StructMemberFlag::IS_EXTERNAL;
497            }
498            let common = CommonStructMember {
499                member_id: m.id(),
500                member_flags: StructMemberFlag(flags_bits),
501                member_type_id: descriptor_to_type_identifier(m.descriptor().member_type.as_ref())?,
502            };
503            let detail = CompleteMemberDetail {
504                name: m.name().to_string(),
505                ann_builtin: AppliedBuiltinMemberAnnotations::default(),
506                ann_custom: OptionalAppliedAnnotationSeq::default(),
507            };
508            member_seq.push(CompleteStructMember { common, detail });
509        }
510        Ok(CompleteStructType {
511            struct_flags,
512            header,
513            member_seq,
514        })
515    }
516}
517
518const fn extensibility_to_flag_bits(ext: ExtensibilityKind) -> u16 {
519    match ext {
520        ExtensibilityKind::Final => StructTypeFlag::IS_FINAL,
521        ExtensibilityKind::Appendable => StructTypeFlag::IS_APPENDABLE,
522        ExtensibilityKind::Mutable => StructTypeFlag::IS_MUTABLE,
523    }
524}
525
526const fn flag_bits_to_extensibility(flags: u16) -> ExtensibilityKind {
527    if flags & StructTypeFlag::IS_FINAL != 0 {
528        ExtensibilityKind::Final
529    } else if flags & StructTypeFlag::IS_MUTABLE != 0 {
530        ExtensibilityKind::Mutable
531    } else {
532        ExtensibilityKind::Appendable
533    }
534}
535
536/// XTypes §7.3.4.4 — builds a `CompleteCollectionElement` (element or key
537/// description) from a `TypeDescriptor`.
538fn complete_collection_element(
539    desc: &TypeDescriptor,
540) -> Result<CompleteCollectionElement, DynamicError> {
541    use crate::type_object::common::{
542        AppliedBuiltinMemberAnnotations, OptionalAppliedAnnotationSeq,
543    };
544    Ok(CompleteCollectionElement {
545        common: CommonCollectionElement {
546            element_flags: CollectionElementFlag(0),
547            type_id: descriptor_to_type_identifier(desc)?,
548        },
549        ann_builtin: AppliedBuiltinMemberAnnotations::default(),
550        ann_custom: OptionalAppliedAnnotationSeq::default(),
551    })
552}
553
554/// Maps a member `TypeDescriptor` to a matching `TypeIdentifier`. Only
555/// primitive + string + sequence + array are encoded inline; composite
556/// members require a TypeRegistry lookup strategy via
557/// `zerodds_types::resolve::TypeRegistry`.
558fn descriptor_to_type_identifier(desc: &TypeDescriptor) -> Result<TypeIdentifier, DynamicError> {
559    match desc.kind {
560        TypeKind::Boolean => Ok(TypeIdentifier::Primitive(PrimitiveKind::Boolean)),
561        TypeKind::Byte => Ok(TypeIdentifier::Primitive(PrimitiveKind::Byte)),
562        TypeKind::Int8 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Int8)),
563        TypeKind::UInt8 => Ok(TypeIdentifier::Primitive(PrimitiveKind::UInt8)),
564        TypeKind::Int16 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Int16)),
565        TypeKind::UInt16 => Ok(TypeIdentifier::Primitive(PrimitiveKind::UInt16)),
566        TypeKind::Int32 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Int32)),
567        TypeKind::UInt32 => Ok(TypeIdentifier::Primitive(PrimitiveKind::UInt32)),
568        TypeKind::Int64 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Int64)),
569        TypeKind::UInt64 => Ok(TypeIdentifier::Primitive(PrimitiveKind::UInt64)),
570        TypeKind::Float32 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Float32)),
571        TypeKind::Float64 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Float64)),
572        TypeKind::Float128 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Float128)),
573        TypeKind::Char8 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Char8)),
574        TypeKind::Char16 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Char16)),
575        TypeKind::String8 => {
576            let bound = desc.bound.first().copied().unwrap_or(0);
577            if bound <= u32::from(u8::MAX) {
578                Ok(TypeIdentifier::String8Small { bound: bound as u8 })
579            } else {
580                Ok(TypeIdentifier::String8Large { bound })
581            }
582        }
583        TypeKind::String16 => {
584            let bound = desc.bound.first().copied().unwrap_or(0);
585            if bound <= u32::from(u8::MAX) {
586                Ok(TypeIdentifier::String16Small { bound: bound as u8 })
587            } else {
588                Ok(TypeIdentifier::String16Large { bound })
589            }
590        }
591        TypeKind::Structure | TypeKind::Union | TypeKind::Enumeration | TypeKind::Alias => {
592            // Bridge phase 1: composite members carry `TypeIdentifier::None`
593            // — the TypeRegistry (C4.2) later resolves via the name
594            // against an EquivalenceHash. Roundtrip tests set this
595            // information separately.
596            Ok(TypeIdentifier::None)
597        }
598        kind => Err(DynamicError::unsupported(format!(
599            "descriptor_to_type_identifier: {kind:?} not yet covered"
600        ))),
601    }
602}
603
604// ----------------------------------------------------------------------
605// TypeObject → DynamicType (Spec §7.6.4)
606// ----------------------------------------------------------------------
607
608impl DynamicTypeBuilderFactory {
609    /// Spec §7.6.4 / §7.5.5.1.4 `create_type_w_type_object(type_object)`.
610    ///
611    /// Scope:
612    /// - `CompleteStructType` with primitive + string members.
613    /// - `MinimalStructType` as read-only with hash names.
614    /// - Other kinds: `Unsupported`.
615    ///
616    /// # Errors
617    /// `Unsupported` for kinds outside the current implementation scope.
618    pub fn create_type_w_type_object(
619        type_obj: &TypeObject,
620    ) -> Result<DynamicTypeBuilder, DynamicError> {
621        match type_obj {
622            TypeObject::Complete(c) => match c {
623                CompleteTypeObject::Struct(s) => complete_struct_to_builder(s),
624                other => Err(DynamicError::unsupported(format!(
625                    "complete-typeobject kind {} not yet supported",
626                    other_kind_name(other)
627                ))),
628            },
629            TypeObject::Minimal(_) => Err(DynamicError::unsupported(
630                "minimal-typeobject → dynamic-type pending C4.2 TypeRegistry",
631            )),
632        }
633    }
634}
635
636fn complete_struct_to_builder(s: &CompleteStructType) -> Result<DynamicTypeBuilder, DynamicError> {
637    let mut desc = TypeDescriptor::structure(s.header.detail.type_name.clone());
638    desc.extensibility_kind = flag_bits_to_extensibility(s.struct_flags.0);
639    if (s.struct_flags.0 & StructTypeFlag::IS_NESTED) != 0 {
640        desc.is_nested = true;
641    }
642    let mut b = DynamicTypeBuilderFactory::create_type(desc)?;
643    for (idx, m) in s.member_seq.iter().enumerate() {
644        let kind = type_id_to_kind(&m.common.member_type_id)?;
645        let member_type = type_id_to_descriptor(&m.common.member_type_id, kind)?;
646        let mut md = MemberDescriptor::new(m.detail.name.clone(), m.common.member_id, member_type);
647        md.index = u32::try_from(idx).unwrap_or(u32::MAX);
648        md.is_key = (m.common.member_flags.0 & StructMemberFlag::IS_KEY) != 0;
649        md.is_optional = (m.common.member_flags.0 & StructMemberFlag::IS_OPTIONAL) != 0;
650        md.is_must_understand =
651            (m.common.member_flags.0 & StructMemberFlag::IS_MUST_UNDERSTAND) != 0;
652        md.is_shared = (m.common.member_flags.0 & StructMemberFlag::IS_EXTERNAL) != 0;
653        b.add_member(md)?;
654    }
655    Ok(b)
656}
657
658const fn other_kind_name(c: &CompleteTypeObject) -> &'static str {
659    match c {
660        CompleteTypeObject::Alias(_) => "alias",
661        CompleteTypeObject::Annotation(_) => "annotation",
662        CompleteTypeObject::Struct(_) => "struct",
663        CompleteTypeObject::Union(_) => "union",
664        CompleteTypeObject::Bitset(_) => "bitset",
665        CompleteTypeObject::Sequence(_) => "sequence",
666        CompleteTypeObject::Array(_) => "array",
667        CompleteTypeObject::Map(_) => "map",
668        CompleteTypeObject::Enumerated(_) => "enum",
669        CompleteTypeObject::Bitmask(_) => "bitmask",
670    }
671}
672
673fn type_id_to_kind(ti: &TypeIdentifier) -> Result<TypeKind, DynamicError> {
674    Ok(match ti {
675        TypeIdentifier::None => TypeKind::NoType,
676        TypeIdentifier::Primitive(p) => primitive_kind_to_type_kind(*p),
677        TypeIdentifier::String8Small { .. } | TypeIdentifier::String8Large { .. } => {
678            TypeKind::String8
679        }
680        TypeIdentifier::String16Small { .. } | TypeIdentifier::String16Large { .. } => {
681            TypeKind::String16
682        }
683        TypeIdentifier::PlainSequenceSmall { .. } | TypeIdentifier::PlainSequenceLarge { .. } => {
684            TypeKind::Sequence
685        }
686        TypeIdentifier::PlainArraySmall { .. } | TypeIdentifier::PlainArrayLarge { .. } => {
687            TypeKind::Array
688        }
689        TypeIdentifier::PlainMapSmall { .. } | TypeIdentifier::PlainMapLarge { .. } => {
690            TypeKind::Map
691        }
692        TypeIdentifier::EquivalenceHashMinimal(_) | TypeIdentifier::EquivalenceHashComplete(_) => {
693            // Composite via TypeRegistry lookup — mark as Structure
694            // by default; resolving it correctly is C4.2.
695            TypeKind::Structure
696        }
697        other => {
698            return Err(DynamicError::unsupported(format!(
699                "type_id_to_kind: {other:?} not yet supported"
700            )));
701        }
702    })
703}
704
705const fn primitive_kind_to_type_kind(p: PrimitiveKind) -> TypeKind {
706    match p {
707        PrimitiveKind::Boolean => TypeKind::Boolean,
708        PrimitiveKind::Byte => TypeKind::Byte,
709        PrimitiveKind::Int8 => TypeKind::Int8,
710        PrimitiveKind::UInt8 => TypeKind::UInt8,
711        PrimitiveKind::Int16 => TypeKind::Int16,
712        PrimitiveKind::UInt16 => TypeKind::UInt16,
713        PrimitiveKind::Int32 => TypeKind::Int32,
714        PrimitiveKind::UInt32 => TypeKind::UInt32,
715        PrimitiveKind::Int64 => TypeKind::Int64,
716        PrimitiveKind::UInt64 => TypeKind::UInt64,
717        PrimitiveKind::Float32 => TypeKind::Float32,
718        PrimitiveKind::Float64 => TypeKind::Float64,
719        PrimitiveKind::Float128 => TypeKind::Float128,
720        PrimitiveKind::Char8 => TypeKind::Char8,
721        PrimitiveKind::Char16 => TypeKind::Char16,
722    }
723}
724
725/// zerodds-lint: recursion-depth 16
726///
727/// Recursive calls via the PlainSequence/PlainArray element. Depth is
728/// implicitly capped by [`TypeIdentifier::MAX_DECODE_DEPTH`] — the
729/// TypeIdentifier can be at most 16 layers deep on wire-decode, after
730/// which decoding fails with `LengthExceeded`.
731fn type_id_to_descriptor(
732    ti: &TypeIdentifier,
733    kind: TypeKind,
734) -> Result<TypeDescriptor, DynamicError> {
735    if kind.is_primitive() {
736        return Ok(TypeDescriptor::primitive(
737            kind,
738            super::type_::primitive_name(kind).to_string(),
739        ));
740    }
741    Ok(match ti {
742        TypeIdentifier::String8Small { bound } => TypeDescriptor::string8(u32::from(*bound)),
743        TypeIdentifier::String8Large { bound } => TypeDescriptor::string8(*bound),
744        TypeIdentifier::String16Small { bound } => TypeDescriptor::string16(u32::from(*bound)),
745        TypeIdentifier::String16Large { bound } => TypeDescriptor::string16(*bound),
746        TypeIdentifier::None => TypeDescriptor {
747            kind: TypeKind::Structure,
748            name: String::from("<unresolved>"),
749            base_type: None,
750            discriminator_type: None,
751            bound: Vec::new(),
752            element_type: None,
753            key_element_type: None,
754            extensibility_kind: ExtensibilityKind::default(),
755            is_nested: false,
756        },
757        TypeIdentifier::EquivalenceHashMinimal(_) | TypeIdentifier::EquivalenceHashComplete(_) => {
758            // We only know the name after a TypeRegistry lookup
759            // (C4.2). Until then: placeholder descriptor with Kind=Structure.
760            TypeDescriptor {
761                kind: TypeKind::Structure,
762                name: String::from("<typeref>"),
763                base_type: None,
764                discriminator_type: None,
765                bound: Vec::new(),
766                element_type: None,
767                key_element_type: None,
768                extensibility_kind: ExtensibilityKind::default(),
769                is_nested: false,
770            }
771        }
772        TypeIdentifier::PlainSequenceSmall { bound, element, .. } => {
773            let elem_kind = type_id_to_kind(element)?;
774            TypeDescriptor::sequence(
775                "<seq>".to_string(),
776                type_id_to_descriptor(element, elem_kind)?,
777                u32::from(*bound),
778            )
779        }
780        TypeIdentifier::PlainSequenceLarge { bound, element, .. } => {
781            let elem_kind = type_id_to_kind(element)?;
782            TypeDescriptor::sequence(
783                "<seq>".to_string(),
784                type_id_to_descriptor(element, elem_kind)?,
785                *bound,
786            )
787        }
788        TypeIdentifier::PlainArraySmall {
789            array_bounds,
790            element,
791            ..
792        } => {
793            let elem_kind = type_id_to_kind(element)?;
794            TypeDescriptor::array(
795                "<arr>".to_string(),
796                type_id_to_descriptor(element, elem_kind)?,
797                array_bounds.iter().map(|b| u32::from(*b)).collect(),
798            )
799        }
800        TypeIdentifier::PlainArrayLarge {
801            array_bounds,
802            element,
803            ..
804        } => {
805            let elem_kind = type_id_to_kind(element)?;
806            TypeDescriptor::array(
807                "<arr>".to_string(),
808                type_id_to_descriptor(element, elem_kind)?,
809                array_bounds.clone(),
810            )
811        }
812        other => {
813            return Err(DynamicError::unsupported(format!(
814                "type_id_to_descriptor: {other:?} not yet supported"
815            )));
816        }
817    })
818}
819
820#[cfg(test)]
821#[allow(
822    clippy::unwrap_used,
823    clippy::expect_used,
824    clippy::panic,
825    clippy::unreachable
826)]
827mod tests {
828    use super::*;
829    use crate::dynamic::DynamicTypeBuilderFactory;
830
831    fn build_int_struct() -> DynamicType {
832        let mut b = DynamicTypeBuilderFactory::create_struct("::Sensor");
833        b.add_struct_member("id", 1, TypeDescriptor::primitive(TypeKind::Int64, "int64"))
834            .unwrap();
835        b.add_struct_member(
836            "temp",
837            2,
838            TypeDescriptor::primitive(TypeKind::Float64, "double"),
839        )
840        .unwrap();
841        b.add_struct_member("name", 3, TypeDescriptor::string8(64))
842            .unwrap();
843        b.build().unwrap()
844    }
845
846    #[test]
847    fn dynamic_struct_to_typeobject_complete() {
848        let t = build_int_struct();
849        let to = t.to_type_object().unwrap();
850        match to {
851            TypeObject::Complete(CompleteTypeObject::Struct(s)) => {
852                assert_eq!(s.header.detail.type_name, "::Sensor");
853                assert_eq!(s.member_seq.len(), 3);
854                assert_eq!(s.member_seq[0].detail.name, "id");
855            }
856            _ => panic!("to_type_object on Structure must return Complete::Struct"),
857        }
858    }
859
860    #[test]
861    fn typeobject_complete_struct_back_to_dynamic_type() {
862        let t = build_int_struct();
863        let to = t.to_type_object().unwrap();
864        let b = DynamicTypeBuilderFactory::create_type_w_type_object(&to).unwrap();
865        let t2 = b.build().unwrap();
866        assert_eq!(t2.name(), "::Sensor");
867        assert_eq!(t2.member_count(), 3);
868        assert_eq!(t2.member_by_id(1).unwrap().name(), "id");
869        assert_eq!(t2.member_by_id(2).unwrap().name(), "temp");
870        assert_eq!(t2.member_by_id(3).unwrap().name(), "name");
871    }
872
873    #[test]
874    fn roundtrip_dynamic_to_typeobject_to_dynamic_equals_logically() {
875        let t = build_int_struct();
876        let to = t.to_type_object().unwrap();
877        let b = DynamicTypeBuilderFactory::create_type_w_type_object(&to).unwrap();
878        let t2 = b.build().unwrap();
879        // Deep equality on the type built by the bridge.
880        assert!(t.equals(&t2), "roundtrip failed: {t:?} vs {t2:?}");
881    }
882
883    #[test]
884    fn unsupported_kind_to_typeobject_yields_unsupported_error() {
885        let prim = DynamicTypeBuilderFactory::get_primitive_type(TypeKind::Int32).unwrap();
886        let err = prim.to_type_object().unwrap_err();
887        assert!(matches!(err, DynamicError::Unsupported(_)));
888    }
889
890    #[test]
891    fn dynamic_alias_to_typeobject_complete() {
892        // alias with element_type = int32.
893        let mut desc = TypeDescriptor::primitive(TypeKind::Int32, "int32");
894        desc.kind = TypeKind::Alias;
895        desc.name = "::SensorId".to_string();
896        desc.element_type = Some(alloc::boxed::Box::new(TypeDescriptor::primitive(
897            TypeKind::Int32,
898            "int32",
899        )));
900        let t = DynamicTypeBuilderFactory::create_type(desc)
901            .unwrap()
902            .build()
903            .unwrap();
904        let to = t.to_type_object().expect("alias bridge ok");
905        match to {
906            TypeObject::Complete(CompleteTypeObject::Alias(a)) => {
907                assert_eq!(a.header.detail.type_name, "::SensorId");
908                assert!(matches!(a.body.related_type, TypeIdentifier::Primitive(_)));
909            }
910            other => panic!("expected Alias, got {other:?}"),
911        }
912    }
913
914    #[test]
915    fn dynamic_enum_to_typeobject_complete() {
916        let mut desc = TypeDescriptor::primitive(TypeKind::Enumeration, "::Color");
917        desc.kind = TypeKind::Enumeration;
918        desc.bound = alloc::vec![32];
919        let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
920        for (id, name) in [(0u32, "RED"), (1u32, "GREEN"), (2u32, "BLUE")] {
921            let m = MemberDescriptor::new(
922                name,
923                id,
924                TypeDescriptor::primitive(TypeKind::Int32, "int32"),
925            );
926            b.add_member(m).unwrap();
927        }
928        let t = b.build().unwrap();
929        let to = t.to_type_object().expect("enum bridge ok");
930        match to {
931            TypeObject::Complete(CompleteTypeObject::Enumerated(e)) => {
932                assert_eq!(e.literal_seq.len(), 3);
933                assert_eq!(e.literal_seq[0].detail.name, "RED");
934                assert_eq!(e.literal_seq[2].common.value, 2);
935            }
936            other => panic!("expected Enumerated, got {other:?}"),
937        }
938    }
939
940    #[test]
941    fn dynamic_bitmask_to_typeobject_complete() {
942        let mut desc = TypeDescriptor::primitive(TypeKind::Bitmask, "::Flags");
943        desc.kind = TypeKind::Bitmask;
944        desc.bound = alloc::vec![8];
945        let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
946        for (pos, name) in [(0u32, "A"), (3u32, "D"), (7u32, "H")] {
947            let m = MemberDescriptor::new(
948                name,
949                pos,
950                TypeDescriptor::primitive(TypeKind::Boolean, "boolean"),
951            );
952            b.add_member(m).unwrap();
953        }
954        let t = b.build().unwrap();
955        let to = t.to_type_object().expect("bitmask bridge ok");
956        match to {
957            TypeObject::Complete(CompleteTypeObject::Bitmask(bm)) => {
958                assert_eq!(bm.bit_bound, 8);
959                assert_eq!(bm.flag_seq.len(), 3);
960                assert_eq!(bm.flag_seq[1].common.position, 3);
961            }
962            other => panic!("expected Bitmask, got {other:?}"),
963        }
964    }
965
966    #[test]
967    fn dynamic_bitset_to_typeobject_complete() {
968        let mut desc = TypeDescriptor::primitive(TypeKind::Bitset, "::Reg");
969        desc.kind = TypeKind::Bitset;
970        let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
971        for (pos, name, width) in [(0u32, "lo", 3u8), (3u32, "hi", 5u8)] {
972            let mut m = MemberDescriptor::new(
973                name,
974                pos,
975                TypeDescriptor::primitive(TypeKind::UInt8, "uint8"),
976            );
977            m.bit_bound = Some(width);
978            b.add_member(m).unwrap();
979        }
980        let t = b.build().unwrap();
981        let to = t.to_type_object().expect("bitset bridge ok");
982        match to {
983            TypeObject::Complete(CompleteTypeObject::Bitset(bs)) => {
984                assert_eq!(bs.field_seq.len(), 2);
985                assert_eq!(bs.field_seq[0].common.position, 0);
986                assert_eq!(bs.field_seq[0].common.bitcount, 3);
987                assert_eq!(bs.field_seq[0].detail.name, "lo");
988                assert_eq!(bs.field_seq[1].common.position, 3);
989                assert_eq!(bs.field_seq[1].common.bitcount, 5);
990                assert_eq!(
991                    bs.field_seq[1].common.holder_type,
992                    PrimitiveKind::UInt8.to_u8()
993                );
994            }
995            other => panic!("expected Bitset, got {other:?}"),
996        }
997    }
998
999    #[test]
1000    fn dynamic_annotation_to_typeobject_complete() {
1001        let mut desc = TypeDescriptor::primitive(TypeKind::Annotation, "::Range");
1002        desc.kind = TypeKind::Annotation;
1003        let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1004        let mut m = MemberDescriptor::new(
1005            "max",
1006            1,
1007            TypeDescriptor::primitive(TypeKind::Int32, "int32"),
1008        );
1009        m.default_value = Some("100".into());
1010        b.add_member(m).unwrap();
1011        let t = b.build().unwrap();
1012        let to = t.to_type_object().expect("annotation bridge ok");
1013        match to {
1014            TypeObject::Complete(CompleteTypeObject::Annotation(a)) => {
1015                assert_eq!(a.member_seq.len(), 1);
1016                assert_eq!(a.member_seq[0].name, "max");
1017                assert_eq!(a.member_seq[0].member_id, 1);
1018                assert_eq!(a.member_seq[0].default_value, b"100".to_vec());
1019                assert!(matches!(
1020                    a.member_seq[0].member_type_id,
1021                    TypeIdentifier::Primitive(PrimitiveKind::Int32)
1022                ));
1023            }
1024            other => panic!("expected Annotation, got {other:?}"),
1025        }
1026    }
1027
1028    #[test]
1029    fn dynamic_sequence_to_typeobject_complete() {
1030        let desc = TypeDescriptor::sequence(
1031            "::Seq",
1032            TypeDescriptor::primitive(TypeKind::Int32, "int32"),
1033            10,
1034        );
1035        let t = DynamicTypeBuilderFactory::create_type(desc)
1036            .unwrap()
1037            .build()
1038            .unwrap();
1039        match t.to_type_object().expect("sequence bridge ok") {
1040            TypeObject::Complete(CompleteTypeObject::Sequence(s)) => {
1041                assert_eq!(s.bound, 10);
1042                assert_eq!(s.detail.type_name, "::Seq");
1043                assert!(matches!(
1044                    s.element.common.type_id,
1045                    TypeIdentifier::Primitive(PrimitiveKind::Int32)
1046                ));
1047            }
1048            other => panic!("expected Sequence, got {other:?}"),
1049        }
1050    }
1051
1052    #[test]
1053    fn dynamic_array_to_typeobject_complete() {
1054        let desc = TypeDescriptor::array(
1055            "::Arr",
1056            TypeDescriptor::primitive(TypeKind::Float64, "double"),
1057            alloc::vec![3, 4],
1058        );
1059        let t = DynamicTypeBuilderFactory::create_type(desc)
1060            .unwrap()
1061            .build()
1062            .unwrap();
1063        match t.to_type_object().expect("array bridge ok") {
1064            TypeObject::Complete(CompleteTypeObject::Array(a)) => {
1065                assert_eq!(a.bound_seq, alloc::vec![3, 4]);
1066                assert!(matches!(
1067                    a.element.common.type_id,
1068                    TypeIdentifier::Primitive(PrimitiveKind::Float64)
1069                ));
1070            }
1071            other => panic!("expected Array, got {other:?}"),
1072        }
1073    }
1074
1075    #[test]
1076    fn dynamic_map_to_typeobject_complete() {
1077        let desc = TypeDescriptor::map(
1078            "::Map",
1079            TypeDescriptor::primitive(TypeKind::Int32, "int32"),
1080            TypeDescriptor::primitive(TypeKind::Boolean, "boolean"),
1081            7,
1082        );
1083        let t = DynamicTypeBuilderFactory::create_type(desc)
1084            .unwrap()
1085            .build()
1086            .unwrap();
1087        match t.to_type_object().expect("map bridge ok") {
1088            TypeObject::Complete(CompleteTypeObject::Map(m)) => {
1089                assert_eq!(m.bound, 7);
1090                assert!(matches!(
1091                    m.key.common.type_id,
1092                    TypeIdentifier::Primitive(PrimitiveKind::Int32)
1093                ));
1094                assert!(matches!(
1095                    m.element.common.type_id,
1096                    TypeIdentifier::Primitive(PrimitiveKind::Boolean)
1097                ));
1098            }
1099            other => panic!("expected Map, got {other:?}"),
1100        }
1101    }
1102
1103    #[test]
1104    fn dynamic_union_to_typeobject_complete() {
1105        let disc = TypeDescriptor::primitive(TypeKind::Int32, "int32");
1106        let t = {
1107            let mut b = DynamicTypeBuilderFactory::create_union("::Shape", disc).unwrap();
1108            let mut m1 = MemberDescriptor::new(
1109                "circle",
1110                1,
1111                TypeDescriptor::primitive(TypeKind::Float64, "double"),
1112            );
1113            m1.label = alloc::vec![1];
1114            let mut m2 = MemberDescriptor::new(
1115                "square",
1116                2,
1117                TypeDescriptor::primitive(TypeKind::Float64, "double"),
1118            );
1119            m2.label = alloc::vec![2, 3];
1120            b.add_member(m1).unwrap();
1121            b.add_member(m2).unwrap();
1122            b.build().unwrap()
1123        };
1124        let to = t.to_type_object().expect("union bridge ok");
1125        match to {
1126            TypeObject::Complete(CompleteTypeObject::Union(u)) => {
1127                assert_eq!(u.member_seq.len(), 2);
1128                assert_eq!(u.member_seq[0].detail.name, "circle");
1129                assert_eq!(u.member_seq[1].common.label_seq, alloc::vec![2, 3]);
1130                assert!(matches!(
1131                    u.discriminator.common.type_id,
1132                    TypeIdentifier::Primitive(PrimitiveKind::Int32)
1133                ));
1134            }
1135            other => panic!("expected Union, got {other:?}"),
1136        }
1137    }
1138
1139    #[test]
1140    fn create_type_w_minimal_typeobject_is_unsupported() {
1141        use crate::type_object::MinimalTypeObject;
1142        use crate::type_object::minimal::{MinimalStructHeader, MinimalStructType};
1143        let m = MinimalStructType {
1144            struct_flags: StructTypeFlag::default(),
1145            header: MinimalStructHeader {
1146                base_type: TypeIdentifier::None,
1147            },
1148            member_seq: alloc::vec![],
1149        };
1150        let to = TypeObject::Minimal(MinimalTypeObject::Struct(m));
1151        let err = DynamicTypeBuilderFactory::create_type_w_type_object(&to).unwrap_err();
1152        assert!(matches!(err, DynamicError::Unsupported(_)));
1153    }
1154}