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//! Diese Bridge ist die Klammer zwischen der **Wire-Repraesentation**
6//! (`TypeObject`, mit `MinimalTypeObject` + `CompleteTypeObject` als
7//! Diskrimination) und der **Runtime-API** (`DynamicType`).
8//!
9//! Implementiert: Struct + Union + Enum + Alias + Sequence/Array/Map.
10//! Bitset/Bitmask/Annotation: Spec-konforme Reject-Errors, Implementation folgt mit MemberDescriptor-Erweiterung (siehe
11//! [`DynamicError::Unsupported`]). Forward-Mapping von Composite-Member-
12//! Types ueber den Namen — TypeRegistry-Lookup auf
13//! EquivalenceHash-TypeIdentifiers ist Aufgabe von C4.2.
14
15use alloc::format;
16use alloc::string::{String, ToString};
17use alloc::vec::Vec;
18
19use crate::type_identifier::{PrimitiveKind, TypeIdentifier};
20use crate::type_object::common::{CommonStructMember, CommonUnionMember};
21use crate::type_object::complete::{
22    CompleteAliasBody, CompleteAliasHeader, CompleteAliasType, CompleteBitflag,
23    CompleteBitmaskType, CompleteDiscriminatorMember, CompleteEnumeratedHeader,
24    CompleteEnumeratedLiteral, CompleteEnumeratedType, CompleteStructHeader, CompleteStructMember,
25    CompleteStructType, CompleteUnionHeader, CompleteUnionMember, CompleteUnionType,
26};
27use crate::type_object::flags::{
28    AliasMemberFlag, AliasTypeFlag, BitflagFlag, BitmaskTypeFlag, EnumLiteralFlag, EnumTypeFlag,
29    StructMemberFlag, StructTypeFlag, UnionDiscriminatorFlag, UnionMemberFlag, UnionTypeFlag,
30};
31use crate::type_object::minimal::CommonDiscriminatorMember;
32use crate::type_object::minimal::{CommonBitflag, CommonEnumeratedHeader, CommonEnumeratedLiteral};
33use crate::type_object::{CompleteTypeObject, TypeObject};
34
35use super::builder::{DynamicTypeBuilder, DynamicTypeBuilderFactory};
36use super::descriptor::{ExtensibilityKind, MemberDescriptor, TypeDescriptor, TypeKind};
37use super::error::DynamicError;
38use super::type_::DynamicType;
39
40// ----------------------------------------------------------------------
41// DynamicType → TypeObject (Spec §7.6.3 — fuer Discovery + TypeLookup)
42// ----------------------------------------------------------------------
43
44impl DynamicType {
45    /// Spec §7.6.3 — wandelt diese DynamicType in ein
46    /// `CompleteTypeObject`. Liefert immer Complete (nicht Minimal),
47    /// weil DynamicType die Namen + Annotations traegt.
48    ///
49    /// Scope:
50    /// - Struct mit primitiven + String + Sequence/Array Members.
51    /// - Union, Enum, Alias als eigene Helper-Methoden (siehe Modul-Doc).
52    ///
53    /// # Errors
54    /// `Unsupported` fuer noch nicht implementierte Kinds,
55    /// `Inconsistent` wenn der Type fehlerhaft ist.
56    pub fn to_type_object(&self) -> Result<TypeObject, DynamicError> {
57        match self.kind() {
58            TypeKind::Structure => Ok(TypeObject::Complete(CompleteTypeObject::Struct(
59                self.to_complete_struct()?,
60            ))),
61            TypeKind::Union => Ok(TypeObject::Complete(CompleteTypeObject::Union(
62                self.to_complete_union()?,
63            ))),
64            TypeKind::Enumeration => Ok(TypeObject::Complete(CompleteTypeObject::Enumerated(
65                self.to_complete_enum()?,
66            ))),
67            TypeKind::Bitmask => Ok(TypeObject::Complete(CompleteTypeObject::Bitmask(
68                self.to_complete_bitmask()?,
69            ))),
70            TypeKind::Alias => Ok(TypeObject::Complete(CompleteTypeObject::Alias(
71                self.to_complete_alias()?,
72            ))),
73            TypeKind::Array | TypeKind::Sequence | TypeKind::Map => {
74                // Spec §7.3.4 — Collection-Types werden via TypeIdentifier
75                // exklusiv repraesentiert (PlainCollection / HashedCollection).
76                // Es gibt keinen CompleteTypeObject fuer plain Collections;
77                // nur ihre Element-Types haben TypeObjects.
78                Err(DynamicError::unsupported(format!(
79                    "to_type_object for {:?} — Collection-Types use TypeIdentifier exclusively (XTypes §7.3.4)",
80                    self.kind(),
81                )))
82            }
83            TypeKind::Bitset | TypeKind::Annotation => {
84                // Bitset benoetigt bit_position + bit_count pro Member;
85                // Annotation benoetigt default_value. Beide sind in
86                // MemberDescriptor nicht direkt strukturiert ablegbar
87                // ohne Zusatz-Konvention. MemberDescriptor-Erweiterung benötigt.
88                Err(DynamicError::unsupported(format!(
89                    "to_type_object for {:?} — needs MemberDescriptor extension (MemberDescriptor extension)",
90                    self.kind(),
91                )))
92            }
93            other => Err(DynamicError::unsupported(format!(
94                "to_type_object: {other:?} is a primitive/no-type kind without TypeObject",
95            ))),
96        }
97    }
98
99    fn to_complete_alias(&self) -> Result<CompleteAliasType, DynamicError> {
100        use crate::type_object::common::{
101            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteTypeDetail,
102            OptionalAppliedAnnotationSeq,
103        };
104        let header = CompleteAliasHeader {
105            detail: CompleteTypeDetail {
106                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
107                ann_custom: OptionalAppliedAnnotationSeq::default(),
108                type_name: self.descriptor().name.clone(),
109            },
110        };
111        let element = self.descriptor().element_type.as_ref().ok_or_else(|| {
112            DynamicError::inconsistent("alias type missing element_type (target)")
113        })?;
114        let related_type = descriptor_to_type_identifier(element)?;
115        let body = CompleteAliasBody {
116            related_flags: AliasMemberFlag(0),
117            related_type,
118            ann_builtin: AppliedBuiltinMemberAnnotations::default(),
119            ann_custom: OptionalAppliedAnnotationSeq::default(),
120        };
121        Ok(CompleteAliasType {
122            alias_flags: AliasTypeFlag(0),
123            header,
124            body,
125        })
126    }
127
128    fn to_complete_enum(&self) -> Result<CompleteEnumeratedType, DynamicError> {
129        use crate::type_object::common::{
130            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
131            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
132        };
133        let bit_bound = self.descriptor().bound.first().copied().unwrap_or(32);
134        let bit_bound_u16 = u16::try_from(bit_bound).unwrap_or(32);
135        let header = CompleteEnumeratedHeader {
136            common: CommonEnumeratedHeader {
137                bit_bound: bit_bound_u16,
138            },
139            detail: CompleteTypeDetail {
140                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
141                ann_custom: OptionalAppliedAnnotationSeq::default(),
142                type_name: self.descriptor().name.clone(),
143            },
144        };
145        let mut literal_seq: Vec<CompleteEnumeratedLiteral> =
146            Vec::with_capacity(self.member_count() as usize);
147        for m in self.members() {
148            let mut flags_bits: u16 = 0;
149            if m.descriptor().is_default_label {
150                flags_bits |= EnumLiteralFlag::IS_DEFAULT_LITERAL;
151            }
152            // Enum-Ordinalwert = MemberId (Convention).
153            let value = i32::try_from(m.id()).map_err(|_| {
154                DynamicError::inconsistent(format!("enum literal id {} exceeds i32 range", m.id()))
155            })?;
156            literal_seq.push(CompleteEnumeratedLiteral {
157                common: CommonEnumeratedLiteral {
158                    value,
159                    flags: EnumLiteralFlag(flags_bits),
160                },
161                detail: CompleteMemberDetail {
162                    name: m.name().to_string(),
163                    ann_builtin: AppliedBuiltinMemberAnnotations::default(),
164                    ann_custom: OptionalAppliedAnnotationSeq::default(),
165                },
166            });
167        }
168        Ok(CompleteEnumeratedType {
169            enum_flags: EnumTypeFlag(0),
170            header,
171            literal_seq,
172        })
173    }
174
175    fn to_complete_bitmask(&self) -> Result<CompleteBitmaskType, DynamicError> {
176        use crate::type_object::common::{
177            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
178            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
179        };
180        let bit_bound = self.descriptor().bound.first().copied().unwrap_or(32);
181        let bit_bound_u16 = u16::try_from(bit_bound).unwrap_or(32);
182        let detail = CompleteTypeDetail {
183            ann_builtin: AppliedBuiltinTypeAnnotations::default(),
184            ann_custom: OptionalAppliedAnnotationSeq::default(),
185            type_name: self.descriptor().name.clone(),
186        };
187        let mut flag_seq: Vec<CompleteBitflag> = Vec::with_capacity(self.member_count() as usize);
188        for m in self.members() {
189            let position = u16::try_from(m.id()).map_err(|_| {
190                DynamicError::inconsistent(format!(
191                    "bitmask flag position {} exceeds u16 range",
192                    m.id()
193                ))
194            })?;
195            flag_seq.push(CompleteBitflag {
196                common: CommonBitflag {
197                    position,
198                    flags: BitflagFlag(0),
199                },
200                detail: CompleteMemberDetail {
201                    name: m.name().to_string(),
202                    ann_builtin: AppliedBuiltinMemberAnnotations::default(),
203                    ann_custom: OptionalAppliedAnnotationSeq::default(),
204                },
205            });
206        }
207        Ok(CompleteBitmaskType {
208            bitmask_flags: BitmaskTypeFlag(0),
209            bit_bound: bit_bound_u16,
210            detail,
211            flag_seq,
212        })
213    }
214
215    fn to_complete_union(&self) -> Result<CompleteUnionType, DynamicError> {
216        use crate::type_object::common::{
217            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
218            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
219        };
220        let union_flags = UnionTypeFlag(extensibility_to_flag_bits(
221            self.descriptor().extensibility_kind,
222        ));
223        let header = CompleteUnionHeader {
224            detail: CompleteTypeDetail {
225                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
226                ann_custom: OptionalAppliedAnnotationSeq::default(),
227                type_name: self.descriptor().name.clone(),
228            },
229        };
230        let disc_descriptor = self
231            .descriptor()
232            .discriminator_type
233            .as_ref()
234            .ok_or_else(|| DynamicError::inconsistent("union type missing discriminator_type"))?;
235        let disc_type = descriptor_to_type_identifier(disc_descriptor)?;
236        let discriminator = CompleteDiscriminatorMember {
237            common: CommonDiscriminatorMember {
238                member_flags: UnionDiscriminatorFlag(0),
239                type_id: disc_type,
240            },
241            ann_builtin: AppliedBuiltinTypeAnnotations::default(),
242            ann_custom: OptionalAppliedAnnotationSeq::default(),
243        };
244        let mut member_seq: Vec<CompleteUnionMember> =
245            Vec::with_capacity(self.member_count() as usize);
246        for m in self.members() {
247            let mut flags_bits: u16 = 0;
248            if m.descriptor().is_default_label {
249                flags_bits |= UnionMemberFlag::IS_DEFAULT;
250            }
251            // Spec §7.3.4.5.3.2 — case-labels sind `long` (i32).
252            let label_seq: Vec<i32> = m
253                .descriptor()
254                .label
255                .iter()
256                .map(|&v| i32::try_from(v).unwrap_or_default())
257                .collect();
258            let common = CommonUnionMember {
259                member_id: m.id(),
260                member_flags: UnionMemberFlag(flags_bits),
261                type_id: descriptor_to_type_identifier(m.descriptor().member_type.as_ref())?,
262                label_seq,
263            };
264            let detail = CompleteMemberDetail {
265                name: m.name().to_string(),
266                ann_builtin: AppliedBuiltinMemberAnnotations::default(),
267                ann_custom: OptionalAppliedAnnotationSeq::default(),
268            };
269            member_seq.push(CompleteUnionMember { common, detail });
270        }
271        Ok(CompleteUnionType {
272            union_flags,
273            header,
274            discriminator,
275            member_seq,
276        })
277    }
278
279    fn to_complete_struct(&self) -> Result<CompleteStructType, DynamicError> {
280        use crate::type_object::common::{
281            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
282            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
283        };
284        let struct_flags = StructTypeFlag(extensibility_to_flag_bits(
285            self.descriptor().extensibility_kind,
286        ));
287        let header = CompleteStructHeader {
288            base_type: TypeIdentifier::None,
289            detail: CompleteTypeDetail {
290                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
291                ann_custom: OptionalAppliedAnnotationSeq::default(),
292                type_name: self.descriptor().name.clone(),
293            },
294        };
295        let mut member_seq: Vec<CompleteStructMember> =
296            Vec::with_capacity(self.member_count() as usize);
297        for m in self.members() {
298            let mut flags_bits: u16 = 0;
299            if m.descriptor().is_key {
300                flags_bits |= StructMemberFlag::IS_KEY;
301            }
302            if m.descriptor().is_optional {
303                flags_bits |= StructMemberFlag::IS_OPTIONAL;
304            }
305            if m.descriptor().is_must_understand {
306                flags_bits |= StructMemberFlag::IS_MUST_UNDERSTAND;
307            }
308            if m.descriptor().is_shared {
309                flags_bits |= StructMemberFlag::IS_EXTERNAL;
310            }
311            let common = CommonStructMember {
312                member_id: m.id(),
313                member_flags: StructMemberFlag(flags_bits),
314                member_type_id: descriptor_to_type_identifier(m.descriptor().member_type.as_ref())?,
315            };
316            let detail = CompleteMemberDetail {
317                name: m.name().to_string(),
318                ann_builtin: AppliedBuiltinMemberAnnotations::default(),
319                ann_custom: OptionalAppliedAnnotationSeq::default(),
320            };
321            member_seq.push(CompleteStructMember { common, detail });
322        }
323        Ok(CompleteStructType {
324            struct_flags,
325            header,
326            member_seq,
327        })
328    }
329}
330
331const fn extensibility_to_flag_bits(ext: ExtensibilityKind) -> u16 {
332    match ext {
333        ExtensibilityKind::Final => StructTypeFlag::IS_FINAL,
334        ExtensibilityKind::Appendable => StructTypeFlag::IS_APPENDABLE,
335        ExtensibilityKind::Mutable => StructTypeFlag::IS_MUTABLE,
336    }
337}
338
339const fn flag_bits_to_extensibility(flags: u16) -> ExtensibilityKind {
340    if flags & StructTypeFlag::IS_FINAL != 0 {
341        ExtensibilityKind::Final
342    } else if flags & StructTypeFlag::IS_MUTABLE != 0 {
343        ExtensibilityKind::Mutable
344    } else {
345        ExtensibilityKind::Appendable
346    }
347}
348
349/// Mapping von einem Member-`TypeDescriptor` auf einen passenden
350/// `TypeIdentifier`. Nur primitive + String + Sequence + Array werden
351/// inline kodiert; Composite-Members verlangen eine TypeRegistry-
352/// Lookup-Strategie über `zerodds_types::resolve::TypeRegistry`.
353fn descriptor_to_type_identifier(desc: &TypeDescriptor) -> Result<TypeIdentifier, DynamicError> {
354    match desc.kind {
355        TypeKind::Boolean => Ok(TypeIdentifier::Primitive(PrimitiveKind::Boolean)),
356        TypeKind::Byte => Ok(TypeIdentifier::Primitive(PrimitiveKind::Byte)),
357        TypeKind::Int8 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Int8)),
358        TypeKind::UInt8 => Ok(TypeIdentifier::Primitive(PrimitiveKind::UInt8)),
359        TypeKind::Int16 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Int16)),
360        TypeKind::UInt16 => Ok(TypeIdentifier::Primitive(PrimitiveKind::UInt16)),
361        TypeKind::Int32 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Int32)),
362        TypeKind::UInt32 => Ok(TypeIdentifier::Primitive(PrimitiveKind::UInt32)),
363        TypeKind::Int64 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Int64)),
364        TypeKind::UInt64 => Ok(TypeIdentifier::Primitive(PrimitiveKind::UInt64)),
365        TypeKind::Float32 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Float32)),
366        TypeKind::Float64 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Float64)),
367        TypeKind::Float128 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Float128)),
368        TypeKind::Char8 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Char8)),
369        TypeKind::Char16 => Ok(TypeIdentifier::Primitive(PrimitiveKind::Char16)),
370        TypeKind::String8 => {
371            let bound = desc.bound.first().copied().unwrap_or(0);
372            if bound <= u32::from(u8::MAX) {
373                Ok(TypeIdentifier::String8Small { bound: bound as u8 })
374            } else {
375                Ok(TypeIdentifier::String8Large { bound })
376            }
377        }
378        TypeKind::String16 => {
379            let bound = desc.bound.first().copied().unwrap_or(0);
380            if bound <= u32::from(u8::MAX) {
381                Ok(TypeIdentifier::String16Small { bound: bound as u8 })
382            } else {
383                Ok(TypeIdentifier::String16Large { bound })
384            }
385        }
386        TypeKind::Structure | TypeKind::Union | TypeKind::Enumeration | TypeKind::Alias => {
387            // Bridge-Phase 1: Composite-Members tragen `TypeIdentifier::None`
388            // — die TypeRegistry (C4.2) loest spaeter ueber den Namen
389            // gegen einen EquivalenceHash. Roundtrip-Tests setzen diese
390            // Information getrennt.
391            Ok(TypeIdentifier::None)
392        }
393        kind => Err(DynamicError::unsupported(format!(
394            "descriptor_to_type_identifier: {kind:?} not yet covered"
395        ))),
396    }
397}
398
399// ----------------------------------------------------------------------
400// TypeObject → DynamicType (Spec §7.6.4)
401// ----------------------------------------------------------------------
402
403impl DynamicTypeBuilderFactory {
404    /// Spec §7.6.4 / §7.5.5.1.4 `create_type_w_type_object(type_object)`.
405    ///
406    /// Scope:
407    /// - `CompleteStructType` mit primitiven + String-Members.
408    /// - `MinimalStructType` als read-only mit Hash-Names.
409    /// - Andere Kinds: `Unsupported`.
410    ///
411    /// # Errors
412    /// `Unsupported` fuer Kinds ausserhalb des aktuellen Implementations-Scopes.
413    pub fn create_type_w_type_object(
414        type_obj: &TypeObject,
415    ) -> Result<DynamicTypeBuilder, DynamicError> {
416        match type_obj {
417            TypeObject::Complete(c) => match c {
418                CompleteTypeObject::Struct(s) => complete_struct_to_builder(s),
419                other => Err(DynamicError::unsupported(format!(
420                    "complete-typeobject kind {} not yet supported",
421                    other_kind_name(other)
422                ))),
423            },
424            TypeObject::Minimal(_) => Err(DynamicError::unsupported(
425                "minimal-typeobject → dynamic-type pending C4.2 TypeRegistry",
426            )),
427        }
428    }
429}
430
431fn complete_struct_to_builder(s: &CompleteStructType) -> Result<DynamicTypeBuilder, DynamicError> {
432    let mut desc = TypeDescriptor::structure(s.header.detail.type_name.clone());
433    desc.extensibility_kind = flag_bits_to_extensibility(s.struct_flags.0);
434    if (s.struct_flags.0 & StructTypeFlag::IS_NESTED) != 0 {
435        desc.is_nested = true;
436    }
437    let mut b = DynamicTypeBuilderFactory::create_type(desc)?;
438    for (idx, m) in s.member_seq.iter().enumerate() {
439        let kind = type_id_to_kind(&m.common.member_type_id)?;
440        let member_type = type_id_to_descriptor(&m.common.member_type_id, kind)?;
441        let mut md = MemberDescriptor::new(m.detail.name.clone(), m.common.member_id, member_type);
442        md.index = u32::try_from(idx).unwrap_or(u32::MAX);
443        md.is_key = (m.common.member_flags.0 & StructMemberFlag::IS_KEY) != 0;
444        md.is_optional = (m.common.member_flags.0 & StructMemberFlag::IS_OPTIONAL) != 0;
445        md.is_must_understand =
446            (m.common.member_flags.0 & StructMemberFlag::IS_MUST_UNDERSTAND) != 0;
447        md.is_shared = (m.common.member_flags.0 & StructMemberFlag::IS_EXTERNAL) != 0;
448        b.add_member(md)?;
449    }
450    Ok(b)
451}
452
453const fn other_kind_name(c: &CompleteTypeObject) -> &'static str {
454    match c {
455        CompleteTypeObject::Alias(_) => "alias",
456        CompleteTypeObject::Annotation(_) => "annotation",
457        CompleteTypeObject::Struct(_) => "struct",
458        CompleteTypeObject::Union(_) => "union",
459        CompleteTypeObject::Bitset(_) => "bitset",
460        CompleteTypeObject::Sequence(_) => "sequence",
461        CompleteTypeObject::Array(_) => "array",
462        CompleteTypeObject::Map(_) => "map",
463        CompleteTypeObject::Enumerated(_) => "enum",
464        CompleteTypeObject::Bitmask(_) => "bitmask",
465    }
466}
467
468fn type_id_to_kind(ti: &TypeIdentifier) -> Result<TypeKind, DynamicError> {
469    Ok(match ti {
470        TypeIdentifier::None => TypeKind::NoType,
471        TypeIdentifier::Primitive(p) => primitive_kind_to_type_kind(*p),
472        TypeIdentifier::String8Small { .. } | TypeIdentifier::String8Large { .. } => {
473            TypeKind::String8
474        }
475        TypeIdentifier::String16Small { .. } | TypeIdentifier::String16Large { .. } => {
476            TypeKind::String16
477        }
478        TypeIdentifier::PlainSequenceSmall { .. } | TypeIdentifier::PlainSequenceLarge { .. } => {
479            TypeKind::Sequence
480        }
481        TypeIdentifier::PlainArraySmall { .. } | TypeIdentifier::PlainArrayLarge { .. } => {
482            TypeKind::Array
483        }
484        TypeIdentifier::PlainMapSmall { .. } | TypeIdentifier::PlainMapLarge { .. } => {
485            TypeKind::Map
486        }
487        TypeIdentifier::EquivalenceHashMinimal(_) | TypeIdentifier::EquivalenceHashComplete(_) => {
488            // Composite via TypeRegistry-Lookup — markieren als Structure
489            // als Default; korrekt aufzuloesen ist C4.2.
490            TypeKind::Structure
491        }
492        other => {
493            return Err(DynamicError::unsupported(format!(
494                "type_id_to_kind: {other:?} not yet supported"
495            )));
496        }
497    })
498}
499
500const fn primitive_kind_to_type_kind(p: PrimitiveKind) -> TypeKind {
501    match p {
502        PrimitiveKind::Boolean => TypeKind::Boolean,
503        PrimitiveKind::Byte => TypeKind::Byte,
504        PrimitiveKind::Int8 => TypeKind::Int8,
505        PrimitiveKind::UInt8 => TypeKind::UInt8,
506        PrimitiveKind::Int16 => TypeKind::Int16,
507        PrimitiveKind::UInt16 => TypeKind::UInt16,
508        PrimitiveKind::Int32 => TypeKind::Int32,
509        PrimitiveKind::UInt32 => TypeKind::UInt32,
510        PrimitiveKind::Int64 => TypeKind::Int64,
511        PrimitiveKind::UInt64 => TypeKind::UInt64,
512        PrimitiveKind::Float32 => TypeKind::Float32,
513        PrimitiveKind::Float64 => TypeKind::Float64,
514        PrimitiveKind::Float128 => TypeKind::Float128,
515        PrimitiveKind::Char8 => TypeKind::Char8,
516        PrimitiveKind::Char16 => TypeKind::Char16,
517    }
518}
519
520/// zerodds-lint: recursion-depth 16
521///
522/// Rekursive Aufrufe via PlainSequence/PlainArray-Element. Tiefe ist
523/// implizit durch [`TypeIdentifier::MAX_DECODE_DEPTH`] gecapt — der
524/// Type-Identifier kann beim Wire-Decode hoechstens 16 Schichten tief
525/// werden, danach scheitert das Decode mit `LengthExceeded`.
526fn type_id_to_descriptor(
527    ti: &TypeIdentifier,
528    kind: TypeKind,
529) -> Result<TypeDescriptor, DynamicError> {
530    if kind.is_primitive() {
531        return Ok(TypeDescriptor::primitive(
532            kind,
533            super::type_::primitive_name(kind).to_string(),
534        ));
535    }
536    Ok(match ti {
537        TypeIdentifier::String8Small { bound } => TypeDescriptor::string8(u32::from(*bound)),
538        TypeIdentifier::String8Large { bound } => TypeDescriptor::string8(*bound),
539        TypeIdentifier::String16Small { bound } => TypeDescriptor::string16(u32::from(*bound)),
540        TypeIdentifier::String16Large { bound } => TypeDescriptor::string16(*bound),
541        TypeIdentifier::None => TypeDescriptor {
542            kind: TypeKind::Structure,
543            name: String::from("<unresolved>"),
544            base_type: None,
545            discriminator_type: None,
546            bound: Vec::new(),
547            element_type: None,
548            key_element_type: None,
549            extensibility_kind: ExtensibilityKind::default(),
550            is_nested: false,
551        },
552        TypeIdentifier::EquivalenceHashMinimal(_) | TypeIdentifier::EquivalenceHashComplete(_) => {
553            // Wir kennen den Namen erst nach einem TypeRegistry-Lookup
554            // (C4.2). Bis dahin: Platzhalter-Descriptor mit Kind=Structure.
555            TypeDescriptor {
556                kind: TypeKind::Structure,
557                name: String::from("<typeref>"),
558                base_type: None,
559                discriminator_type: None,
560                bound: Vec::new(),
561                element_type: None,
562                key_element_type: None,
563                extensibility_kind: ExtensibilityKind::default(),
564                is_nested: false,
565            }
566        }
567        TypeIdentifier::PlainSequenceSmall { bound, element, .. } => {
568            let elem_kind = type_id_to_kind(element)?;
569            TypeDescriptor::sequence(
570                "<seq>".to_string(),
571                type_id_to_descriptor(element, elem_kind)?,
572                u32::from(*bound),
573            )
574        }
575        TypeIdentifier::PlainSequenceLarge { bound, element, .. } => {
576            let elem_kind = type_id_to_kind(element)?;
577            TypeDescriptor::sequence(
578                "<seq>".to_string(),
579                type_id_to_descriptor(element, elem_kind)?,
580                *bound,
581            )
582        }
583        TypeIdentifier::PlainArraySmall {
584            array_bounds,
585            element,
586            ..
587        } => {
588            let elem_kind = type_id_to_kind(element)?;
589            TypeDescriptor::array(
590                "<arr>".to_string(),
591                type_id_to_descriptor(element, elem_kind)?,
592                array_bounds.iter().map(|b| u32::from(*b)).collect(),
593            )
594        }
595        TypeIdentifier::PlainArrayLarge {
596            array_bounds,
597            element,
598            ..
599        } => {
600            let elem_kind = type_id_to_kind(element)?;
601            TypeDescriptor::array(
602                "<arr>".to_string(),
603                type_id_to_descriptor(element, elem_kind)?,
604                array_bounds.clone(),
605            )
606        }
607        other => {
608            return Err(DynamicError::unsupported(format!(
609                "type_id_to_descriptor: {other:?} not yet supported"
610            )));
611        }
612    })
613}
614
615#[cfg(test)]
616#[allow(
617    clippy::unwrap_used,
618    clippy::expect_used,
619    clippy::panic,
620    clippy::unreachable
621)]
622mod tests {
623    use super::*;
624    use crate::dynamic::DynamicTypeBuilderFactory;
625
626    fn build_int_struct() -> DynamicType {
627        let mut b = DynamicTypeBuilderFactory::create_struct("::Sensor");
628        b.add_struct_member("id", 1, TypeDescriptor::primitive(TypeKind::Int64, "int64"))
629            .unwrap();
630        b.add_struct_member(
631            "temp",
632            2,
633            TypeDescriptor::primitive(TypeKind::Float64, "double"),
634        )
635        .unwrap();
636        b.add_struct_member("name", 3, TypeDescriptor::string8(64))
637            .unwrap();
638        b.build().unwrap()
639    }
640
641    #[test]
642    fn dynamic_struct_to_typeobject_complete() {
643        let t = build_int_struct();
644        let to = t.to_type_object().unwrap();
645        match to {
646            TypeObject::Complete(CompleteTypeObject::Struct(s)) => {
647                assert_eq!(s.header.detail.type_name, "::Sensor");
648                assert_eq!(s.member_seq.len(), 3);
649                assert_eq!(s.member_seq[0].detail.name, "id");
650            }
651            _ => panic!("to_type_object on Structure must return Complete::Struct"),
652        }
653    }
654
655    #[test]
656    fn typeobject_complete_struct_back_to_dynamic_type() {
657        let t = build_int_struct();
658        let to = t.to_type_object().unwrap();
659        let b = DynamicTypeBuilderFactory::create_type_w_type_object(&to).unwrap();
660        let t2 = b.build().unwrap();
661        assert_eq!(t2.name(), "::Sensor");
662        assert_eq!(t2.member_count(), 3);
663        assert_eq!(t2.member_by_id(1).unwrap().name(), "id");
664        assert_eq!(t2.member_by_id(2).unwrap().name(), "temp");
665        assert_eq!(t2.member_by_id(3).unwrap().name(), "name");
666    }
667
668    #[test]
669    fn roundtrip_dynamic_to_typeobject_to_dynamic_equals_logically() {
670        let t = build_int_struct();
671        let to = t.to_type_object().unwrap();
672        let b = DynamicTypeBuilderFactory::create_type_w_type_object(&to).unwrap();
673        let t2 = b.build().unwrap();
674        // Deep-Equality auf den von der Bridge gebauten Type.
675        assert!(t.equals(&t2), "roundtrip failed: {t:?} vs {t2:?}");
676    }
677
678    #[test]
679    fn unsupported_kind_to_typeobject_yields_unsupported_error() {
680        let prim = DynamicTypeBuilderFactory::get_primitive_type(TypeKind::Int32).unwrap();
681        let err = prim.to_type_object().unwrap_err();
682        assert!(matches!(err, DynamicError::Unsupported(_)));
683    }
684
685    #[test]
686    fn dynamic_alias_to_typeobject_complete() {
687        // alias mit element_type = int32.
688        let mut desc = TypeDescriptor::primitive(TypeKind::Int32, "int32");
689        desc.kind = TypeKind::Alias;
690        desc.name = "::SensorId".to_string();
691        desc.element_type = Some(alloc::boxed::Box::new(TypeDescriptor::primitive(
692            TypeKind::Int32,
693            "int32",
694        )));
695        let t = DynamicTypeBuilderFactory::create_type(desc)
696            .unwrap()
697            .build()
698            .unwrap();
699        let to = t.to_type_object().expect("alias bridge ok");
700        match to {
701            TypeObject::Complete(CompleteTypeObject::Alias(a)) => {
702                assert_eq!(a.header.detail.type_name, "::SensorId");
703                assert!(matches!(a.body.related_type, TypeIdentifier::Primitive(_)));
704            }
705            other => panic!("expected Alias, got {other:?}"),
706        }
707    }
708
709    #[test]
710    fn dynamic_enum_to_typeobject_complete() {
711        let mut desc = TypeDescriptor::primitive(TypeKind::Enumeration, "::Color");
712        desc.kind = TypeKind::Enumeration;
713        desc.bound = alloc::vec![32];
714        let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
715        for (id, name) in [(0u32, "RED"), (1u32, "GREEN"), (2u32, "BLUE")] {
716            let m = MemberDescriptor::new(
717                name,
718                id,
719                TypeDescriptor::primitive(TypeKind::Int32, "int32"),
720            );
721            b.add_member(m).unwrap();
722        }
723        let t = b.build().unwrap();
724        let to = t.to_type_object().expect("enum bridge ok");
725        match to {
726            TypeObject::Complete(CompleteTypeObject::Enumerated(e)) => {
727                assert_eq!(e.literal_seq.len(), 3);
728                assert_eq!(e.literal_seq[0].detail.name, "RED");
729                assert_eq!(e.literal_seq[2].common.value, 2);
730            }
731            other => panic!("expected Enumerated, got {other:?}"),
732        }
733    }
734
735    #[test]
736    fn dynamic_bitmask_to_typeobject_complete() {
737        let mut desc = TypeDescriptor::primitive(TypeKind::Bitmask, "::Flags");
738        desc.kind = TypeKind::Bitmask;
739        desc.bound = alloc::vec![8];
740        let mut b = DynamicTypeBuilderFactory::create_type(desc).unwrap();
741        for (pos, name) in [(0u32, "A"), (3u32, "D"), (7u32, "H")] {
742            let m = MemberDescriptor::new(
743                name,
744                pos,
745                TypeDescriptor::primitive(TypeKind::Boolean, "boolean"),
746            );
747            b.add_member(m).unwrap();
748        }
749        let t = b.build().unwrap();
750        let to = t.to_type_object().expect("bitmask bridge ok");
751        match to {
752            TypeObject::Complete(CompleteTypeObject::Bitmask(bm)) => {
753                assert_eq!(bm.bit_bound, 8);
754                assert_eq!(bm.flag_seq.len(), 3);
755                assert_eq!(bm.flag_seq[1].common.position, 3);
756            }
757            other => panic!("expected Bitmask, got {other:?}"),
758        }
759    }
760
761    #[test]
762    fn dynamic_union_to_typeobject_complete() {
763        let disc = TypeDescriptor::primitive(TypeKind::Int32, "int32");
764        let t = {
765            let mut b = DynamicTypeBuilderFactory::create_union("::Shape", disc).unwrap();
766            let mut m1 = MemberDescriptor::new(
767                "circle",
768                1,
769                TypeDescriptor::primitive(TypeKind::Float64, "double"),
770            );
771            m1.label = alloc::vec![1];
772            let mut m2 = MemberDescriptor::new(
773                "square",
774                2,
775                TypeDescriptor::primitive(TypeKind::Float64, "double"),
776            );
777            m2.label = alloc::vec![2, 3];
778            b.add_member(m1).unwrap();
779            b.add_member(m2).unwrap();
780            b.build().unwrap()
781        };
782        let to = t.to_type_object().expect("union bridge ok");
783        match to {
784            TypeObject::Complete(CompleteTypeObject::Union(u)) => {
785                assert_eq!(u.member_seq.len(), 2);
786                assert_eq!(u.member_seq[0].detail.name, "circle");
787                assert_eq!(u.member_seq[1].common.label_seq, alloc::vec![2, 3]);
788                assert!(matches!(
789                    u.discriminator.common.type_id,
790                    TypeIdentifier::Primitive(PrimitiveKind::Int32)
791                ));
792            }
793            other => panic!("expected Union, got {other:?}"),
794        }
795    }
796
797    #[test]
798    fn collection_kinds_to_typeobject_yield_explicit_error() {
799        // Sequence ist TypeIdentifier-only — to_type_object muss einen
800        // klaren Spec-konformen Error zurueckgeben.
801        let mut seq_desc = TypeDescriptor::primitive(TypeKind::Sequence, "sequence");
802        seq_desc.kind = TypeKind::Sequence;
803        seq_desc.element_type = Some(alloc::boxed::Box::new(TypeDescriptor::primitive(
804            TypeKind::Int32,
805            "int32",
806        )));
807        seq_desc.bound = alloc::vec![16];
808        let seq = DynamicTypeBuilderFactory::create_type(seq_desc)
809            .unwrap()
810            .build()
811            .unwrap();
812        let err = seq.to_type_object().unwrap_err();
813        match err {
814            DynamicError::Unsupported(msg) => {
815                assert!(msg.contains("Collection-Types"), "msg: {msg}");
816            }
817            other => panic!("expected Unsupported, got {other:?}"),
818        }
819    }
820
821    #[test]
822    fn create_type_w_minimal_typeobject_is_unsupported() {
823        use crate::type_object::MinimalTypeObject;
824        use crate::type_object::minimal::{MinimalStructHeader, MinimalStructType};
825        let m = MinimalStructType {
826            struct_flags: StructTypeFlag::default(),
827            header: MinimalStructHeader {
828                base_type: TypeIdentifier::None,
829            },
830            member_seq: alloc::vec![],
831        };
832        let to = TypeObject::Minimal(MinimalTypeObject::Struct(m));
833        let err = DynamicTypeBuilderFactory::create_type_w_type_object(&to).unwrap_err();
834        assert!(matches!(err, DynamicError::Unsupported(_)));
835    }
836}