Skip to main content

zerodds_types/type_object/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! TypeObject (XTypes 1.3 §7.3.4.4).
4//!
5//! TypeObject ist eine Union `{ MinimalTypeObject | CompleteTypeObject }`.
6//! Im Wire-Format wird der Diskriminator als `EquivalenceKind` (1 byte)
7//! kodiert: `EK_MINIMAL=0xF1`, `EK_COMPLETE=0xF2`.
8//!
9//! Beide Varianten sind vollstaendig implementiert (T2 Minimal, T3 Complete).
10
11pub mod common;
12pub mod complete;
13pub mod flags;
14pub mod kinds;
15pub mod minimal;
16
17use zerodds_cdr::{BufferReader, BufferWriter, EncodeError};
18
19use crate::error::TypeCodecError;
20use crate::type_identifier::kinds::{EK_COMPLETE, EK_MINIMAL};
21
22pub use complete::CompleteTypeObject;
23pub use minimal::MinimalTypeObject;
24
25/// TypeObject-Wrapper (Minimal oder Complete).
26///
27/// `Complete` ist deutlich groesser als `Minimal` (Namen + Annotationen
28/// laut XTypes 1.3 §7.3.1). Wire-Layout ist Spec-normativ; Boxing der
29/// `Complete`-Variante wuerde ~20 Konstruktor-Callsites umbiegen ohne
30/// echten Memory-Gewinn (TypeObject lebt selten auf dem Hot-Path,
31/// sondern in TypeLookup-Caches).
32#[allow(clippy::large_enum_variant)]
33#[derive(Debug, Clone, PartialEq, Eq)]
34#[non_exhaustive]
35pub enum TypeObject {
36    /// Minimal-Repraesentation (hash-genau, namenlos).
37    Minimal(MinimalTypeObject),
38    /// Complete-Repraesentation (mit Namen + Annotationen).
39    Complete(CompleteTypeObject),
40}
41
42impl TypeObject {
43    /// EquivalenceKind-Byte.
44    #[must_use]
45    pub const fn discriminator(&self) -> u8 {
46        match self {
47            Self::Minimal(_) => EK_MINIMAL,
48            Self::Complete(_) => EK_COMPLETE,
49        }
50    }
51
52    /// Encode als `{ octet _d; body }`.
53    ///
54    /// # Errors
55    /// Buffer-Overflow.
56    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
57        w.write_u8(self.discriminator())?;
58        match self {
59            Self::Minimal(m) => m.encode_into(w),
60            Self::Complete(c) => c.encode_into(w),
61        }
62    }
63
64    /// Decode.
65    ///
66    /// # Errors
67    /// `TypeCodecError::UnknownTypeKind` bei unbekanntem Equivalence-Kind.
68    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, TypeCodecError> {
69        let d = r.read_u8()?;
70        match d {
71            EK_MINIMAL => Ok(Self::Minimal(MinimalTypeObject::decode_from(r)?)),
72            EK_COMPLETE => Ok(Self::Complete(CompleteTypeObject::decode_from(r)?)),
73            other => Err(TypeCodecError::UnknownTypeKind { kind: other }),
74        }
75    }
76
77    /// Convenience: LE-Bytes.
78    ///
79    /// # Errors
80    /// Encode-Fehler.
81    pub fn to_bytes_le(&self) -> Result<alloc::vec::Vec<u8>, EncodeError> {
82        let mut w = BufferWriter::new(zerodds_cdr::Endianness::Little);
83        self.encode_into(&mut w)?;
84        Ok(w.into_bytes())
85    }
86
87    /// Convenience: aus LE-Bytes.
88    ///
89    /// # Errors
90    /// Decode-Fehler.
91    pub fn from_bytes_le(bytes: &[u8]) -> Result<Self, TypeCodecError> {
92        let mut r = BufferReader::new(bytes, zerodds_cdr::Endianness::Little);
93        Self::decode_from(&mut r)
94    }
95}
96
97// ============================================================================
98// Tests
99// ============================================================================
100
101#[cfg(test)]
102#[allow(clippy::unwrap_used, clippy::panic)]
103mod tests {
104    use super::*;
105    use crate::type_identifier::{PrimitiveKind, TypeIdentifier};
106    use crate::type_object::common::{CommonStructMember, CommonUnionMember, NameHash};
107    use crate::type_object::flags::{
108        AliasMemberFlag, AliasTypeFlag, BitmaskTypeFlag, CollectionElementFlag, CollectionTypeFlag,
109        EnumLiteralFlag, EnumTypeFlag, StructMemberFlag, StructTypeFlag, UnionDiscriminatorFlag,
110        UnionMemberFlag, UnionTypeFlag,
111    };
112    use crate::type_object::minimal::{
113        CommonAliasBody, CommonCollectionElement, CommonDiscriminatorMember,
114        CommonEnumeratedHeader, CommonEnumeratedLiteral, MinimalAliasBody, MinimalAliasType,
115        MinimalArrayType, MinimalCollectionElement, MinimalDiscriminatorMember,
116        MinimalEnumeratedHeader, MinimalEnumeratedLiteral, MinimalEnumeratedType, MinimalMapType,
117        MinimalSequenceType, MinimalStructHeader, MinimalStructMember, MinimalStructType,
118        MinimalUnionMember, MinimalUnionType,
119    };
120
121    fn roundtrip(to: TypeObject) {
122        let bytes = to.to_bytes_le().unwrap();
123        let decoded = TypeObject::from_bytes_le(&bytes).unwrap();
124        assert_eq!(to, decoded);
125    }
126
127    fn name_hash(bytes: [u8; 4]) -> NameHash {
128        NameHash(bytes)
129    }
130
131    // ------------------------------------------------------------------
132    // Struct
133    // ------------------------------------------------------------------
134
135    #[test]
136    fn minimal_struct_sensor_with_two_fields() {
137        let st = MinimalStructType {
138            struct_flags: StructTypeFlag(StructTypeFlag::IS_APPENDABLE),
139            header: MinimalStructHeader {
140                base_type: TypeIdentifier::None,
141            },
142            member_seq: alloc::vec![
143                MinimalStructMember {
144                    common: CommonStructMember {
145                        member_id: 1,
146                        member_flags: StructMemberFlag(StructMemberFlag::IS_KEY),
147                        member_type_id: TypeIdentifier::Primitive(PrimitiveKind::Int64),
148                    },
149                    detail: name_hash([0xde, 0xad, 0xbe, 0xef]),
150                },
151                MinimalStructMember {
152                    common: CommonStructMember {
153                        member_id: 2,
154                        member_flags: StructMemberFlag::default(),
155                        member_type_id: TypeIdentifier::String8Small { bound: 128 },
156                    },
157                    detail: name_hash([0xfe, 0xed, 0xfa, 0xce]),
158                },
159            ],
160        };
161        roundtrip(TypeObject::Minimal(MinimalTypeObject::Struct(st)));
162    }
163
164    #[test]
165    fn minimal_struct_with_base_type() {
166        let st = MinimalStructType {
167            struct_flags: StructTypeFlag::empty(),
168            header: MinimalStructHeader {
169                base_type: TypeIdentifier::EquivalenceHashMinimal(
170                    crate::type_identifier::EquivalenceHash([0x01; 14]),
171                ),
172            },
173            member_seq: alloc::vec![],
174        };
175        roundtrip(TypeObject::Minimal(MinimalTypeObject::Struct(st)));
176    }
177
178    #[test]
179    fn minimal_struct_mutable_final_flags() {
180        for flags in [
181            StructTypeFlag::IS_FINAL,
182            StructTypeFlag::IS_MUTABLE | StructTypeFlag::IS_AUTOID_HASH,
183        ] {
184            let st = MinimalStructType {
185                struct_flags: StructTypeFlag(flags),
186                header: MinimalStructHeader {
187                    base_type: TypeIdentifier::None,
188                },
189                member_seq: alloc::vec![],
190            };
191            roundtrip(TypeObject::Minimal(MinimalTypeObject::Struct(st)));
192        }
193    }
194
195    // ------------------------------------------------------------------
196    // Union
197    // ------------------------------------------------------------------
198
199    #[test]
200    fn minimal_union_with_int_discriminator_two_cases() {
201        let u = MinimalUnionType {
202            union_flags: UnionTypeFlag::default(),
203            discriminator: MinimalDiscriminatorMember {
204                common: CommonDiscriminatorMember {
205                    member_flags: UnionDiscriminatorFlag::default(),
206                    type_id: TypeIdentifier::Primitive(PrimitiveKind::Int32),
207                },
208            },
209            member_seq: alloc::vec![
210                MinimalUnionMember {
211                    common: CommonUnionMember {
212                        member_id: 1,
213                        member_flags: UnionMemberFlag::default(),
214                        type_id: TypeIdentifier::Primitive(PrimitiveKind::Int64),
215                        label_seq: alloc::vec![1, 2, 3],
216                    },
217                    detail: name_hash([0xaa, 0xbb, 0xcc, 0xdd]),
218                },
219                MinimalUnionMember {
220                    common: CommonUnionMember {
221                        member_id: 2,
222                        member_flags: UnionMemberFlag(UnionMemberFlag::IS_DEFAULT),
223                        type_id: TypeIdentifier::String8Small { bound: 64 },
224                        label_seq: alloc::vec![],
225                    },
226                    detail: name_hash([0x11, 0x22, 0x33, 0x44]),
227                },
228            ],
229        };
230        roundtrip(TypeObject::Minimal(MinimalTypeObject::Union(u)));
231    }
232
233    // ------------------------------------------------------------------
234    // Enum
235    // ------------------------------------------------------------------
236
237    #[test]
238    fn minimal_enum_color_3_literals() {
239        let e = MinimalEnumeratedType {
240            enum_flags: EnumTypeFlag::default(),
241            header: MinimalEnumeratedHeader {
242                common: CommonEnumeratedHeader { bit_bound: 32 },
243            },
244            literal_seq: alloc::vec![
245                MinimalEnumeratedLiteral {
246                    common: CommonEnumeratedLiteral {
247                        value: 0,
248                        flags: EnumLiteralFlag(EnumLiteralFlag::IS_DEFAULT_LITERAL),
249                    },
250                    detail: name_hash([0x01, 0x02, 0x03, 0x04]),
251                },
252                MinimalEnumeratedLiteral {
253                    common: CommonEnumeratedLiteral {
254                        value: 1,
255                        flags: EnumLiteralFlag::default(),
256                    },
257                    detail: name_hash([0x05, 0x06, 0x07, 0x08]),
258                },
259                MinimalEnumeratedLiteral {
260                    common: CommonEnumeratedLiteral {
261                        value: 2,
262                        flags: EnumLiteralFlag::default(),
263                    },
264                    detail: name_hash([0x09, 0x0a, 0x0b, 0x0c]),
265                },
266            ],
267        };
268        roundtrip(TypeObject::Minimal(MinimalTypeObject::Enumerated(e)));
269    }
270
271    // ------------------------------------------------------------------
272    // Alias
273    // ------------------------------------------------------------------
274
275    #[test]
276    fn minimal_alias_int64_typedef() {
277        let a = MinimalAliasType {
278            alias_flags: AliasTypeFlag::default(),
279            body: MinimalAliasBody {
280                common: CommonAliasBody {
281                    related_flags: AliasMemberFlag::default(),
282                    related_type: TypeIdentifier::Primitive(PrimitiveKind::Int64),
283                },
284            },
285        };
286        roundtrip(TypeObject::Minimal(MinimalTypeObject::Alias(a)));
287    }
288
289    // ------------------------------------------------------------------
290    // Bitmask
291    // ------------------------------------------------------------------
292
293    #[test]
294    fn minimal_bitmask_3_flags() {
295        use crate::type_object::minimal::{CommonBitflag, MinimalBitflag, MinimalBitmaskType};
296        let b = MinimalBitmaskType {
297            bitmask_flags: BitmaskTypeFlag::default(),
298            bit_bound: 32,
299            flag_seq: alloc::vec![
300                MinimalBitflag {
301                    common: CommonBitflag {
302                        position: 0,
303                        flags: crate::type_object::flags::BitflagFlag::default(),
304                    },
305                    detail: name_hash([0x10; 4]),
306                },
307                MinimalBitflag {
308                    common: CommonBitflag {
309                        position: 1,
310                        flags: crate::type_object::flags::BitflagFlag::default(),
311                    },
312                    detail: name_hash([0x20; 4]),
313                },
314                MinimalBitflag {
315                    common: CommonBitflag {
316                        position: 2,
317                        flags: crate::type_object::flags::BitflagFlag::default(),
318                    },
319                    detail: name_hash([0x30; 4]),
320                },
321            ],
322        };
323        roundtrip(TypeObject::Minimal(MinimalTypeObject::Bitmask(b)));
324    }
325
326    // ------------------------------------------------------------------
327    // Sequence / Array / Map
328    // ------------------------------------------------------------------
329
330    #[test]
331    fn minimal_sequence_of_floats() {
332        let s = MinimalSequenceType {
333            collection_flag: CollectionTypeFlag::default(),
334            bound: 100,
335            element: MinimalCollectionElement {
336                common: CommonCollectionElement {
337                    element_flags: CollectionElementFlag::default(),
338                    type_id: TypeIdentifier::Primitive(PrimitiveKind::Float64),
339                },
340            },
341        };
342        roundtrip(TypeObject::Minimal(MinimalTypeObject::Sequence(s)));
343    }
344
345    #[test]
346    fn minimal_array_3d_doubles() {
347        let a = MinimalArrayType {
348            collection_flag: CollectionTypeFlag::default(),
349            bound_seq: alloc::vec![4, 4, 4],
350            element: MinimalCollectionElement {
351                common: CommonCollectionElement {
352                    element_flags: CollectionElementFlag::default(),
353                    type_id: TypeIdentifier::Primitive(PrimitiveKind::Float64),
354                },
355            },
356        };
357        roundtrip(TypeObject::Minimal(MinimalTypeObject::Array(a)));
358    }
359
360    #[test]
361    fn minimal_map_string_to_int64() {
362        let m = MinimalMapType {
363            collection_flag: CollectionTypeFlag::default(),
364            bound: 1_000,
365            key: MinimalCollectionElement {
366                common: CommonCollectionElement {
367                    element_flags: CollectionElementFlag::default(),
368                    type_id: TypeIdentifier::String8Small { bound: 64 },
369                },
370            },
371            element: MinimalCollectionElement {
372                common: CommonCollectionElement {
373                    element_flags: CollectionElementFlag::default(),
374                    type_id: TypeIdentifier::Primitive(PrimitiveKind::Int64),
375                },
376            },
377        };
378        roundtrip(TypeObject::Minimal(MinimalTypeObject::Map(m)));
379    }
380
381    // ------------------------------------------------------------------
382    // Annotation
383    // ------------------------------------------------------------------
384
385    #[test]
386    fn minimal_annotation_with_named_parameter() {
387        use crate::type_object::flags::{AnnotationParameterFlag, AnnotationTypeFlag};
388        use crate::type_object::minimal::{
389            AnnotationParameterValue, CommonAnnotationParameter, MinimalAnnotationParameter,
390            MinimalAnnotationType,
391        };
392        let a = MinimalAnnotationType {
393            annotation_flag: AnnotationTypeFlag::default(),
394            member_seq: alloc::vec![MinimalAnnotationParameter {
395                common: CommonAnnotationParameter {
396                    member_id: 1,
397                    member_flags: AnnotationParameterFlag::default(),
398                    member_type_id: TypeIdentifier::Primitive(PrimitiveKind::Int32),
399                },
400                name: alloc::string::String::from("max"),
401                default_value: AnnotationParameterValue {
402                    raw: alloc::vec![0, 0, 0, 10],
403                },
404            }],
405        };
406        roundtrip(TypeObject::Minimal(MinimalTypeObject::Annotation(a)));
407    }
408
409    // ------------------------------------------------------------------
410    // Bitset
411    // ------------------------------------------------------------------
412
413    #[test]
414    fn minimal_bitset_roundtrips() {
415        use crate::type_object::flags::{BitfieldFlag, BitsetTypeFlag};
416        use crate::type_object::minimal::{CommonBitfield, MinimalBitfield, MinimalBitsetType};
417        let b = MinimalBitsetType {
418            bitset_flags: BitsetTypeFlag::default(),
419            field_seq: alloc::vec![MinimalBitfield {
420                common: CommonBitfield {
421                    position: 0,
422                    flags: BitfieldFlag::default(),
423                    bitcount: 3,
424                    holder_type: 0x07, // TK_UINT32
425                },
426                name_hash: name_hash([0xab; 4]),
427            }],
428        };
429        roundtrip(TypeObject::Minimal(MinimalTypeObject::Bitset(b)));
430    }
431
432    // ------------------------------------------------------------------
433    // Complete — T3
434    // ------------------------------------------------------------------
435
436    #[test]
437    fn complete_struct_sensor_with_names_and_annotations() {
438        use crate::type_object::common::{
439            AppliedAnnotation, AppliedAnnotationParameter, AppliedBuiltinMemberAnnotations,
440            AppliedBuiltinTypeAnnotations, AppliedVerbatimAnnotation, CompleteMemberDetail,
441            CompleteTypeDetail, OptionalAppliedAnnotationSeq, VerbatimPlacement,
442        };
443        use crate::type_object::complete::{
444            CompleteStructHeader, CompleteStructMember, CompleteStructType,
445        };
446        let st = CompleteStructType {
447            struct_flags: StructTypeFlag(StructTypeFlag::IS_MUTABLE),
448            header: CompleteStructHeader {
449                base_type: TypeIdentifier::None,
450                detail: CompleteTypeDetail {
451                    ann_builtin: AppliedBuiltinTypeAnnotations {
452                        verbatim: Some(AppliedVerbatimAnnotation {
453                            placement: VerbatimPlacement::Before,
454                            language: alloc::string::String::from("c++"),
455                            text: alloc::string::String::from("// hand-written"),
456                        }),
457                    },
458                    ann_custom: OptionalAppliedAnnotationSeq(Some(alloc::vec![
459                        AppliedAnnotation {
460                            annotation_typeid: TypeIdentifier::EquivalenceHashComplete(
461                                crate::type_identifier::EquivalenceHash([0xA0; 14]),
462                            ),
463                            param_seq: alloc::vec![AppliedAnnotationParameter {
464                                paramname_hash: name_hash([0x01; 4]),
465                                value: alloc::vec![0, 0, 0, 42],
466                            }],
467                        },
468                    ])),
469                    type_name: alloc::string::String::from("::sensors::Chatter"),
470                },
471            },
472            member_seq: alloc::vec![CompleteStructMember {
473                common: CommonStructMember {
474                    member_id: 1,
475                    member_flags: StructMemberFlag(StructMemberFlag::IS_KEY),
476                    member_type_id: TypeIdentifier::Primitive(PrimitiveKind::Int64),
477                },
478                detail: CompleteMemberDetail {
479                    name: alloc::string::String::from("sensor_id"),
480                    ann_builtin: AppliedBuiltinMemberAnnotations {
481                        unit: Some(alloc::string::String::from("celsius")),
482                        min: Some(alloc::vec![0, 0, 0, 0]),
483                        max: Some(alloc::vec![0xff, 0xff, 0xff, 0xff]),
484                        hash_id: None,
485                        default_value: None,
486                    },
487                    ann_custom: OptionalAppliedAnnotationSeq::default(),
488                },
489            }],
490        };
491        roundtrip(TypeObject::Complete(CompleteTypeObject::Struct(st)));
492    }
493
494    #[test]
495    fn complete_union_roundtrips() {
496        use crate::type_object::common::{
497            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
498            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
499        };
500        use crate::type_object::complete::{
501            CompleteDiscriminatorMember, CompleteUnionHeader, CompleteUnionMember,
502            CompleteUnionType,
503        };
504        let u = CompleteUnionType {
505            union_flags: UnionTypeFlag::default(),
506            header: CompleteUnionHeader {
507                detail: CompleteTypeDetail {
508                    ann_builtin: AppliedBuiltinTypeAnnotations::default(),
509                    ann_custom: OptionalAppliedAnnotationSeq::default(),
510                    type_name: alloc::string::String::from("::MyUnion"),
511                },
512            },
513            discriminator: CompleteDiscriminatorMember {
514                common: CommonDiscriminatorMember {
515                    member_flags: UnionDiscriminatorFlag::default(),
516                    type_id: TypeIdentifier::Primitive(PrimitiveKind::Int32),
517                },
518                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
519                ann_custom: OptionalAppliedAnnotationSeq::default(),
520            },
521            member_seq: alloc::vec![CompleteUnionMember {
522                common: CommonUnionMember {
523                    member_id: 1,
524                    member_flags: UnionMemberFlag::default(),
525                    type_id: TypeIdentifier::Primitive(PrimitiveKind::Int64),
526                    label_seq: alloc::vec![42],
527                },
528                detail: CompleteMemberDetail {
529                    name: alloc::string::String::from("value"),
530                    ann_builtin: AppliedBuiltinMemberAnnotations::default(),
531                    ann_custom: OptionalAppliedAnnotationSeq::default(),
532                },
533            }],
534        };
535        roundtrip(TypeObject::Complete(CompleteTypeObject::Union(u)));
536    }
537
538    #[test]
539    fn complete_enum_with_literal_names() {
540        use crate::type_object::common::{
541            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
542            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
543        };
544        use crate::type_object::complete::{
545            CompleteEnumeratedHeader, CompleteEnumeratedLiteral, CompleteEnumeratedType,
546        };
547        let e = CompleteEnumeratedType {
548            enum_flags: EnumTypeFlag::default(),
549            header: CompleteEnumeratedHeader {
550                common: CommonEnumeratedHeader { bit_bound: 32 },
551                detail: CompleteTypeDetail {
552                    ann_builtin: AppliedBuiltinTypeAnnotations::default(),
553                    ann_custom: OptionalAppliedAnnotationSeq::default(),
554                    type_name: alloc::string::String::from("::Color"),
555                },
556            },
557            literal_seq: alloc::vec![
558                CompleteEnumeratedLiteral {
559                    common: CommonEnumeratedLiteral {
560                        value: 0,
561                        flags: EnumLiteralFlag(EnumLiteralFlag::IS_DEFAULT_LITERAL),
562                    },
563                    detail: CompleteMemberDetail {
564                        name: alloc::string::String::from("RED"),
565                        ann_builtin: AppliedBuiltinMemberAnnotations::default(),
566                        ann_custom: OptionalAppliedAnnotationSeq::default(),
567                    },
568                },
569                CompleteEnumeratedLiteral {
570                    common: CommonEnumeratedLiteral {
571                        value: 1,
572                        flags: EnumLiteralFlag::default(),
573                    },
574                    detail: CompleteMemberDetail {
575                        name: alloc::string::String::from("GREEN"),
576                        ann_builtin: AppliedBuiltinMemberAnnotations::default(),
577                        ann_custom: OptionalAppliedAnnotationSeq::default(),
578                    },
579                },
580            ],
581        };
582        roundtrip(TypeObject::Complete(CompleteTypeObject::Enumerated(e)));
583    }
584
585    #[test]
586    fn complete_alias_typedef() {
587        use crate::type_object::common::{
588            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteTypeDetail,
589            OptionalAppliedAnnotationSeq,
590        };
591        use crate::type_object::complete::{
592            CompleteAliasBody, CompleteAliasHeader, CompleteAliasType,
593        };
594        let a = CompleteAliasType {
595            alias_flags: AliasTypeFlag::default(),
596            header: CompleteAliasHeader {
597                detail: CompleteTypeDetail {
598                    ann_builtin: AppliedBuiltinTypeAnnotations::default(),
599                    ann_custom: OptionalAppliedAnnotationSeq::default(),
600                    type_name: alloc::string::String::from("::Count"),
601                },
602            },
603            body: CompleteAliasBody {
604                related_flags: AliasMemberFlag::default(),
605                related_type: TypeIdentifier::Primitive(PrimitiveKind::UInt64),
606                ann_builtin: AppliedBuiltinMemberAnnotations::default(),
607                ann_custom: OptionalAppliedAnnotationSeq::default(),
608            },
609        };
610        roundtrip(TypeObject::Complete(CompleteTypeObject::Alias(a)));
611    }
612
613    #[test]
614    fn complete_array_3d_roundtrips() {
615        use crate::type_object::common::{
616            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteTypeDetail,
617            OptionalAppliedAnnotationSeq,
618        };
619        use crate::type_object::complete::{CompleteArrayType, CompleteCollectionElement};
620        let a = CompleteArrayType {
621            collection_flag: CollectionTypeFlag::default(),
622            bound_seq: alloc::vec![3, 4, 5],
623            detail: CompleteTypeDetail {
624                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
625                ann_custom: OptionalAppliedAnnotationSeq::default(),
626                type_name: alloc::string::String::from("::Cube"),
627            },
628            element: CompleteCollectionElement {
629                common: CommonCollectionElement {
630                    element_flags: CollectionElementFlag::default(),
631                    type_id: TypeIdentifier::Primitive(PrimitiveKind::Int32),
632                },
633                ann_builtin: AppliedBuiltinMemberAnnotations::default(),
634                ann_custom: OptionalAppliedAnnotationSeq::default(),
635            },
636        };
637        roundtrip(TypeObject::Complete(CompleteTypeObject::Array(a)));
638    }
639
640    #[test]
641    fn complete_map_roundtrips() {
642        use crate::type_object::common::{
643            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteTypeDetail,
644            OptionalAppliedAnnotationSeq,
645        };
646        use crate::type_object::complete::{CompleteCollectionElement, CompleteMapType};
647        let m = CompleteMapType {
648            collection_flag: CollectionTypeFlag::default(),
649            bound: 200,
650            detail: CompleteTypeDetail {
651                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
652                ann_custom: OptionalAppliedAnnotationSeq::default(),
653                type_name: alloc::string::String::from("::StrIntMap"),
654            },
655            key: CompleteCollectionElement {
656                common: CommonCollectionElement {
657                    element_flags: CollectionElementFlag::default(),
658                    type_id: TypeIdentifier::String8Small { bound: 32 },
659                },
660                ann_builtin: AppliedBuiltinMemberAnnotations::default(),
661                ann_custom: OptionalAppliedAnnotationSeq::default(),
662            },
663            element: CompleteCollectionElement {
664                common: CommonCollectionElement {
665                    element_flags: CollectionElementFlag::default(),
666                    type_id: TypeIdentifier::Primitive(PrimitiveKind::Int64),
667                },
668                ann_builtin: AppliedBuiltinMemberAnnotations::default(),
669                ann_custom: OptionalAppliedAnnotationSeq::default(),
670            },
671        };
672        roundtrip(TypeObject::Complete(CompleteTypeObject::Map(m)));
673    }
674
675    #[test]
676    fn complete_bitmask_roundtrips() {
677        use crate::type_object::common::{
678            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
679            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
680        };
681        use crate::type_object::complete::{CompleteBitflag, CompleteBitmaskType};
682        use crate::type_object::flags::BitflagFlag;
683        let b = CompleteBitmaskType {
684            bitmask_flags: BitmaskTypeFlag::default(),
685            bit_bound: 32,
686            detail: CompleteTypeDetail {
687                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
688                ann_custom: OptionalAppliedAnnotationSeq::default(),
689                type_name: alloc::string::String::from("::Perms"),
690            },
691            flag_seq: alloc::vec![
692                CompleteBitflag {
693                    common: crate::type_object::minimal::CommonBitflag {
694                        position: 0,
695                        flags: BitflagFlag::default(),
696                    },
697                    detail: CompleteMemberDetail {
698                        name: alloc::string::String::from("READ"),
699                        ann_builtin: AppliedBuiltinMemberAnnotations::default(),
700                        ann_custom: OptionalAppliedAnnotationSeq::default(),
701                    },
702                },
703                CompleteBitflag {
704                    common: crate::type_object::minimal::CommonBitflag {
705                        position: 1,
706                        flags: BitflagFlag::default(),
707                    },
708                    detail: CompleteMemberDetail {
709                        name: alloc::string::String::from("WRITE"),
710                        ann_builtin: AppliedBuiltinMemberAnnotations::default(),
711                        ann_custom: OptionalAppliedAnnotationSeq::default(),
712                    },
713                },
714            ],
715        };
716        roundtrip(TypeObject::Complete(CompleteTypeObject::Bitmask(b)));
717    }
718
719    #[test]
720    fn complete_bitset_roundtrips() {
721        use crate::type_object::common::{
722            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
723            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
724        };
725        use crate::type_object::complete::{CompleteBitfield, CompleteBitsetType};
726        use crate::type_object::flags::{BitfieldFlag, BitsetTypeFlag};
727        let b = CompleteBitsetType {
728            bitset_flags: BitsetTypeFlag::default(),
729            detail: CompleteTypeDetail {
730                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
731                ann_custom: OptionalAppliedAnnotationSeq::default(),
732                type_name: alloc::string::String::from("::Packed"),
733            },
734            field_seq: alloc::vec![CompleteBitfield {
735                common: crate::type_object::minimal::CommonBitfield {
736                    position: 0,
737                    flags: BitfieldFlag::default(),
738                    bitcount: 3,
739                    holder_type: 0x07,
740                },
741                detail: CompleteMemberDetail {
742                    name: alloc::string::String::from("a"),
743                    ann_builtin: AppliedBuiltinMemberAnnotations::default(),
744                    ann_custom: OptionalAppliedAnnotationSeq::default(),
745                },
746            }],
747        };
748        roundtrip(TypeObject::Complete(CompleteTypeObject::Bitset(b)));
749    }
750
751    #[test]
752    fn complete_annotation_roundtrips() {
753        use crate::type_object::common::{
754            AppliedBuiltinTypeAnnotations, CompleteTypeDetail, OptionalAppliedAnnotationSeq,
755        };
756        use crate::type_object::complete::{CompleteAnnotationParameter, CompleteAnnotationType};
757        use crate::type_object::flags::{AnnotationParameterFlag, AnnotationTypeFlag};
758        let a = CompleteAnnotationType {
759            annotation_flag: AnnotationTypeFlag::default(),
760            detail: CompleteTypeDetail {
761                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
762                ann_custom: OptionalAppliedAnnotationSeq::default(),
763                type_name: alloc::string::String::from("::my_ann"),
764            },
765            member_seq: alloc::vec![CompleteAnnotationParameter {
766                member_id: 1,
767                member_flags: AnnotationParameterFlag::default(),
768                member_type_id: TypeIdentifier::Primitive(PrimitiveKind::Int32),
769                name: alloc::string::String::from("max"),
770                default_value: alloc::vec![0, 0, 0, 99],
771            }],
772        };
773        roundtrip(TypeObject::Complete(CompleteTypeObject::Annotation(a)));
774    }
775
776    #[test]
777    fn complete_verbatim_placement_variants_roundtrip() {
778        use crate::type_object::common::{
779            AppliedBuiltinTypeAnnotations, AppliedVerbatimAnnotation, CompleteTypeDetail,
780            OptionalAppliedAnnotationSeq, VerbatimPlacement,
781        };
782        use crate::type_object::complete::{CompleteStructHeader, CompleteStructType};
783        for placement in [
784            VerbatimPlacement::Before,
785            VerbatimPlacement::After,
786            VerbatimPlacement::BeginFile,
787            VerbatimPlacement::EndFile,
788            VerbatimPlacement::Other(alloc::string::String::from("CUSTOM")),
789        ] {
790            let st = CompleteStructType {
791                struct_flags: StructTypeFlag::empty(),
792                header: CompleteStructHeader {
793                    base_type: TypeIdentifier::None,
794                    detail: CompleteTypeDetail {
795                        ann_builtin: AppliedBuiltinTypeAnnotations {
796                            verbatim: Some(AppliedVerbatimAnnotation {
797                                placement: placement.clone(),
798                                language: alloc::string::String::from("rust"),
799                                text: alloc::string::String::from("// hand written"),
800                            }),
801                        },
802                        ann_custom: OptionalAppliedAnnotationSeq::default(),
803                        type_name: alloc::string::String::from("::X"),
804                    },
805                },
806                member_seq: alloc::vec![],
807            };
808            roundtrip(TypeObject::Complete(CompleteTypeObject::Struct(st)));
809        }
810    }
811
812    #[test]
813    fn complete_sequence_of_floats() {
814        use crate::type_object::common::{
815            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteTypeDetail,
816            OptionalAppliedAnnotationSeq,
817        };
818        use crate::type_object::complete::{CompleteCollectionElement, CompleteSequenceType};
819        let s = CompleteSequenceType {
820            collection_flag: CollectionTypeFlag::default(),
821            bound: 128,
822            detail: CompleteTypeDetail {
823                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
824                ann_custom: OptionalAppliedAnnotationSeq::default(),
825                type_name: alloc::string::String::from("::Floats"),
826            },
827            element: CompleteCollectionElement {
828                common: CommonCollectionElement {
829                    element_flags: CollectionElementFlag::default(),
830                    type_id: TypeIdentifier::Primitive(PrimitiveKind::Float64),
831                },
832                ann_builtin: AppliedBuiltinMemberAnnotations::default(),
833                ann_custom: OptionalAppliedAnnotationSeq::default(),
834            },
835        };
836        roundtrip(TypeObject::Complete(CompleteTypeObject::Sequence(s)));
837    }
838
839    // ------------------------------------------------------------------
840    // Unknown discriminator
841    // ------------------------------------------------------------------
842
843    #[test]
844    fn unknown_equivalence_kind_fails_gracefully() {
845        let bytes = [0xAB_u8];
846        let err = TypeObject::from_bytes_le(&bytes).unwrap_err();
847        assert!(matches!(
848            err,
849            TypeCodecError::UnknownTypeKind { kind: 0xAB }
850        ));
851    }
852}