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.
15//!
16//! The reverse direction (`create_type_w_type_object`) resolves a top-level
17//! Complete TypeObject of any of the 10 kinds; the registry-aware
18//! `create_type_w_type_object_in` additionally follows composite member
19//! `EquivalenceHash` references through a [`crate::resolve::TypeRegistry`] so a
20//! struct/union with nested struct/union/enum/bitmask/bitset MEMBERS yields a
21//! fully-populated `DynamicType`. Residual (honest `Unsupported`): a COLLECTION
22//! whose element is itself a composite keeps a shallow element descriptor (the
23//! `TypeDescriptor::element_type` model holds a descriptor, not a full
24//! `DynamicType`), Minimal TypeObjects, and alias-to-composite targets.
25
26use alloc::format;
27use alloc::string::{String, ToString};
28use alloc::vec::Vec;
29
30use crate::type_identifier::{PrimitiveKind, TypeIdentifier};
31use crate::type_object::common::{CommonStructMember, CommonUnionMember};
32use crate::type_object::complete::{
33    CompleteAliasBody, CompleteAliasHeader, CompleteAliasType, CompleteAnnotationParameter,
34    CompleteAnnotationType, CompleteArrayType, CompleteBitfield, CompleteBitflag,
35    CompleteBitmaskType, CompleteBitsetType, CompleteCollectionElement,
36    CompleteDiscriminatorMember, CompleteEnumeratedHeader, CompleteEnumeratedLiteral,
37    CompleteEnumeratedType, CompleteMapType, CompleteSequenceType, CompleteStructHeader,
38    CompleteStructMember, CompleteStructType, CompleteUnionHeader, CompleteUnionMember,
39    CompleteUnionType,
40};
41use crate::type_object::flags::{
42    AliasMemberFlag, AliasTypeFlag, AnnotationParameterFlag, AnnotationTypeFlag, BitfieldFlag,
43    BitflagFlag, BitmaskTypeFlag, BitsetTypeFlag, CollectionElementFlag, CollectionTypeFlag,
44    EnumLiteralFlag, EnumTypeFlag, StructMemberFlag, StructTypeFlag, UnionDiscriminatorFlag,
45    UnionMemberFlag, UnionTypeFlag,
46};
47use crate::type_object::minimal::CommonDiscriminatorMember;
48use crate::type_object::minimal::{
49    CommonBitfield, CommonBitflag, CommonCollectionElement, CommonEnumeratedHeader,
50    CommonEnumeratedLiteral,
51};
52use crate::type_object::{CompleteTypeObject, TypeObject};
53
54use super::builder::{DynamicTypeBuilder, DynamicTypeBuilderFactory};
55use super::descriptor::{ExtensibilityKind, MemberDescriptor, TypeDescriptor, TypeKind};
56use super::error::DynamicError;
57use super::type_::DynamicType;
58
59// ----------------------------------------------------------------------
60// DynamicType → TypeObject (spec §7.6.3 — for discovery + TypeLookup)
61// ----------------------------------------------------------------------
62
63impl DynamicType {
64    /// Spec §7.6.3 — converts this DynamicType into a
65    /// `CompleteTypeObject`. Always returns complete (not minimal),
66    /// because DynamicType carries the names + annotations.
67    ///
68    /// Scope:
69    /// - Struct with primitive + string + sequence/array members.
70    /// - Union, enum, alias as their own helper methods (see module doc).
71    ///
72    /// # Errors
73    /// `Unsupported` for not-yet-implemented kinds,
74    /// `Inconsistent` if the type is malformed.
75    pub fn to_type_object(&self) -> Result<TypeObject, DynamicError> {
76        match self.kind() {
77            TypeKind::Structure => Ok(TypeObject::Complete(CompleteTypeObject::Struct(
78                self.to_complete_struct()?,
79            ))),
80            TypeKind::Union => Ok(TypeObject::Complete(CompleteTypeObject::Union(
81                self.to_complete_union()?,
82            ))),
83            TypeKind::Enumeration => Ok(TypeObject::Complete(CompleteTypeObject::Enumerated(
84                self.to_complete_enum()?,
85            ))),
86            TypeKind::Bitmask => Ok(TypeObject::Complete(CompleteTypeObject::Bitmask(
87                self.to_complete_bitmask()?,
88            ))),
89            TypeKind::Alias => Ok(TypeObject::Complete(CompleteTypeObject::Alias(
90                self.to_complete_alias()?,
91            ))),
92            TypeKind::Sequence => Ok(TypeObject::Complete(CompleteTypeObject::Sequence(
93                self.to_complete_sequence()?,
94            ))),
95            TypeKind::Array => Ok(TypeObject::Complete(CompleteTypeObject::Array(
96                self.to_complete_array()?,
97            ))),
98            TypeKind::Map => Ok(TypeObject::Complete(CompleteTypeObject::Map(
99                self.to_complete_map()?,
100            ))),
101            TypeKind::Bitset => Ok(TypeObject::Complete(CompleteTypeObject::Bitset(
102                self.to_complete_bitset()?,
103            ))),
104            TypeKind::Annotation => Ok(TypeObject::Complete(CompleteTypeObject::Annotation(
105                self.to_complete_annotation()?,
106            ))),
107            other => Err(DynamicError::unsupported(format!(
108                "to_type_object: {other:?} is a primitive/no-type kind without TypeObject",
109            ))),
110        }
111    }
112
113    /// XTypes §7.3.4.4 — `sequence<T>` → `CompleteSequenceType`.
114    /// `bound = 0` bedeutet unbounded.
115    fn to_complete_sequence(&self) -> Result<CompleteSequenceType, DynamicError> {
116        use crate::type_object::common::{
117            AppliedBuiltinTypeAnnotations, CompleteTypeDetail, OptionalAppliedAnnotationSeq,
118        };
119        let element = self
120            .descriptor()
121            .element_type
122            .as_ref()
123            .ok_or_else(|| DynamicError::inconsistent("sequence type missing element_type"))?;
124        let bound = self.descriptor().bound.first().copied().unwrap_or(0);
125        Ok(CompleteSequenceType {
126            collection_flag: CollectionTypeFlag(0),
127            bound,
128            detail: CompleteTypeDetail {
129                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
130                ann_custom: OptionalAppliedAnnotationSeq::default(),
131                type_name: self.descriptor().name.clone(),
132            },
133            element: complete_collection_element(element)?,
134        })
135    }
136
137    /// XTypes §7.3.4.4 — `T[N]` → `CompleteArrayType`.
138    /// `bound_seq` traegt alle Array-Dimensionen.
139    fn to_complete_array(&self) -> Result<CompleteArrayType, DynamicError> {
140        use crate::type_object::common::{
141            AppliedBuiltinTypeAnnotations, CompleteTypeDetail, OptionalAppliedAnnotationSeq,
142        };
143        let element = self
144            .descriptor()
145            .element_type
146            .as_ref()
147            .ok_or_else(|| DynamicError::inconsistent("array type missing element_type"))?;
148        let bound_seq = self.descriptor().bound.clone();
149        if bound_seq.is_empty() {
150            return Err(DynamicError::inconsistent(
151                "array type missing dimensions (bound)",
152            ));
153        }
154        Ok(CompleteArrayType {
155            collection_flag: CollectionTypeFlag(0),
156            bound_seq,
157            detail: CompleteTypeDetail {
158                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
159                ann_custom: OptionalAppliedAnnotationSeq::default(),
160                type_name: self.descriptor().name.clone(),
161            },
162            element: complete_collection_element(element)?,
163        })
164    }
165
166    /// XTypes §7.3.4.4 — `map<K, V>` → `CompleteMapType`.
167    /// `element_type` traegt den Value-, `key_element_type` den Key-Typ.
168    fn to_complete_map(&self) -> Result<CompleteMapType, DynamicError> {
169        use crate::type_object::common::{
170            AppliedBuiltinTypeAnnotations, CompleteTypeDetail, OptionalAppliedAnnotationSeq,
171        };
172        let value =
173            self.descriptor().element_type.as_ref().ok_or_else(|| {
174                DynamicError::inconsistent("map type missing element_type (value)")
175            })?;
176        let key = self
177            .descriptor()
178            .key_element_type
179            .as_ref()
180            .ok_or_else(|| DynamicError::inconsistent("map type missing key_element_type"))?;
181        let bound = self.descriptor().bound.first().copied().unwrap_or(0);
182        Ok(CompleteMapType {
183            collection_flag: CollectionTypeFlag(0),
184            bound,
185            detail: CompleteTypeDetail {
186                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
187                ann_custom: OptionalAppliedAnnotationSeq::default(),
188                type_name: self.descriptor().name.clone(),
189            },
190            key: complete_collection_element(key)?,
191            element: complete_collection_element(value)?,
192        })
193    }
194
195    fn to_complete_alias(&self) -> Result<CompleteAliasType, DynamicError> {
196        use crate::type_object::common::{
197            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteTypeDetail,
198            OptionalAppliedAnnotationSeq,
199        };
200        let header = CompleteAliasHeader {
201            detail: CompleteTypeDetail {
202                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
203                ann_custom: OptionalAppliedAnnotationSeq::default(),
204                type_name: self.descriptor().name.clone(),
205            },
206        };
207        let element = self.descriptor().element_type.as_ref().ok_or_else(|| {
208            DynamicError::inconsistent("alias type missing element_type (target)")
209        })?;
210        let related_type = descriptor_to_type_identifier(element)?;
211        let body = CompleteAliasBody {
212            related_flags: AliasMemberFlag(0),
213            related_type,
214            ann_builtin: AppliedBuiltinMemberAnnotations::default(),
215            ann_custom: OptionalAppliedAnnotationSeq::default(),
216        };
217        Ok(CompleteAliasType {
218            alias_flags: AliasTypeFlag(0),
219            header,
220            body,
221        })
222    }
223
224    fn to_complete_enum(&self) -> Result<CompleteEnumeratedType, DynamicError> {
225        use crate::type_object::common::{
226            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
227            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
228        };
229        let bit_bound = self.descriptor().bound.first().copied().unwrap_or(32);
230        let bit_bound_u16 = u16::try_from(bit_bound).unwrap_or(32);
231        let header = CompleteEnumeratedHeader {
232            common: CommonEnumeratedHeader {
233                bit_bound: bit_bound_u16,
234            },
235            detail: CompleteTypeDetail {
236                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
237                ann_custom: OptionalAppliedAnnotationSeq::default(),
238                type_name: self.descriptor().name.clone(),
239            },
240        };
241        let mut literal_seq: Vec<CompleteEnumeratedLiteral> =
242            Vec::with_capacity(self.member_count() as usize);
243        for m in self.members() {
244            let mut flags_bits: u16 = 0;
245            if m.descriptor().is_default_label {
246                flags_bits |= EnumLiteralFlag::IS_DEFAULT_LITERAL;
247            }
248            // Enum-Ordinalwert = MemberId (Convention).
249            let value = i32::try_from(m.id()).map_err(|_| {
250                DynamicError::inconsistent(format!("enum literal id {} exceeds i32 range", m.id()))
251            })?;
252            literal_seq.push(CompleteEnumeratedLiteral {
253                common: CommonEnumeratedLiteral {
254                    value,
255                    flags: EnumLiteralFlag(flags_bits),
256                },
257                detail: CompleteMemberDetail {
258                    name: m.name().to_string(),
259                    ann_builtin: AppliedBuiltinMemberAnnotations::default(),
260                    ann_custom: OptionalAppliedAnnotationSeq::default(),
261                },
262            });
263        }
264        Ok(CompleteEnumeratedType {
265            enum_flags: EnumTypeFlag(0),
266            header,
267            literal_seq,
268        })
269    }
270
271    fn to_complete_bitmask(&self) -> Result<CompleteBitmaskType, DynamicError> {
272        use crate::type_object::common::{
273            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
274            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
275        };
276        let bit_bound = self.descriptor().bound.first().copied().unwrap_or(32);
277        let bit_bound_u16 = u16::try_from(bit_bound).unwrap_or(32);
278        let detail = CompleteTypeDetail {
279            ann_builtin: AppliedBuiltinTypeAnnotations::default(),
280            ann_custom: OptionalAppliedAnnotationSeq::default(),
281            type_name: self.descriptor().name.clone(),
282        };
283        let mut flag_seq: Vec<CompleteBitflag> = Vec::with_capacity(self.member_count() as usize);
284        for m in self.members() {
285            let position = u16::try_from(m.id()).map_err(|_| {
286                DynamicError::inconsistent(format!(
287                    "bitmask flag position {} exceeds u16 range",
288                    m.id()
289                ))
290            })?;
291            flag_seq.push(CompleteBitflag {
292                common: CommonBitflag {
293                    position,
294                    flags: BitflagFlag(0),
295                },
296                detail: CompleteMemberDetail {
297                    name: m.name().to_string(),
298                    ann_builtin: AppliedBuiltinMemberAnnotations::default(),
299                    ann_custom: OptionalAppliedAnnotationSeq::default(),
300                },
301            });
302        }
303        Ok(CompleteBitmaskType {
304            bitmask_flags: BitmaskTypeFlag(0),
305            bit_bound: bit_bound_u16,
306            detail,
307            flag_seq,
308        })
309    }
310
311    /// Spec §7.3.4.4 — Bitset: ein `CompleteBitfield` je Member, mit
312    /// Bit-Startposition (`MemberDescriptor.id`), Breite
313    /// (`MemberDescriptor.bit_bound`) und Holding-Type (TypeKind-Byte des
314    /// Member-Typs, der ein primitiver Integer-Typ sein muss).
315    fn to_complete_bitset(&self) -> Result<CompleteBitsetType, DynamicError> {
316        use crate::type_object::common::{
317            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
318            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
319        };
320        let detail = CompleteTypeDetail {
321            ann_builtin: AppliedBuiltinTypeAnnotations::default(),
322            ann_custom: OptionalAppliedAnnotationSeq::default(),
323            type_name: self.descriptor().name.clone(),
324        };
325        let mut field_seq: Vec<CompleteBitfield> = Vec::with_capacity(self.member_count() as usize);
326        for m in self.members() {
327            let position = u16::try_from(m.id()).map_err(|_| {
328                DynamicError::inconsistent(format!(
329                    "bitset field position {} exceeds u16 range",
330                    m.id()
331                ))
332            })?;
333            let holder_type = match descriptor_to_type_identifier(
334                m.descriptor().member_type.as_ref(),
335            )? {
336                TypeIdentifier::Primitive(pk) => pk.to_u8(),
337                other => {
338                    return Err(DynamicError::inconsistent(format!(
339                        "bitset field {:?} holder must be a primitive integer type, got {other:?}",
340                        m.name()
341                    )));
342                }
343            };
344            let bitcount = m.descriptor().bit_bound.ok_or_else(|| {
345                DynamicError::inconsistent(format!(
346                    "bitset field {:?} missing bit_bound (bitfield width)",
347                    m.name()
348                ))
349            })?;
350            field_seq.push(CompleteBitfield {
351                common: CommonBitfield {
352                    position,
353                    flags: BitfieldFlag(0),
354                    bitcount,
355                    holder_type,
356                },
357                detail: CompleteMemberDetail {
358                    name: m.name().to_string(),
359                    ann_builtin: AppliedBuiltinMemberAnnotations::default(),
360                    ann_custom: OptionalAppliedAnnotationSeq::default(),
361                },
362            });
363        }
364        Ok(CompleteBitsetType {
365            bitset_flags: BitsetTypeFlag(0),
366            detail,
367            field_seq,
368        })
369    }
370
371    /// Spec §7.3.4.4 — Annotation: ein `CompleteAnnotationParameter` je
372    /// Member (Parameter), mit Member-Id, Parameter-Typ, Name und dem
373    /// (opaque als UTF-8-Bytes abgelegten) Default-Value aus
374    /// `MemberDescriptor.default_value`.
375    fn to_complete_annotation(&self) -> Result<CompleteAnnotationType, DynamicError> {
376        use crate::type_object::common::{
377            AppliedBuiltinTypeAnnotations, CompleteTypeDetail, OptionalAppliedAnnotationSeq,
378        };
379        let detail = CompleteTypeDetail {
380            ann_builtin: AppliedBuiltinTypeAnnotations::default(),
381            ann_custom: OptionalAppliedAnnotationSeq::default(),
382            type_name: self.descriptor().name.clone(),
383        };
384        let mut member_seq: Vec<CompleteAnnotationParameter> =
385            Vec::with_capacity(self.member_count() as usize);
386        for m in self.members() {
387            let member_type_id =
388                descriptor_to_type_identifier(m.descriptor().member_type.as_ref())?;
389            let default_value = m
390                .descriptor()
391                .default_value
392                .as_deref()
393                .map(|s| s.as_bytes().to_vec())
394                .unwrap_or_default();
395            member_seq.push(CompleteAnnotationParameter {
396                member_id: m.id(),
397                member_flags: AnnotationParameterFlag(0),
398                member_type_id,
399                name: m.name().to_string(),
400                default_value,
401            });
402        }
403        Ok(CompleteAnnotationType {
404            annotation_flag: AnnotationTypeFlag(0),
405            detail,
406            member_seq,
407        })
408    }
409
410    fn to_complete_union(&self) -> Result<CompleteUnionType, DynamicError> {
411        use crate::type_object::common::{
412            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
413            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
414        };
415        let union_flags = UnionTypeFlag(extensibility_to_flag_bits(
416            self.descriptor().extensibility_kind,
417        ));
418        let header = CompleteUnionHeader {
419            detail: CompleteTypeDetail {
420                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
421                ann_custom: OptionalAppliedAnnotationSeq::default(),
422                type_name: self.descriptor().name.clone(),
423            },
424        };
425        let disc_descriptor = self
426            .descriptor()
427            .discriminator_type
428            .as_ref()
429            .ok_or_else(|| DynamicError::inconsistent("union type missing discriminator_type"))?;
430        let disc_type = descriptor_to_type_identifier(disc_descriptor)?;
431        let discriminator = CompleteDiscriminatorMember {
432            common: CommonDiscriminatorMember {
433                member_flags: UnionDiscriminatorFlag(0),
434                type_id: disc_type,
435            },
436            ann_builtin: AppliedBuiltinTypeAnnotations::default(),
437            ann_custom: OptionalAppliedAnnotationSeq::default(),
438        };
439        let mut member_seq: Vec<CompleteUnionMember> =
440            Vec::with_capacity(self.member_count() as usize);
441        for m in self.members() {
442            let mut flags_bits: u16 = 0;
443            if m.descriptor().is_default_label {
444                flags_bits |= UnionMemberFlag::IS_DEFAULT;
445            }
446            // Spec §7.3.4.5.3.2 — case labels are `long` (i32).
447            let label_seq: Vec<i32> = m
448                .descriptor()
449                .label
450                .iter()
451                .map(|&v| i32::try_from(v).unwrap_or_default())
452                .collect();
453            let common = CommonUnionMember {
454                member_id: m.id(),
455                member_flags: UnionMemberFlag(flags_bits),
456                type_id: descriptor_to_type_identifier(m.descriptor().member_type.as_ref())?,
457                label_seq,
458            };
459            let detail = CompleteMemberDetail {
460                name: m.name().to_string(),
461                ann_builtin: AppliedBuiltinMemberAnnotations::default(),
462                ann_custom: OptionalAppliedAnnotationSeq::default(),
463            };
464            member_seq.push(CompleteUnionMember { common, detail });
465        }
466        Ok(CompleteUnionType {
467            union_flags,
468            header,
469            discriminator,
470            member_seq,
471        })
472    }
473
474    fn to_complete_struct(&self) -> Result<CompleteStructType, DynamicError> {
475        use crate::type_object::common::{
476            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
477            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
478        };
479        let struct_flags = StructTypeFlag(extensibility_to_flag_bits(
480            self.descriptor().extensibility_kind,
481        ));
482        let header = CompleteStructHeader {
483            base_type: TypeIdentifier::None,
484            detail: CompleteTypeDetail {
485                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
486                ann_custom: OptionalAppliedAnnotationSeq::default(),
487                type_name: self.descriptor().name.clone(),
488            },
489        };
490        let mut member_seq: Vec<CompleteStructMember> =
491            Vec::with_capacity(self.member_count() as usize);
492        for m in self.members() {
493            let mut flags_bits: u16 = 0;
494            if m.descriptor().is_key {
495                flags_bits |= StructMemberFlag::IS_KEY;
496            }
497            if m.descriptor().is_optional {
498                flags_bits |= StructMemberFlag::IS_OPTIONAL;
499            }
500            if m.descriptor().is_must_understand {
501                flags_bits |= StructMemberFlag::IS_MUST_UNDERSTAND;
502            }
503            if m.descriptor().is_shared {
504                flags_bits |= StructMemberFlag::IS_EXTERNAL;
505            }
506            let common = CommonStructMember {
507                member_id: m.id(),
508                member_flags: StructMemberFlag(flags_bits),
509                member_type_id: descriptor_to_type_identifier(m.descriptor().member_type.as_ref())?,
510            };
511            let detail = CompleteMemberDetail {
512                name: m.name().to_string(),
513                ann_builtin: AppliedBuiltinMemberAnnotations::default(),
514                ann_custom: OptionalAppliedAnnotationSeq::default(),
515            };
516            member_seq.push(CompleteStructMember { common, detail });
517        }
518        Ok(CompleteStructType {
519            struct_flags,
520            header,
521            member_seq,
522        })
523    }
524}
525
526const fn extensibility_to_flag_bits(ext: ExtensibilityKind) -> u16 {
527    match ext {
528        ExtensibilityKind::Final => StructTypeFlag::IS_FINAL,
529        ExtensibilityKind::Appendable => StructTypeFlag::IS_APPENDABLE,
530        ExtensibilityKind::Mutable => StructTypeFlag::IS_MUTABLE,
531    }
532}
533
534const fn flag_bits_to_extensibility(flags: u16) -> ExtensibilityKind {
535    if flags & StructTypeFlag::IS_FINAL != 0 {
536        ExtensibilityKind::Final
537    } else if flags & StructTypeFlag::IS_MUTABLE != 0 {
538        ExtensibilityKind::Mutable
539    } else {
540        ExtensibilityKind::Appendable
541    }
542}
543
544/// XTypes §7.3.4.4 — builds a `CompleteCollectionElement` (element or key
545/// description) from a `TypeDescriptor`.
546fn complete_collection_element(
547    desc: &TypeDescriptor,
548) -> Result<CompleteCollectionElement, DynamicError> {
549    use crate::type_object::common::{
550        AppliedBuiltinMemberAnnotations, OptionalAppliedAnnotationSeq,
551    };
552    Ok(CompleteCollectionElement {
553        common: CommonCollectionElement {
554            element_flags: CollectionElementFlag(0),
555            type_id: descriptor_to_type_identifier(desc)?,
556        },
557        ann_builtin: AppliedBuiltinMemberAnnotations::default(),
558        ann_custom: OptionalAppliedAnnotationSeq::default(),
559    })
560}
561
562/// Maps a member `TypeDescriptor` to a matching `TypeIdentifier`. Only
563/// primitive + string + sequence + array are encoded inline; composite
564/// members require a TypeRegistry lookup strategy via
565/// `zerodds_types::resolve::TypeRegistry`.
566fn descriptor_to_type_identifier(desc: &TypeDescriptor) -> Result<TypeIdentifier, DynamicError> {
567    match desc.kind {
568        TypeKind::Boolean => Ok(TypeIdentifier::Primitive(PrimitiveKind::Boolean)),
569        TypeKind::Byte => Ok(TypeIdentifier::Primitive(PrimitiveKind::Byte)),
570        TypeKind::Int8 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Int8)),
571        TypeKind::UInt8 => Ok(TypeIdentifier::Primitive(PrimitiveKind::UInt8)),
572        TypeKind::Int16 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Int16)),
573        TypeKind::UInt16 => Ok(TypeIdentifier::Primitive(PrimitiveKind::UInt16)),
574        TypeKind::Int32 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Int32)),
575        TypeKind::UInt32 => Ok(TypeIdentifier::Primitive(PrimitiveKind::UInt32)),
576        TypeKind::Int64 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Int64)),
577        TypeKind::UInt64 => Ok(TypeIdentifier::Primitive(PrimitiveKind::UInt64)),
578        TypeKind::Float32 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Float32)),
579        TypeKind::Float64 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Float64)),
580        TypeKind::Float128 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Float128)),
581        TypeKind::Char8 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Char8)),
582        TypeKind::Char16 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Char16)),
583        TypeKind::String8 => {
584            let bound = desc.bound.first().copied().unwrap_or(0);
585            if bound <= u32::from(u8::MAX) {
586                Ok(TypeIdentifier::String8Small { bound: bound as u8 })
587            } else {
588                Ok(TypeIdentifier::String8Large { bound })
589            }
590        }
591        TypeKind::String16 => {
592            let bound = desc.bound.first().copied().unwrap_or(0);
593            if bound <= u32::from(u8::MAX) {
594                Ok(TypeIdentifier::String16Small { bound: bound as u8 })
595            } else {
596                Ok(TypeIdentifier::String16Large { bound })
597            }
598        }
599        TypeKind::Structure | TypeKind::Union | TypeKind::Enumeration | TypeKind::Alias => {
600            // Bridge phase 1: composite members carry `TypeIdentifier::None`
601            // — the TypeRegistry (C4.2) later resolves via the name
602            // against an EquivalenceHash. Roundtrip tests set this
603            // information separately.
604            Ok(TypeIdentifier::None)
605        }
606        kind => Err(DynamicError::unsupported(format!(
607            "descriptor_to_type_identifier: {kind:?} not yet covered"
608        ))),
609    }
610}
611
612// ----------------------------------------------------------------------
613// TypeObject → DynamicType (Spec §7.6.4)
614// ----------------------------------------------------------------------
615
616impl DynamicTypeBuilderFactory {
617    /// Spec §7.6.4 / §7.5.5.1.4 `create_type_w_type_object(type_object)`.
618    ///
619    /// Scope:
620    /// - `CompleteStructType` with primitive + string members.
621    /// - `MinimalStructType` as read-only with hash names.
622    /// - Other kinds: `Unsupported`.
623    ///
624    /// # Errors
625    /// `Unsupported` for kinds outside the current implementation scope.
626    pub fn create_type_w_type_object(
627        type_obj: &TypeObject,
628    ) -> Result<DynamicTypeBuilder, DynamicError> {
629        match type_obj {
630            TypeObject::Complete(c) => match c {
631                CompleteTypeObject::Struct(s) => complete_struct_to_builder(s),
632                other => Err(DynamicError::unsupported(format!(
633                    "complete-typeobject kind {} not yet supported",
634                    other_kind_name(other)
635                ))),
636            },
637            TypeObject::Minimal(_) => Err(DynamicError::unsupported(
638                "minimal-typeobject → dynamic-type pending C4.2 TypeRegistry",
639            )),
640        }
641    }
642
643    /// Registry-aware `create_type_w_type_object` (XTypes §7.6.4 with a
644    /// TypeLookupService). Unlike the bare overload, composite member types
645    /// referenced by `EquivalenceHash` are resolved recursively against
646    /// `registry`, so a struct/union with nested struct/union/enum/bitmask/
647    /// bitset MEMBERS yields a fully-populated `DynamicType` (members carry
648    /// their own members). Covers all 10 top-level Complete kinds.
649    ///
650    /// Residual (honest `Unsupported`, not silent): a COLLECTION whose element
651    /// is itself a composite keeps a shallow element descriptor — the
652    /// `TypeDescriptor::element_type` model holds a descriptor, not a full
653    /// `DynamicType`, so `sequence<NestedStruct>` cannot yet carry the nested
654    /// members (needs a model change); likewise Minimal TypeObjects and
655    /// alias-to-composite targets.
656    ///
657    /// # Errors
658    /// `Unsupported` for the residual cases above and unresolvable hashes.
659    pub fn create_type_w_type_object_in(
660        type_obj: &TypeObject,
661        registry: &crate::resolve::TypeRegistry,
662    ) -> Result<DynamicType, DynamicError> {
663        match type_obj {
664            TypeObject::Complete(c) => resolve_complete(c, registry),
665            TypeObject::Minimal(_) => Err(DynamicError::unsupported(
666                "minimal-typeobject → dynamic-type pending C4.2 TypeRegistry",
667            )),
668        }
669    }
670}
671
672/// Resolves a `TypeIdentifier` to a full `DynamicType`, following composite
673/// `EquivalenceHash` references through `registry` recursively.
674///
675/// zerodds-lint: recursion-depth 16 (capped by TypeIdentifier decode depth +
676/// registry acyclicity; a missing/cyclic hash returns `Unsupported`).
677fn resolve_type_id(
678    ti: &TypeIdentifier,
679    registry: &crate::resolve::TypeRegistry,
680) -> Result<DynamicType, DynamicError> {
681    use super::builder::DynamicTypeBuilderFactory as F;
682    match ti {
683        TypeIdentifier::Primitive(p) => {
684            let kind = primitive_kind_to_type_kind(*p);
685            F::get_primitive_type(kind)
686        }
687        TypeIdentifier::String8Small { bound } => Ok(F::create_string_type(u32::from(*bound))),
688        TypeIdentifier::String8Large { bound } => Ok(F::create_string_type(*bound)),
689        TypeIdentifier::String16Small { bound } => Ok(F::create_wstring_type(u32::from(*bound))),
690        TypeIdentifier::String16Large { bound } => Ok(F::create_wstring_type(*bound)),
691        // Plain collections: build the collection DynamicType. A composite
692        // element keeps a shallow element descriptor (model residual above).
693        TypeIdentifier::PlainSequenceSmall { .. }
694        | TypeIdentifier::PlainSequenceLarge { .. }
695        | TypeIdentifier::PlainArraySmall { .. }
696        | TypeIdentifier::PlainArrayLarge { .. }
697        | TypeIdentifier::PlainMapSmall { .. }
698        | TypeIdentifier::PlainMapLarge { .. } => {
699            let kind = type_id_to_kind(ti)?;
700            let desc = type_id_to_descriptor(ti, kind)?;
701            F::create_type(desc)?.build()
702        }
703        TypeIdentifier::EquivalenceHashComplete(h) => {
704            let c = registry.get_complete(h).ok_or_else(|| {
705                DynamicError::unsupported("composite member: EquivalenceHash not in TypeRegistry")
706            })?;
707            resolve_complete(c, registry)
708        }
709        TypeIdentifier::EquivalenceHashMinimal(_) => Err(DynamicError::unsupported(
710            "minimal composite member → dynamic-type pending C4.2",
711        )),
712        other => Err(DynamicError::unsupported(format!(
713            "resolve_type_id: {other:?} not yet supported"
714        ))),
715    }
716}
717
718/// Builds a full `DynamicType` from a `CompleteTypeObject`, resolving composite
719/// member types through `registry`. Handles all 10 Complete kinds.
720/// zerodds-lint: recursion-depth 64 (runtime DynamicData codec; bounded by type nesting).
721fn resolve_complete(
722    c: &CompleteTypeObject,
723    registry: &crate::resolve::TypeRegistry,
724) -> Result<DynamicType, DynamicError> {
725    use super::builder::DynamicTypeBuilderFactory as F;
726    match c {
727        CompleteTypeObject::Struct(s) => complete_struct_to_builder_in(s, registry)?.build(),
728        CompleteTypeObject::Union(u) => {
729            let disc_kind = type_id_to_kind(&u.discriminator.common.type_id)?;
730            let disc_desc = type_id_to_descriptor(&u.discriminator.common.type_id, disc_kind)?;
731            let mut b = F::create_type(TypeDescriptor::union(
732                u.header.detail.type_name.clone(),
733                disc_desc,
734            ))?;
735            for m in &u.member_seq {
736                let mt = resolve_type_id(&m.common.type_id, registry)?;
737                let mut md = MemberDescriptor::new(
738                    m.detail.name.clone(),
739                    m.common.member_id,
740                    mt.descriptor().clone(),
741                );
742                md.label = m.common.label_seq.iter().map(|&v| i64::from(v)).collect();
743                md.is_default_label = (m.common.member_flags.0 & UnionMemberFlag::IS_DEFAULT) != 0;
744                b.add_member_resolved(md, mt)?;
745            }
746            b.build()
747        }
748        CompleteTypeObject::Enumerated(e) => {
749            let mut desc = TypeDescriptor::enumeration(e.header.detail.type_name.clone());
750            desc.bound = alloc::vec![u32::from(e.header.common.bit_bound)];
751            let mut b = F::create_type(desc)?;
752            for lit in &e.literal_seq {
753                let id = u32::try_from(lit.common.value).unwrap_or(0);
754                let mut md = MemberDescriptor::new(
755                    lit.detail.name.clone(),
756                    id,
757                    TypeDescriptor::primitive(TypeKind::Int32, "int32"),
758                );
759                md.is_default_label =
760                    (lit.common.flags.0 & EnumLiteralFlag::IS_DEFAULT_LITERAL) != 0;
761                b.add_member(md)?;
762            }
763            b.build()
764        }
765        CompleteTypeObject::Bitmask(bm) => {
766            let mut desc = bare_descriptor(TypeKind::Bitmask, bm.detail.type_name.clone());
767            desc.bound = alloc::vec![u32::from(bm.bit_bound)];
768            let mut b = F::create_type(desc)?;
769            for f in &bm.flag_seq {
770                let md = MemberDescriptor::new(
771                    f.detail.name.clone(),
772                    u32::from(f.common.position),
773                    TypeDescriptor::primitive(TypeKind::Boolean, "boolean"),
774                );
775                b.add_member(md)?;
776            }
777            b.build()
778        }
779        CompleteTypeObject::Bitset(bs) => {
780            let desc = bare_descriptor(TypeKind::Bitset, bs.detail.type_name.clone());
781            let mut b = F::create_type(desc)?;
782            for f in &bs.field_seq {
783                let holder = PrimitiveKind::from_u8(f.common.holder_type).ok_or_else(|| {
784                    DynamicError::inconsistent(format!(
785                        "bitset field holder_type {} is not a primitive",
786                        f.common.holder_type
787                    ))
788                })?;
789                let mut md = MemberDescriptor::new(
790                    f.detail.name.clone(),
791                    u32::from(f.common.position),
792                    TypeDescriptor::primitive(primitive_kind_to_type_kind(holder), "holder"),
793                );
794                md.bit_bound = Some(f.common.bitcount);
795                b.add_member(md)?;
796            }
797            b.build()
798        }
799        CompleteTypeObject::Alias(a) => {
800            // Leaf alias targets resolve fully; a composite target keeps a
801            // shallow descriptor (model residual).
802            let target_kind = type_id_to_kind(&a.body.related_type)?;
803            let target = type_id_to_descriptor(&a.body.related_type, target_kind)?;
804            let mut desc = bare_descriptor(TypeKind::Alias, a.header.detail.type_name.clone());
805            desc.element_type = Some(alloc::boxed::Box::new(target));
806            F::create_type(desc)?.build()
807        }
808        CompleteTypeObject::Sequence(_)
809        | CompleteTypeObject::Array(_)
810        | CompleteTypeObject::Map(_) => {
811            // Named (non-anonymous) collection TypeObject: build via descriptor;
812            // composite elements stay shallow (model residual).
813            resolve_named_collection(c)
814        }
815        CompleteTypeObject::Annotation(an) => {
816            let desc = bare_descriptor(TypeKind::Annotation, an.detail.type_name.clone());
817            let mut b = F::create_type(desc)?;
818            for m in &an.member_seq {
819                let mk = type_id_to_kind(&m.member_type_id)?;
820                let mt = type_id_to_descriptor(&m.member_type_id, mk)?;
821                let mut md = MemberDescriptor::new(m.name.clone(), m.member_id, mt);
822                if !m.default_value.is_empty() {
823                    md.default_value = Some(String::from_utf8_lossy(&m.default_value).into_owned());
824                }
825                b.add_member(md)?;
826            }
827            b.build()
828        }
829    }
830}
831
832/// Like [`complete_struct_to_builder`] but resolves each member's type fully
833/// (recursively through `registry`) and attaches it via `add_member_resolved`,
834/// so nested composite members carry their own members.
835fn complete_struct_to_builder_in(
836    s: &CompleteStructType,
837    registry: &crate::resolve::TypeRegistry,
838) -> Result<DynamicTypeBuilder, DynamicError> {
839    let mut desc = TypeDescriptor::structure(s.header.detail.type_name.clone());
840    desc.extensibility_kind = flag_bits_to_extensibility(s.struct_flags.0);
841    if (s.struct_flags.0 & StructTypeFlag::IS_NESTED) != 0 {
842        desc.is_nested = true;
843    }
844    let mut b = DynamicTypeBuilderFactory::create_type(desc)?;
845    for (idx, m) in s.member_seq.iter().enumerate() {
846        let mt = resolve_type_id(&m.common.member_type_id, registry)?;
847        let mut md = MemberDescriptor::new(
848            m.detail.name.clone(),
849            m.common.member_id,
850            mt.descriptor().clone(),
851        );
852        md.index = u32::try_from(idx).unwrap_or(u32::MAX);
853        md.is_key = (m.common.member_flags.0 & StructMemberFlag::IS_KEY) != 0;
854        md.is_optional = (m.common.member_flags.0 & StructMemberFlag::IS_OPTIONAL) != 0;
855        md.is_must_understand =
856            (m.common.member_flags.0 & StructMemberFlag::IS_MUST_UNDERSTAND) != 0;
857        md.is_shared = (m.common.member_flags.0 & StructMemberFlag::IS_EXTERNAL) != 0;
858        b.add_member_resolved(md, mt)?;
859    }
860    Ok(b)
861}
862
863/// Builds a top-level named collection (Sequence/Array/Map) `CompleteTypeObject`
864/// into a `DynamicType` via its descriptor.
865fn resolve_named_collection(c: &CompleteTypeObject) -> Result<DynamicType, DynamicError> {
866    use super::builder::DynamicTypeBuilderFactory as F;
867    let desc = match c {
868        CompleteTypeObject::Sequence(s) => {
869            let ek = type_id_to_kind(&s.element.common.type_id)?;
870            let ed = type_id_to_descriptor(&s.element.common.type_id, ek)?;
871            TypeDescriptor::sequence(s.detail.type_name.clone(), ed, s.bound)
872        }
873        CompleteTypeObject::Array(a) => {
874            let ek = type_id_to_kind(&a.element.common.type_id)?;
875            let ed = type_id_to_descriptor(&a.element.common.type_id, ek)?;
876            TypeDescriptor::array(a.detail.type_name.clone(), ed, a.bound_seq.clone())
877        }
878        CompleteTypeObject::Map(m) => {
879            let kk = type_id_to_kind(&m.key.common.type_id)?;
880            let kd = type_id_to_descriptor(&m.key.common.type_id, kk)?;
881            let vk = type_id_to_kind(&m.element.common.type_id)?;
882            let vd = type_id_to_descriptor(&m.element.common.type_id, vk)?;
883            TypeDescriptor::map(m.detail.type_name.clone(), kd, vd, m.bound)
884        }
885        other => {
886            return Err(DynamicError::unsupported(format!(
887                "resolve_named_collection called on non-collection: {other:?}"
888            )));
889        }
890    };
891    F::create_type(desc)?.build()
892}
893
894/// A bare `TypeDescriptor` carrying just `kind` + `name` (all structural fields
895/// empty) — for the non-collection composite kinds whose body lives in their
896/// members (Bitmask/Bitset/Annotation) or in `element_type` (Alias), set by the
897/// caller after construction.
898fn bare_descriptor(kind: TypeKind, name: impl Into<String>) -> TypeDescriptor {
899    TypeDescriptor {
900        kind,
901        name: name.into(),
902        base_type: None,
903        discriminator_type: None,
904        bound: Vec::new(),
905        element_type: None,
906        key_element_type: None,
907        extensibility_kind: ExtensibilityKind::default(),
908        is_nested: false,
909    }
910}
911
912fn complete_struct_to_builder(s: &CompleteStructType) -> Result<DynamicTypeBuilder, DynamicError> {
913    let mut desc = TypeDescriptor::structure(s.header.detail.type_name.clone());
914    desc.extensibility_kind = flag_bits_to_extensibility(s.struct_flags.0);
915    if (s.struct_flags.0 & StructTypeFlag::IS_NESTED) != 0 {
916        desc.is_nested = true;
917    }
918    let mut b = DynamicTypeBuilderFactory::create_type(desc)?;
919    for (idx, m) in s.member_seq.iter().enumerate() {
920        let kind = type_id_to_kind(&m.common.member_type_id)?;
921        let member_type = type_id_to_descriptor(&m.common.member_type_id, kind)?;
922        let mut md = MemberDescriptor::new(m.detail.name.clone(), m.common.member_id, member_type);
923        md.index = u32::try_from(idx).unwrap_or(u32::MAX);
924        md.is_key = (m.common.member_flags.0 & StructMemberFlag::IS_KEY) != 0;
925        md.is_optional = (m.common.member_flags.0 & StructMemberFlag::IS_OPTIONAL) != 0;
926        md.is_must_understand =
927            (m.common.member_flags.0 & StructMemberFlag::IS_MUST_UNDERSTAND) != 0;
928        md.is_shared = (m.common.member_flags.0 & StructMemberFlag::IS_EXTERNAL) != 0;
929        b.add_member(md)?;
930    }
931    Ok(b)
932}
933
934const fn other_kind_name(c: &CompleteTypeObject) -> &'static str {
935    match c {
936        CompleteTypeObject::Alias(_) => "alias",
937        CompleteTypeObject::Annotation(_) => "annotation",
938        CompleteTypeObject::Struct(_) => "struct",
939        CompleteTypeObject::Union(_) => "union",
940        CompleteTypeObject::Bitset(_) => "bitset",
941        CompleteTypeObject::Sequence(_) => "sequence",
942        CompleteTypeObject::Array(_) => "array",
943        CompleteTypeObject::Map(_) => "map",
944        CompleteTypeObject::Enumerated(_) => "enum",
945        CompleteTypeObject::Bitmask(_) => "bitmask",
946    }
947}
948
949fn type_id_to_kind(ti: &TypeIdentifier) -> Result<TypeKind, DynamicError> {
950    Ok(match ti {
951        TypeIdentifier::None => TypeKind::NoType,
952        TypeIdentifier::Primitive(p) => primitive_kind_to_type_kind(*p),
953        TypeIdentifier::String8Small { .. } | TypeIdentifier::String8Large { .. } => {
954            TypeKind::String8
955        }
956        TypeIdentifier::String16Small { .. } | TypeIdentifier::String16Large { .. } => {
957            TypeKind::String16
958        }
959        TypeIdentifier::PlainSequenceSmall { .. } | TypeIdentifier::PlainSequenceLarge { .. } => {
960            TypeKind::Sequence
961        }
962        TypeIdentifier::PlainArraySmall { .. } | TypeIdentifier::PlainArrayLarge { .. } => {
963            TypeKind::Array
964        }
965        TypeIdentifier::PlainMapSmall { .. } | TypeIdentifier::PlainMapLarge { .. } => {
966            TypeKind::Map
967        }
968        TypeIdentifier::EquivalenceHashMinimal(_) | TypeIdentifier::EquivalenceHashComplete(_) => {
969            // Composite via TypeRegistry lookup — mark as Structure
970            // by default; resolving it correctly is C4.2.
971            TypeKind::Structure
972        }
973        other => {
974            return Err(DynamicError::unsupported(format!(
975                "type_id_to_kind: {other:?} not yet supported"
976            )));
977        }
978    })
979}
980
981const fn primitive_kind_to_type_kind(p: PrimitiveKind) -> TypeKind {
982    match p {
983        PrimitiveKind::Boolean => TypeKind::Boolean,
984        PrimitiveKind::Byte => TypeKind::Byte,
985        PrimitiveKind::Int8 => TypeKind::Int8,
986        PrimitiveKind::UInt8 => TypeKind::UInt8,
987        PrimitiveKind::Int16 => TypeKind::Int16,
988        PrimitiveKind::UInt16 => TypeKind::UInt16,
989        PrimitiveKind::Int32 => TypeKind::Int32,
990        PrimitiveKind::UInt32 => TypeKind::UInt32,
991        PrimitiveKind::Int64 => TypeKind::Int64,
992        PrimitiveKind::UInt64 => TypeKind::UInt64,
993        PrimitiveKind::Float32 => TypeKind::Float32,
994        PrimitiveKind::Float64 => TypeKind::Float64,
995        PrimitiveKind::Float128 => TypeKind::Float128,
996        PrimitiveKind::Char8 => TypeKind::Char8,
997        PrimitiveKind::Char16 => TypeKind::Char16,
998    }
999}
1000
1001/// zerodds-lint: recursion-depth 16
1002///
1003/// Recursive calls via the PlainSequence/PlainArray element. Depth is
1004/// implicitly capped by [`TypeIdentifier::MAX_DECODE_DEPTH`] — the
1005/// TypeIdentifier can be at most 16 layers deep on wire-decode, after
1006/// which decoding fails with `LengthExceeded`.
1007fn type_id_to_descriptor(
1008    ti: &TypeIdentifier,
1009    kind: TypeKind,
1010) -> Result<TypeDescriptor, DynamicError> {
1011    if kind.is_primitive() {
1012        return Ok(TypeDescriptor::primitive(
1013            kind,
1014            super::type_::primitive_name(kind).to_string(),
1015        ));
1016    }
1017    Ok(match ti {
1018        TypeIdentifier::String8Small { bound } => TypeDescriptor::string8(u32::from(*bound)),
1019        TypeIdentifier::String8Large { bound } => TypeDescriptor::string8(*bound),
1020        TypeIdentifier::String16Small { bound } => TypeDescriptor::string16(u32::from(*bound)),
1021        TypeIdentifier::String16Large { bound } => TypeDescriptor::string16(*bound),
1022        TypeIdentifier::None => TypeDescriptor {
1023            kind: TypeKind::Structure,
1024            name: String::from("<unresolved>"),
1025            base_type: None,
1026            discriminator_type: None,
1027            bound: Vec::new(),
1028            element_type: None,
1029            key_element_type: None,
1030            extensibility_kind: ExtensibilityKind::default(),
1031            is_nested: false,
1032        },
1033        TypeIdentifier::EquivalenceHashMinimal(_) | TypeIdentifier::EquivalenceHashComplete(_) => {
1034            // We only know the name after a TypeRegistry lookup
1035            // (C4.2). Until then: placeholder descriptor with Kind=Structure.
1036            TypeDescriptor {
1037                kind: TypeKind::Structure,
1038                name: String::from("<typeref>"),
1039                base_type: None,
1040                discriminator_type: None,
1041                bound: Vec::new(),
1042                element_type: None,
1043                key_element_type: None,
1044                extensibility_kind: ExtensibilityKind::default(),
1045                is_nested: false,
1046            }
1047        }
1048        TypeIdentifier::PlainSequenceSmall { bound, element, .. } => {
1049            let elem_kind = type_id_to_kind(element)?;
1050            TypeDescriptor::sequence(
1051                "<seq>".to_string(),
1052                type_id_to_descriptor(element, elem_kind)?,
1053                u32::from(*bound),
1054            )
1055        }
1056        TypeIdentifier::PlainSequenceLarge { bound, element, .. } => {
1057            let elem_kind = type_id_to_kind(element)?;
1058            TypeDescriptor::sequence(
1059                "<seq>".to_string(),
1060                type_id_to_descriptor(element, elem_kind)?,
1061                *bound,
1062            )
1063        }
1064        TypeIdentifier::PlainArraySmall {
1065            array_bounds,
1066            element,
1067            ..
1068        } => {
1069            let elem_kind = type_id_to_kind(element)?;
1070            TypeDescriptor::array(
1071                "<arr>".to_string(),
1072                type_id_to_descriptor(element, elem_kind)?,
1073                array_bounds.iter().map(|b| u32::from(*b)).collect(),
1074            )
1075        }
1076        TypeIdentifier::PlainArrayLarge {
1077            array_bounds,
1078            element,
1079            ..
1080        } => {
1081            let elem_kind = type_id_to_kind(element)?;
1082            TypeDescriptor::array(
1083                "<arr>".to_string(),
1084                type_id_to_descriptor(element, elem_kind)?,
1085                array_bounds.clone(),
1086            )
1087        }
1088        other => {
1089            return Err(DynamicError::unsupported(format!(
1090                "type_id_to_descriptor: {other:?} not yet supported"
1091            )));
1092        }
1093    })
1094}
1095
1096#[cfg(test)]
1097#[allow(
1098    clippy::unwrap_used,
1099    clippy::expect_used,
1100    clippy::panic,
1101    clippy::unreachable
1102)]
1103mod tests {
1104    use super::*;
1105    use crate::dynamic::DynamicTypeBuilderFactory;
1106
1107    fn build_int_struct() -> DynamicType {
1108        let mut b = DynamicTypeBuilderFactory::create_struct("::Sensor");
1109        b.add_struct_member("id", 1, TypeDescriptor::primitive(TypeKind::Int64, "int64"))
1110            .unwrap();
1111        b.add_struct_member(
1112            "temp",
1113            2,
1114            TypeDescriptor::primitive(TypeKind::Float64, "double"),
1115        )
1116        .unwrap();
1117        b.add_struct_member("name", 3, TypeDescriptor::string8(64))
1118            .unwrap();
1119        b.build().unwrap()
1120    }
1121
1122    #[test]
1123    fn dynamic_struct_to_typeobject_complete() {
1124        let t = build_int_struct();
1125        let to = t.to_type_object().unwrap();
1126        match to {
1127            TypeObject::Complete(CompleteTypeObject::Struct(s)) => {
1128                assert_eq!(s.header.detail.type_name, "::Sensor");
1129                assert_eq!(s.member_seq.len(), 3);
1130                assert_eq!(s.member_seq[0].detail.name, "id");
1131            }
1132            _ => panic!("to_type_object on Structure must return Complete::Struct"),
1133        }
1134    }
1135
1136    #[test]
1137    fn typeobject_complete_struct_back_to_dynamic_type() {
1138        let t = build_int_struct();
1139        let to = t.to_type_object().unwrap();
1140        let b = DynamicTypeBuilderFactory::create_type_w_type_object(&to).unwrap();
1141        let t2 = b.build().unwrap();
1142        assert_eq!(t2.name(), "::Sensor");
1143        assert_eq!(t2.member_count(), 3);
1144        assert_eq!(t2.member_by_id(1).unwrap().name(), "id");
1145        assert_eq!(t2.member_by_id(2).unwrap().name(), "temp");
1146        assert_eq!(t2.member_by_id(3).unwrap().name(), "name");
1147    }
1148
1149    #[test]
1150    fn roundtrip_dynamic_to_typeobject_to_dynamic_equals_logically() {
1151        let t = build_int_struct();
1152        let to = t.to_type_object().unwrap();
1153        let b = DynamicTypeBuilderFactory::create_type_w_type_object(&to).unwrap();
1154        let t2 = b.build().unwrap();
1155        // Deep equality on the type built by the bridge.
1156        assert!(t.equals(&t2), "roundtrip failed: {t:?} vs {t2:?}");
1157    }
1158
1159    #[test]
1160    fn unsupported_kind_to_typeobject_yields_unsupported_error() {
1161        let prim = DynamicTypeBuilderFactory::get_primitive_type(TypeKind::Int32).unwrap();
1162        let err = prim.to_type_object().unwrap_err();
1163        assert!(matches!(err, DynamicError::Unsupported(_)));
1164    }
1165
1166    #[test]
1167    fn dynamic_alias_to_typeobject_complete() {
1168        // alias with element_type = int32.
1169        let mut desc = TypeDescriptor::primitive(TypeKind::Int32, "int32");
1170        desc.kind = TypeKind::Alias;
1171        desc.name = "::SensorId".to_string();
1172        desc.element_type = Some(alloc::boxed::Box::new(TypeDescriptor::primitive(
1173            TypeKind::Int32,
1174            "int32",
1175        )));
1176        let t = DynamicTypeBuilderFactory::create_type(desc)
1177            .unwrap()
1178            .build()
1179            .unwrap();
1180        let to = t.to_type_object().expect("alias bridge ok");
1181        match to {
1182            TypeObject::Complete(CompleteTypeObject::Alias(a)) => {
1183                assert_eq!(a.header.detail.type_name, "::SensorId");
1184                assert!(matches!(a.body.related_type, TypeIdentifier::Primitive(_)));
1185            }
1186            other => panic!("expected Alias, got {other:?}"),
1187        }
1188    }
1189
1190    #[test]
1191    fn dynamic_enum_to_typeobject_complete() {
1192        let mut desc = TypeDescriptor::primitive(TypeKind::Enumeration, "::Color");
1193        desc.kind = TypeKind::Enumeration;
1194        desc.bound = alloc::vec![32];
1195        let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1196        for (id, name) in [(0u32, "RED"), (1u32, "GREEN"), (2u32, "BLUE")] {
1197            let m = MemberDescriptor::new(
1198                name,
1199                id,
1200                TypeDescriptor::primitive(TypeKind::Int32, "int32"),
1201            );
1202            b.add_member(m).unwrap();
1203        }
1204        let t = b.build().unwrap();
1205        let to = t.to_type_object().expect("enum bridge ok");
1206        match to {
1207            TypeObject::Complete(CompleteTypeObject::Enumerated(e)) => {
1208                assert_eq!(e.literal_seq.len(), 3);
1209                assert_eq!(e.literal_seq[0].detail.name, "RED");
1210                assert_eq!(e.literal_seq[2].common.value, 2);
1211            }
1212            other => panic!("expected Enumerated, got {other:?}"),
1213        }
1214    }
1215
1216    #[test]
1217    fn dynamic_bitmask_to_typeobject_complete() {
1218        let mut desc = TypeDescriptor::primitive(TypeKind::Bitmask, "::Flags");
1219        desc.kind = TypeKind::Bitmask;
1220        desc.bound = alloc::vec![8];
1221        let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1222        for (pos, name) in [(0u32, "A"), (3u32, "D"), (7u32, "H")] {
1223            let m = MemberDescriptor::new(
1224                name,
1225                pos,
1226                TypeDescriptor::primitive(TypeKind::Boolean, "boolean"),
1227            );
1228            b.add_member(m).unwrap();
1229        }
1230        let t = b.build().unwrap();
1231        let to = t.to_type_object().expect("bitmask bridge ok");
1232        match to {
1233            TypeObject::Complete(CompleteTypeObject::Bitmask(bm)) => {
1234                assert_eq!(bm.bit_bound, 8);
1235                assert_eq!(bm.flag_seq.len(), 3);
1236                assert_eq!(bm.flag_seq[1].common.position, 3);
1237            }
1238            other => panic!("expected Bitmask, got {other:?}"),
1239        }
1240    }
1241
1242    #[test]
1243    fn dynamic_bitset_to_typeobject_complete() {
1244        let mut desc = TypeDescriptor::primitive(TypeKind::Bitset, "::Reg");
1245        desc.kind = TypeKind::Bitset;
1246        let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1247        for (pos, name, width) in [(0u32, "lo", 3u8), (3u32, "hi", 5u8)] {
1248            let mut m = MemberDescriptor::new(
1249                name,
1250                pos,
1251                TypeDescriptor::primitive(TypeKind::UInt8, "uint8"),
1252            );
1253            m.bit_bound = Some(width);
1254            b.add_member(m).unwrap();
1255        }
1256        let t = b.build().unwrap();
1257        let to = t.to_type_object().expect("bitset bridge ok");
1258        match to {
1259            TypeObject::Complete(CompleteTypeObject::Bitset(bs)) => {
1260                assert_eq!(bs.field_seq.len(), 2);
1261                assert_eq!(bs.field_seq[0].common.position, 0);
1262                assert_eq!(bs.field_seq[0].common.bitcount, 3);
1263                assert_eq!(bs.field_seq[0].detail.name, "lo");
1264                assert_eq!(bs.field_seq[1].common.position, 3);
1265                assert_eq!(bs.field_seq[1].common.bitcount, 5);
1266                assert_eq!(
1267                    bs.field_seq[1].common.holder_type,
1268                    PrimitiveKind::UInt8.to_u8()
1269                );
1270            }
1271            other => panic!("expected Bitset, got {other:?}"),
1272        }
1273    }
1274
1275    #[test]
1276    fn dynamic_annotation_to_typeobject_complete() {
1277        let mut desc = TypeDescriptor::primitive(TypeKind::Annotation, "::Range");
1278        desc.kind = TypeKind::Annotation;
1279        let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
1280        let mut m = MemberDescriptor::new(
1281            "max",
1282            1,
1283            TypeDescriptor::primitive(TypeKind::Int32, "int32"),
1284        );
1285        m.default_value = Some("100".into());
1286        b.add_member(m).unwrap();
1287        let t = b.build().unwrap();
1288        let to = t.to_type_object().expect("annotation bridge ok");
1289        match to {
1290            TypeObject::Complete(CompleteTypeObject::Annotation(a)) => {
1291                assert_eq!(a.member_seq.len(), 1);
1292                assert_eq!(a.member_seq[0].name, "max");
1293                assert_eq!(a.member_seq[0].member_id, 1);
1294                assert_eq!(a.member_seq[0].default_value, b"100".to_vec());
1295                assert!(matches!(
1296                    a.member_seq[0].member_type_id,
1297                    TypeIdentifier::Primitive(PrimitiveKind::Int32)
1298                ));
1299            }
1300            other => panic!("expected Annotation, got {other:?}"),
1301        }
1302    }
1303
1304    #[test]
1305    fn dynamic_sequence_to_typeobject_complete() {
1306        let desc = TypeDescriptor::sequence(
1307            "::Seq",
1308            TypeDescriptor::primitive(TypeKind::Int32, "int32"),
1309            10,
1310        );
1311        let t = DynamicTypeBuilderFactory::create_type(desc)
1312            .unwrap()
1313            .build()
1314            .unwrap();
1315        match t.to_type_object().expect("sequence bridge ok") {
1316            TypeObject::Complete(CompleteTypeObject::Sequence(s)) => {
1317                assert_eq!(s.bound, 10);
1318                assert_eq!(s.detail.type_name, "::Seq");
1319                assert!(matches!(
1320                    s.element.common.type_id,
1321                    TypeIdentifier::Primitive(PrimitiveKind::Int32)
1322                ));
1323            }
1324            other => panic!("expected Sequence, got {other:?}"),
1325        }
1326    }
1327
1328    #[test]
1329    fn dynamic_array_to_typeobject_complete() {
1330        let desc = TypeDescriptor::array(
1331            "::Arr",
1332            TypeDescriptor::primitive(TypeKind::Float64, "double"),
1333            alloc::vec![3, 4],
1334        );
1335        let t = DynamicTypeBuilderFactory::create_type(desc)
1336            .unwrap()
1337            .build()
1338            .unwrap();
1339        match t.to_type_object().expect("array bridge ok") {
1340            TypeObject::Complete(CompleteTypeObject::Array(a)) => {
1341                assert_eq!(a.bound_seq, alloc::vec![3, 4]);
1342                assert!(matches!(
1343                    a.element.common.type_id,
1344                    TypeIdentifier::Primitive(PrimitiveKind::Float64)
1345                ));
1346            }
1347            other => panic!("expected Array, got {other:?}"),
1348        }
1349    }
1350
1351    #[test]
1352    fn dynamic_map_to_typeobject_complete() {
1353        let desc = TypeDescriptor::map(
1354            "::Map",
1355            TypeDescriptor::primitive(TypeKind::Int32, "int32"),
1356            TypeDescriptor::primitive(TypeKind::Boolean, "boolean"),
1357            7,
1358        );
1359        let t = DynamicTypeBuilderFactory::create_type(desc)
1360            .unwrap()
1361            .build()
1362            .unwrap();
1363        match t.to_type_object().expect("map bridge ok") {
1364            TypeObject::Complete(CompleteTypeObject::Map(m)) => {
1365                assert_eq!(m.bound, 7);
1366                assert!(matches!(
1367                    m.key.common.type_id,
1368                    TypeIdentifier::Primitive(PrimitiveKind::Int32)
1369                ));
1370                assert!(matches!(
1371                    m.element.common.type_id,
1372                    TypeIdentifier::Primitive(PrimitiveKind::Boolean)
1373                ));
1374            }
1375            other => panic!("expected Map, got {other:?}"),
1376        }
1377    }
1378
1379    #[test]
1380    fn dynamic_union_to_typeobject_complete() {
1381        let disc = TypeDescriptor::primitive(TypeKind::Int32, "int32");
1382        let t = {
1383            let mut b = DynamicTypeBuilderFactory::create_union("::Shape", disc).unwrap();
1384            let mut m1 = MemberDescriptor::new(
1385                "circle",
1386                1,
1387                TypeDescriptor::primitive(TypeKind::Float64, "double"),
1388            );
1389            m1.label = alloc::vec![1];
1390            let mut m2 = MemberDescriptor::new(
1391                "square",
1392                2,
1393                TypeDescriptor::primitive(TypeKind::Float64, "double"),
1394            );
1395            m2.label = alloc::vec![2, 3];
1396            b.add_member(m1).unwrap();
1397            b.add_member(m2).unwrap();
1398            b.build().unwrap()
1399        };
1400        let to = t.to_type_object().expect("union bridge ok");
1401        match to {
1402            TypeObject::Complete(CompleteTypeObject::Union(u)) => {
1403                assert_eq!(u.member_seq.len(), 2);
1404                assert_eq!(u.member_seq[0].detail.name, "circle");
1405                assert_eq!(u.member_seq[1].common.label_seq, alloc::vec![2, 3]);
1406                assert!(matches!(
1407                    u.discriminator.common.type_id,
1408                    TypeIdentifier::Primitive(PrimitiveKind::Int32)
1409                ));
1410            }
1411            other => panic!("expected Union, got {other:?}"),
1412        }
1413    }
1414
1415    #[test]
1416    fn create_type_w_minimal_typeobject_is_unsupported() {
1417        use crate::type_object::MinimalTypeObject;
1418        use crate::type_object::minimal::{MinimalStructHeader, MinimalStructType};
1419        let m = MinimalStructType {
1420            struct_flags: StructTypeFlag::default(),
1421            header: MinimalStructHeader {
1422                base_type: TypeIdentifier::None,
1423            },
1424            member_seq: alloc::vec![],
1425        };
1426        let to = TypeObject::Minimal(MinimalTypeObject::Struct(m));
1427        let err = DynamicTypeBuilderFactory::create_type_w_type_object(&to).unwrap_err();
1428        assert!(matches!(err, DynamicError::Unsupported(_)));
1429    }
1430
1431    #[test]
1432    fn resolve_nested_struct_member_fully_via_registry() {
1433        use crate::builder::TypeObjectBuilder;
1434        use crate::resolve::TypeRegistry;
1435        use crate::type_identifier::EquivalenceHash;
1436
1437        // inner: Point { x: int32, y: int32 }
1438        let inner = TypeObjectBuilder::struct_type("::Point")
1439            .member("x", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| m)
1440            .member("y", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| m)
1441            .build_complete();
1442        let h = EquivalenceHash([0x42; 14]);
1443        let mut reg = TypeRegistry::new();
1444        reg.insert_complete(h, CompleteTypeObject::Struct(inner));
1445
1446        // outer: Line { start: Point (by hash), len: int32 }
1447        let outer = TypeObjectBuilder::struct_type("::Line")
1448            .member("start", TypeIdentifier::EquivalenceHashComplete(h), |m| m)
1449            .member(
1450                "len",
1451                TypeIdentifier::Primitive(PrimitiveKind::Int32),
1452                |m| m,
1453            )
1454            .build_complete();
1455        let to = TypeObject::Complete(CompleteTypeObject::Struct(outer));
1456
1457        let dt = DynamicTypeBuilderFactory::create_type_w_type_object_in(&to, &reg).unwrap();
1458        assert_eq!(dt.member_count(), 2);
1459        let start = dt.member_by_name("start").expect("start member");
1460        // The nested Point is FULLY resolved (not a shallow <typeref> placeholder).
1461        assert_eq!(start.dynamic_type().kind(), TypeKind::Structure);
1462        assert_eq!(start.dynamic_type().member_count(), 2);
1463        assert!(start.dynamic_type().member_by_name("x").is_some());
1464        assert!(start.dynamic_type().member_by_name("y").is_some());
1465        // A missing-hash reference resolves to an honest Unsupported, not a panic.
1466        let dangling = TypeObjectBuilder::struct_type("::Bad")
1467            .member(
1468                "ref",
1469                TypeIdentifier::EquivalenceHashComplete(EquivalenceHash([0xFF; 14])),
1470                |m| m,
1471            )
1472            .build_complete();
1473        let bad = TypeObject::Complete(CompleteTypeObject::Struct(dangling));
1474        assert!(matches!(
1475            DynamicTypeBuilderFactory::create_type_w_type_object_in(&bad, &reg),
1476            Err(DynamicError::Unsupported(_))
1477        ));
1478    }
1479}