Skip to main content

zerodds_types/
builder.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Programmatic builder for TypeObjects.
4//!
5//! Enables readable code like:
6//!
7//! ```no_run
8//! use zerodds_types::builder::{Extensibility, TypeObjectBuilder};
9//! use zerodds_types::{PrimitiveKind, TypeIdentifier};
10//!
11//! let chatter = TypeObjectBuilder::struct_type("::chat::Chatter")
12//!     .extensibility(Extensibility::Appendable)
13//!     .member(
14//!         "msg_id",
15//!         TypeIdentifier::Primitive(PrimitiveKind::Int64),
16//!         |m| m.key(),
17//!     )
18//!     .member(
19//!         "text",
20//!         TypeIdentifier::String8Small { bound: 255 },
21//!         |m| m,
22//!     )
23//!     .build_complete();
24//! ```
25//!
26//! Scope in T5: StructBuilder, EnumBuilder, AliasBuilder — the three
27//! most frequently used top-level types. Union, collections,
28//! bitmask, bitset, annotation follow on demand.
29
30use alloc::string::String;
31use alloc::vec::Vec;
32
33use crate::type_identifier::TypeIdentifier;
34use crate::type_object::common::{
35    AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CommonStructMember,
36    CompleteMemberDetail, CompleteTypeDetail, NameHash, OptionalAppliedAnnotationSeq,
37};
38use crate::type_object::complete::{
39    CompleteAliasBody, CompleteAliasHeader, CompleteAliasType, CompleteEnumeratedHeader,
40    CompleteEnumeratedLiteral, CompleteEnumeratedType, CompleteStructHeader, CompleteStructMember,
41    CompleteStructType,
42};
43use crate::type_object::flags::{
44    AliasMemberFlag, AliasTypeFlag, EnumLiteralFlag, EnumTypeFlag, StructMemberFlag, StructTypeFlag,
45};
46use crate::type_object::minimal::CommonAliasBody;
47use crate::type_object::minimal::{
48    CommonEnumeratedHeader, CommonEnumeratedLiteral, MinimalAliasBody, MinimalAliasType,
49    MinimalEnumeratedHeader, MinimalEnumeratedLiteral, MinimalEnumeratedType, MinimalStructHeader,
50    MinimalStructMember, MinimalStructType,
51};
52
53/// Extensibility kind (§7.2.2.4). Simpler representation than the
54/// flag bits: exactly one of three values.
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum Extensibility {
57    /// `@final` — the type cannot be extended.
58    Final,
59    /// `@appendable` — new fields at the end (default).
60    Appendable,
61    /// `@mutable` — each field with an explicit @id; arbitrary evolution.
62    Mutable,
63}
64
65impl Default for Extensibility {
66    fn default() -> Self {
67        Self::Appendable
68    }
69}
70
71impl Extensibility {
72    const fn to_flag_bits(self) -> u16 {
73        match self {
74            Self::Final => StructTypeFlag::IS_FINAL,
75            Self::Appendable => StructTypeFlag::IS_APPENDABLE,
76            Self::Mutable => StructTypeFlag::IS_MUTABLE,
77        }
78    }
79}
80
81/// Einstiegspunkt.
82pub struct TypeObjectBuilder;
83
84impl TypeObjectBuilder {
85    /// Starts a struct builder with the given qualified
86    /// name (e.g. "::sensors::Chatter").
87    #[must_use]
88    pub fn struct_type(name: impl Into<String>) -> StructBuilder {
89        StructBuilder {
90            name: name.into(),
91            extensibility: Extensibility::default(),
92            nested: false,
93            autoid_hash: false,
94            base_type: TypeIdentifier::None,
95            members: Vec::new(),
96        }
97    }
98
99    /// Starts an enum builder.
100    #[must_use]
101    pub fn enum_type(name: impl Into<String>) -> EnumBuilder {
102        EnumBuilder {
103            name: name.into(),
104            bit_bound: 32,
105            literals: Vec::new(),
106        }
107    }
108
109    /// Starts an alias builder.
110    #[must_use]
111    pub fn alias(name: impl Into<String>, target: TypeIdentifier) -> AliasBuilder {
112        AliasBuilder {
113            name: name.into(),
114            related_type: target,
115        }
116    }
117}
118
119// ============================================================================
120// Struct
121// ============================================================================
122
123/// Builder for struct types.
124pub struct StructBuilder {
125    name: String,
126    extensibility: Extensibility,
127    nested: bool,
128    autoid_hash: bool,
129    base_type: TypeIdentifier,
130    members: Vec<StructMemberSpec>,
131}
132
133/// Inner state of a struct member — set via [`StructMemberBuilder`].
134pub struct StructMemberSpec {
135    name: String,
136    type_id: TypeIdentifier,
137    explicit_id: Option<u32>,
138    flags: u16,
139    unit: Option<String>,
140    min: Option<Vec<u8>>,
141    max: Option<Vec<u8>>,
142    hash_id: Option<String>,
143    default_value: Option<String>,
144}
145
146/// Fluent builder for member attributes.
147pub struct StructMemberBuilder<'a> {
148    spec: &'a mut StructMemberSpec,
149}
150
151impl StructMemberBuilder<'_> {
152    /// Marks a member as `@key`.
153    #[must_use]
154    pub fn key(self) -> Self {
155        self.spec.flags |= StructMemberFlag::IS_KEY;
156        self
157    }
158
159    /// `@optional`.
160    #[must_use]
161    pub fn optional(self) -> Self {
162        self.spec.flags |= StructMemberFlag::IS_OPTIONAL;
163        self
164    }
165
166    /// `@must_understand`.
167    #[must_use]
168    pub fn must_understand(self) -> Self {
169        self.spec.flags |= StructMemberFlag::IS_MUST_UNDERSTAND;
170        self
171    }
172
173    /// `@external` — indirect storage.
174    #[must_use]
175    pub fn external(self) -> Self {
176        self.spec.flags |= StructMemberFlag::IS_EXTERNAL;
177        self
178    }
179
180    /// Explizite `@id(n)`.
181    #[must_use]
182    pub fn id(self, id: u32) -> Self {
183        self.spec.explicit_id = Some(id);
184        self
185    }
186
187    /// `@unit("...")` — nur in Complete sichtbar.
188    #[must_use]
189    pub fn unit(self, unit: impl Into<String>) -> Self {
190        self.spec.unit = Some(unit.into());
191        self
192    }
193
194    /// `@min(val)` — opaque bytes.
195    #[must_use]
196    pub fn min_bytes(self, min: Vec<u8>) -> Self {
197        self.spec.min = Some(min);
198        self
199    }
200
201    /// `@max(val)` — opaque bytes.
202    #[must_use]
203    pub fn max_bytes(self, max: Vec<u8>) -> Self {
204        self.spec.max = Some(max);
205        self
206    }
207
208    /// `@hashid("name")`.
209    #[must_use]
210    pub fn hash_id(self, name: impl Into<String>) -> Self {
211        self.spec.hash_id = Some(name.into());
212        self
213    }
214
215    /// `@default(val)` — XTypes 1.3 §7.2.4.4.4.4.9. The value as a string
216    /// (encoder-converted on wire encode). Carried in the complete
217    /// TypeObject via `AppliedBuiltinMemberAnnotations.default_value`.
218    #[must_use]
219    pub fn set_member_default(self, value: impl Into<String>) -> Self {
220        self.spec.default_value = Some(value.into());
221        self
222    }
223}
224
225impl StructBuilder {
226    /// Sets the extensibility (default: Appendable).
227    #[must_use]
228    pub fn extensibility(mut self, ext: Extensibility) -> Self {
229        self.extensibility = ext;
230        self
231    }
232
233    /// Marks as `@nested`.
234    #[must_use]
235    pub fn nested(mut self) -> Self {
236        self.nested = true;
237        self
238    }
239
240    /// Aktiviert `@autoid(HASH)`.
241    #[must_use]
242    pub fn autoid_hash(mut self) -> Self {
243        self.autoid_hash = true;
244        self
245    }
246
247    /// Sets a base type for inheritance.
248    #[must_use]
249    pub fn base(mut self, base: TypeIdentifier) -> Self {
250        self.base_type = base;
251        self
252    }
253
254    /// Adds a member.
255    ///
256    /// The callback receives a `StructMemberBuilder` to set flags
257    /// and annotations.
258    #[must_use]
259    pub fn member<F>(mut self, name: impl Into<String>, ty: TypeIdentifier, f: F) -> Self
260    where
261        F: FnOnce(StructMemberBuilder<'_>) -> StructMemberBuilder<'_>,
262    {
263        let mut spec = StructMemberSpec {
264            name: name.into(),
265            type_id: ty,
266            explicit_id: None,
267            flags: 0,
268            unit: None,
269            min: None,
270            max: None,
271            hash_id: None,
272            default_value: None,
273        };
274        let _ = f(StructMemberBuilder { spec: &mut spec });
275        self.members.push(spec);
276        self
277    }
278
279    fn struct_flags(&self) -> StructTypeFlag {
280        let mut bits = self.extensibility.to_flag_bits();
281        if self.nested {
282            bits |= StructTypeFlag::IS_NESTED;
283        }
284        if self.autoid_hash {
285            bits |= StructTypeFlag::IS_AUTOID_HASH;
286        }
287        StructTypeFlag(bits)
288    }
289
290    /// Member-ID assignment:
291    /// - explicit_id, if set
292    /// - otherwise autoid-hash (first 4 bytes SHA-256 over the name — simplified)
293    /// - otherwise sequential from 1
294    fn resolve_member_ids(&self) -> Vec<u32> {
295        let mut ids = Vec::with_capacity(self.members.len());
296        let mut next_seq: u32 = 1;
297        for spec in &self.members {
298            let id = if let Some(explicit) = spec.explicit_id {
299                explicit
300            } else if self.autoid_hash {
301                // XTypes §7.2.2.4.9 + §7.3.1.2.1.1: for `@autoid(HASH)`
302                // the member ID is derived from bits [4..28) (=24 bits) of the
303                // first 4 MD5 bytes of the member name. Bits [0..4)
304                // are reserved (E-flag etc.) and not used.
305                let nh = NameHash::from_name(&spec.name);
306                (u32::from_le_bytes(nh.0) >> 4) & 0x00FF_FFFF
307            } else {
308                let v = next_seq;
309                next_seq += 1;
310                v
311            };
312            ids.push(id);
313        }
314        ids
315    }
316
317    fn member_common(spec: &StructMemberSpec, id: u32) -> CommonStructMember {
318        CommonStructMember {
319            member_id: id,
320            member_flags: StructMemberFlag(spec.flags),
321            member_type_id: spec.type_id.clone(),
322        }
323    }
324
325    fn member_detail(spec: &StructMemberSpec) -> CompleteMemberDetail {
326        CompleteMemberDetail {
327            name: spec.name.clone(),
328            ann_builtin: AppliedBuiltinMemberAnnotations {
329                unit: spec.unit.clone(),
330                min: spec.min.clone(),
331                max: spec.max.clone(),
332                hash_id: spec.hash_id.clone(),
333                default_value: spec.default_value.clone(),
334            },
335            ann_custom: OptionalAppliedAnnotationSeq::default(),
336        }
337    }
338
339    /// Builds a `MinimalStructType`.
340    #[must_use]
341    pub fn build_minimal(&self) -> MinimalStructType {
342        let ids = self.resolve_member_ids();
343        let member_seq = self
344            .members
345            .iter()
346            .zip(ids.iter())
347            .map(|(spec, id)| MinimalStructMember {
348                common: Self::member_common(spec, *id),
349                detail: NameHash::from_name(&spec.name),
350            })
351            .collect();
352        MinimalStructType {
353            struct_flags: self.struct_flags(),
354            header: MinimalStructHeader {
355                base_type: self.base_type.clone(),
356            },
357            member_seq,
358        }
359    }
360
361    /// Builds a `CompleteStructType`.
362    #[must_use]
363    pub fn build_complete(&self) -> CompleteStructType {
364        let ids = self.resolve_member_ids();
365        let member_seq = self
366            .members
367            .iter()
368            .zip(ids.iter())
369            .map(|(spec, id)| CompleteStructMember {
370                common: Self::member_common(spec, *id),
371                detail: Self::member_detail(spec),
372            })
373            .collect();
374        CompleteStructType {
375            struct_flags: self.struct_flags(),
376            header: CompleteStructHeader {
377                base_type: self.base_type.clone(),
378                detail: CompleteTypeDetail {
379                    ann_builtin: AppliedBuiltinTypeAnnotations::default(),
380                    ann_custom: OptionalAppliedAnnotationSeq::default(),
381                    type_name: self.name.clone(),
382                },
383            },
384            member_seq,
385        }
386    }
387}
388
389// ============================================================================
390// Enum
391// ============================================================================
392
393/// Builder for enumerated types.
394pub struct EnumBuilder {
395    name: String,
396    bit_bound: u16,
397    literals: Vec<EnumLiteralSpec>,
398}
399
400struct EnumLiteralSpec {
401    name: String,
402    value: i32,
403    is_default: bool,
404}
405
406impl EnumBuilder {
407    /// Sets the bit width (8/16/32, default 32).
408    #[must_use]
409    pub fn bit_bound(mut self, bits: u16) -> Self {
410        self.bit_bound = bits;
411        self
412    }
413
414    /// Adds a literal.
415    #[must_use]
416    pub fn literal(mut self, name: impl Into<String>, value: i32) -> Self {
417        self.literals.push(EnumLiteralSpec {
418            name: name.into(),
419            value,
420            is_default: false,
421        });
422        self
423    }
424
425    /// Adds the default literal (`@default_literal`).
426    #[must_use]
427    pub fn default_literal(mut self, name: impl Into<String>, value: i32) -> Self {
428        self.literals.push(EnumLiteralSpec {
429            name: name.into(),
430            value,
431            is_default: true,
432        });
433        self
434    }
435
436    /// Builds a `MinimalEnumeratedType`.
437    #[must_use]
438    pub fn build_minimal(&self) -> MinimalEnumeratedType {
439        MinimalEnumeratedType {
440            enum_flags: EnumTypeFlag::default(),
441            header: MinimalEnumeratedHeader {
442                common: CommonEnumeratedHeader {
443                    bit_bound: self.bit_bound,
444                },
445            },
446            literal_seq: self
447                .literals
448                .iter()
449                .map(|l| MinimalEnumeratedLiteral {
450                    common: CommonEnumeratedLiteral {
451                        value: l.value,
452                        flags: EnumLiteralFlag(if l.is_default {
453                            EnumLiteralFlag::IS_DEFAULT_LITERAL
454                        } else {
455                            0
456                        }),
457                    },
458                    detail: NameHash::from_name(&l.name),
459                })
460                .collect(),
461        }
462    }
463
464    /// Builds a `CompleteEnumeratedType`.
465    #[must_use]
466    pub fn build_complete(&self) -> CompleteEnumeratedType {
467        CompleteEnumeratedType {
468            enum_flags: EnumTypeFlag::default(),
469            header: CompleteEnumeratedHeader {
470                common: CommonEnumeratedHeader {
471                    bit_bound: self.bit_bound,
472                },
473                detail: CompleteTypeDetail {
474                    ann_builtin: AppliedBuiltinTypeAnnotations::default(),
475                    ann_custom: OptionalAppliedAnnotationSeq::default(),
476                    type_name: self.name.clone(),
477                },
478            },
479            literal_seq: self
480                .literals
481                .iter()
482                .map(|l| CompleteEnumeratedLiteral {
483                    common: CommonEnumeratedLiteral {
484                        value: l.value,
485                        flags: EnumLiteralFlag(if l.is_default {
486                            EnumLiteralFlag::IS_DEFAULT_LITERAL
487                        } else {
488                            0
489                        }),
490                    },
491                    detail: CompleteMemberDetail {
492                        name: l.name.clone(),
493                        ann_builtin: AppliedBuiltinMemberAnnotations::default(),
494                        ann_custom: OptionalAppliedAnnotationSeq::default(),
495                    },
496                })
497                .collect(),
498        }
499    }
500}
501
502// ============================================================================
503// Alias
504// ============================================================================
505
506/// Builder for alias/typedef.
507pub struct AliasBuilder {
508    name: String,
509    related_type: TypeIdentifier,
510}
511
512impl AliasBuilder {
513    /// Minimal-Alias.
514    #[must_use]
515    pub fn build_minimal(&self) -> MinimalAliasType {
516        MinimalAliasType {
517            alias_flags: AliasTypeFlag::default(),
518            body: MinimalAliasBody {
519                common: CommonAliasBody {
520                    related_flags: AliasMemberFlag::default(),
521                    related_type: self.related_type.clone(),
522                },
523            },
524        }
525    }
526
527    /// Complete-Alias.
528    #[must_use]
529    pub fn build_complete(&self) -> CompleteAliasType {
530        CompleteAliasType {
531            alias_flags: AliasTypeFlag::default(),
532            header: CompleteAliasHeader {
533                detail: CompleteTypeDetail {
534                    ann_builtin: AppliedBuiltinTypeAnnotations::default(),
535                    ann_custom: OptionalAppliedAnnotationSeq::default(),
536                    type_name: self.name.clone(),
537                },
538            },
539            body: CompleteAliasBody {
540                related_flags: AliasMemberFlag::default(),
541                related_type: self.related_type.clone(),
542                ann_builtin: AppliedBuiltinMemberAnnotations::default(),
543                ann_custom: OptionalAppliedAnnotationSeq::default(),
544            },
545        }
546    }
547}
548
549// ============================================================================
550// Union
551// ============================================================================
552
553/// Builder for union types.
554pub struct UnionBuilder {
555    name: String,
556    extensibility: Extensibility,
557    discriminator_type: TypeIdentifier,
558    cases: Vec<UnionCaseSpec>,
559}
560
561struct UnionCaseSpec {
562    name: String,
563    member_id: Option<u32>,
564    type_id: TypeIdentifier,
565    labels: Vec<i32>,
566    is_default: bool,
567}
568
569impl UnionBuilder {
570    /// Sets the extensibility (default Appendable).
571    #[must_use]
572    pub fn extensibility(mut self, ext: Extensibility) -> Self {
573        self.extensibility = ext;
574        self
575    }
576
577    /// Adds a case member.
578    #[must_use]
579    pub fn case(mut self, name: impl Into<String>, ty: TypeIdentifier, labels: Vec<i32>) -> Self {
580        self.cases.push(UnionCaseSpec {
581            name: name.into(),
582            member_id: None,
583            type_id: ty,
584            labels,
585            is_default: false,
586        });
587        self
588    }
589
590    /// Adds the default case.
591    #[must_use]
592    pub fn default_case(mut self, name: impl Into<String>, ty: TypeIdentifier) -> Self {
593        self.cases.push(UnionCaseSpec {
594            name: name.into(),
595            member_id: None,
596            type_id: ty,
597            labels: Vec::new(),
598            is_default: true,
599        });
600        self
601    }
602
603    fn union_flags(&self) -> crate::type_object::flags::UnionTypeFlag {
604        use crate::type_object::flags::UnionTypeFlag;
605        // UnionTypeFlag analogously uses the struct bits for
606        // extensibility (§7.3.4.5) — reuse the bit positions.
607        UnionTypeFlag(match self.extensibility {
608            Extensibility::Final => StructTypeFlag::IS_FINAL,
609            Extensibility::Appendable => StructTypeFlag::IS_APPENDABLE,
610            Extensibility::Mutable => StructTypeFlag::IS_MUTABLE,
611        })
612    }
613
614    fn resolve_case_ids(&self) -> Vec<u32> {
615        let mut ids = Vec::with_capacity(self.cases.len());
616        let mut next: u32 = 1;
617        for c in &self.cases {
618            ids.push(c.member_id.unwrap_or_else(|| {
619                let v = next;
620                next += 1;
621                v
622            }));
623        }
624        ids
625    }
626
627    /// Builds a `MinimalUnionType`.
628    #[must_use]
629    pub fn build_minimal(&self) -> crate::type_object::minimal::MinimalUnionType {
630        use crate::type_object::common::CommonUnionMember;
631        use crate::type_object::flags::{UnionDiscriminatorFlag, UnionMemberFlag};
632        use crate::type_object::minimal::{
633            CommonDiscriminatorMember, MinimalDiscriminatorMember, MinimalUnionMember,
634            MinimalUnionType,
635        };
636        let ids = self.resolve_case_ids();
637        let member_seq = self
638            .cases
639            .iter()
640            .zip(ids.iter())
641            .map(|(c, id)| MinimalUnionMember {
642                common: CommonUnionMember {
643                    member_id: *id,
644                    member_flags: UnionMemberFlag(if c.is_default {
645                        UnionMemberFlag::IS_DEFAULT
646                    } else {
647                        0
648                    }),
649                    type_id: c.type_id.clone(),
650                    label_seq: c.labels.clone(),
651                },
652                detail: NameHash::from_name(&c.name),
653            })
654            .collect();
655        MinimalUnionType {
656            union_flags: self.union_flags(),
657            discriminator: MinimalDiscriminatorMember {
658                common: CommonDiscriminatorMember {
659                    member_flags: UnionDiscriminatorFlag::default(),
660                    type_id: self.discriminator_type.clone(),
661                },
662            },
663            member_seq,
664        }
665    }
666
667    /// Builds a `CompleteUnionType`.
668    #[must_use]
669    pub fn build_complete(&self) -> crate::type_object::complete::CompleteUnionType {
670        use crate::type_object::common::CommonUnionMember;
671        use crate::type_object::complete::{
672            CompleteDiscriminatorMember, CompleteUnionHeader, CompleteUnionMember,
673            CompleteUnionType,
674        };
675        use crate::type_object::flags::{UnionDiscriminatorFlag, UnionMemberFlag};
676        use crate::type_object::minimal::CommonDiscriminatorMember;
677        let ids = self.resolve_case_ids();
678        let member_seq = self
679            .cases
680            .iter()
681            .zip(ids.iter())
682            .map(|(c, id)| CompleteUnionMember {
683                common: CommonUnionMember {
684                    member_id: *id,
685                    member_flags: UnionMemberFlag(if c.is_default {
686                        UnionMemberFlag::IS_DEFAULT
687                    } else {
688                        0
689                    }),
690                    type_id: c.type_id.clone(),
691                    label_seq: c.labels.clone(),
692                },
693                detail: CompleteMemberDetail {
694                    name: c.name.clone(),
695                    ann_builtin: AppliedBuiltinMemberAnnotations::default(),
696                    ann_custom: OptionalAppliedAnnotationSeq::default(),
697                },
698            })
699            .collect();
700        CompleteUnionType {
701            union_flags: self.union_flags(),
702            header: CompleteUnionHeader {
703                detail: CompleteTypeDetail {
704                    ann_builtin: AppliedBuiltinTypeAnnotations::default(),
705                    ann_custom: OptionalAppliedAnnotationSeq::default(),
706                    type_name: self.name.clone(),
707                },
708            },
709            discriminator: CompleteDiscriminatorMember {
710                common: CommonDiscriminatorMember {
711                    member_flags: UnionDiscriminatorFlag::default(),
712                    type_id: self.discriminator_type.clone(),
713                },
714                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
715                ann_custom: OptionalAppliedAnnotationSeq::default(),
716            },
717            member_seq,
718        }
719    }
720}
721
722// ============================================================================
723// Sequence / Array / Map
724// ============================================================================
725
726/// Builder for `sequence<T, N>`.
727pub struct SequenceBuilder {
728    element: TypeIdentifier,
729    bound: u32,
730}
731
732impl SequenceBuilder {
733    /// Builds a MinimalSequenceType.
734    #[must_use]
735    pub fn build_minimal(&self) -> crate::type_object::minimal::MinimalSequenceType {
736        use crate::type_object::flags::{CollectionElementFlag, CollectionTypeFlag};
737        use crate::type_object::minimal::{
738            CommonCollectionElement, MinimalCollectionElement, MinimalSequenceType,
739        };
740        MinimalSequenceType {
741            collection_flag: CollectionTypeFlag::default(),
742            bound: self.bound,
743            element: MinimalCollectionElement {
744                common: CommonCollectionElement {
745                    element_flags: CollectionElementFlag::default(),
746                    type_id: self.element.clone(),
747                },
748            },
749        }
750    }
751}
752
753/// Builder for `T[D1, D2, ...]`.
754pub struct ArrayBuilder {
755    element: TypeIdentifier,
756    dimensions: Vec<u32>,
757}
758
759impl ArrayBuilder {
760    /// Builds a MinimalArrayType.
761    #[must_use]
762    pub fn build_minimal(&self) -> crate::type_object::minimal::MinimalArrayType {
763        use crate::type_object::flags::{CollectionElementFlag, CollectionTypeFlag};
764        use crate::type_object::minimal::{
765            CommonCollectionElement, MinimalArrayType, MinimalCollectionElement,
766        };
767        MinimalArrayType {
768            collection_flag: CollectionTypeFlag::default(),
769            bound_seq: self.dimensions.clone(),
770            element: MinimalCollectionElement {
771                common: CommonCollectionElement {
772                    element_flags: CollectionElementFlag::default(),
773                    type_id: self.element.clone(),
774                },
775            },
776        }
777    }
778}
779
780/// Validation error when building collection types.
781#[derive(Debug, Clone, PartialEq, Eq)]
782pub enum BuilderError {
783    /// MUTABLE extensibility is not allowed on maps
784    /// (XTypes 1.3 §7.4.3.5.3 Rules 11-16). The spec allows only FINAL/
785    /// APPENDABLE; MUTABLE has historically been silently treated as
786    /// APPENDABLE. This error prevents the silent demotion.
787    MutableMapExtensibilityNotAllowed,
788}
789
790/// Builder for `map<K, V, N>`.
791pub struct MapBuilder {
792    key: TypeIdentifier,
793    value: TypeIdentifier,
794    bound: u32,
795}
796
797impl MapBuilder {
798    /// Validating builder: allows only FINAL/APPENDABLE extensibility.
799    /// MUTABLE wirft `BuilderError::MutableMapExtensibilityNotAllowed`
800    /// (XTypes 1.3 §7.4.3.5.3 Rules 11-16).
801    ///
802    /// # Errors
803    /// `MutableMapExtensibilityNotAllowed` if `ext == Mutable`.
804    pub fn add_map_member(
805        self,
806        ext: Extensibility,
807    ) -> Result<crate::type_object::minimal::MinimalMapType, BuilderError> {
808        if matches!(ext, Extensibility::Mutable) {
809            return Err(BuilderError::MutableMapExtensibilityNotAllowed);
810        }
811        Ok(self.build_minimal())
812    }
813
814    /// Builds a MinimalMapType.
815    #[must_use]
816    pub fn build_minimal(&self) -> crate::type_object::minimal::MinimalMapType {
817        use crate::type_object::flags::{CollectionElementFlag, CollectionTypeFlag};
818        use crate::type_object::minimal::{
819            CommonCollectionElement, MinimalCollectionElement, MinimalMapType,
820        };
821        let element = MinimalCollectionElement {
822            common: CommonCollectionElement {
823                element_flags: CollectionElementFlag::default(),
824                type_id: self.value.clone(),
825            },
826        };
827        let key = MinimalCollectionElement {
828            common: CommonCollectionElement {
829                element_flags: CollectionElementFlag::default(),
830                type_id: self.key.clone(),
831            },
832        };
833        MinimalMapType {
834            collection_flag: CollectionTypeFlag::default(),
835            bound: self.bound,
836            key,
837            element,
838        }
839    }
840}
841
842// ============================================================================
843// Bitmask / Bitset
844// ============================================================================
845
846/// Builder for `bitmask` types.
847pub struct BitmaskBuilder {
848    name: String,
849    bit_bound: u16,
850    flags: Vec<(String, u16)>,
851}
852
853impl BitmaskBuilder {
854    /// Bit width (default 32).
855    #[must_use]
856    pub fn bit_bound(mut self, bits: u16) -> Self {
857        self.bit_bound = bits;
858        self
859    }
860
861    /// Adds a bit flag.
862    #[must_use]
863    pub fn flag(mut self, name: impl Into<String>, position: u16) -> Self {
864        self.flags.push((name.into(), position));
865        self
866    }
867
868    /// Builds a MinimalBitmaskType.
869    #[must_use]
870    pub fn build_minimal(&self) -> crate::type_object::minimal::MinimalBitmaskType {
871        use crate::type_object::flags::{BitflagFlag, BitmaskTypeFlag};
872        use crate::type_object::minimal::{CommonBitflag, MinimalBitflag, MinimalBitmaskType};
873        MinimalBitmaskType {
874            bitmask_flags: BitmaskTypeFlag::default(),
875            bit_bound: self.bit_bound,
876            flag_seq: self
877                .flags
878                .iter()
879                .map(|(n, p)| MinimalBitflag {
880                    common: CommonBitflag {
881                        position: *p,
882                        flags: BitflagFlag::default(),
883                    },
884                    detail: NameHash::from_name(n),
885                })
886                .collect(),
887        }
888    }
889
890    /// Builds a `CompleteBitmaskType` (with names + annotation placeholders).
891    #[must_use]
892    pub fn build_complete(&self) -> crate::type_object::complete::CompleteBitmaskType {
893        use crate::type_object::common::{
894            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
895            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
896        };
897        use crate::type_object::complete::{CompleteBitflag, CompleteBitmaskType};
898        use crate::type_object::flags::{BitflagFlag, BitmaskTypeFlag};
899        CompleteBitmaskType {
900            bitmask_flags: BitmaskTypeFlag::default(),
901            bit_bound: self.bit_bound,
902            detail: CompleteTypeDetail {
903                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
904                ann_custom: OptionalAppliedAnnotationSeq::default(),
905                type_name: self.name.clone(),
906            },
907            flag_seq: self
908                .flags
909                .iter()
910                .map(|(n, p)| CompleteBitflag {
911                    common: crate::type_object::minimal::CommonBitflag {
912                        position: *p,
913                        flags: BitflagFlag::default(),
914                    },
915                    detail: CompleteMemberDetail {
916                        name: n.clone(),
917                        ann_builtin: AppliedBuiltinMemberAnnotations::default(),
918                        ann_custom: OptionalAppliedAnnotationSeq::default(),
919                    },
920                })
921                .collect(),
922        }
923    }
924
925    /// Getter — mostly used via [`build_complete`].
926    #[must_use]
927    pub fn name(&self) -> &str {
928        &self.name
929    }
930}
931
932/// Builder for `bitset` types.
933pub struct BitsetBuilder {
934    name: String,
935    fields: Vec<BitfieldSpec>,
936}
937
938struct BitfieldSpec {
939    name: String,
940    position: u16,
941    bitcount: u8,
942    holder_type: u8,
943}
944
945impl BitsetBuilder {
946    /// Adds a bitfield.
947    #[must_use]
948    pub fn field(
949        mut self,
950        name: impl Into<String>,
951        position: u16,
952        bitcount: u8,
953        holder_type: u8,
954    ) -> Self {
955        self.fields.push(BitfieldSpec {
956            name: name.into(),
957            position,
958            bitcount,
959            holder_type,
960        });
961        self
962    }
963
964    /// Builds a MinimalBitsetType.
965    #[must_use]
966    pub fn build_minimal(&self) -> crate::type_object::minimal::MinimalBitsetType {
967        use crate::type_object::flags::{BitfieldFlag, BitsetTypeFlag};
968        use crate::type_object::minimal::{CommonBitfield, MinimalBitfield, MinimalBitsetType};
969        MinimalBitsetType {
970            bitset_flags: BitsetTypeFlag::default(),
971            field_seq: self
972                .fields
973                .iter()
974                .map(|f| MinimalBitfield {
975                    common: CommonBitfield {
976                        position: f.position,
977                        flags: BitfieldFlag::default(),
978                        bitcount: f.bitcount,
979                        holder_type: f.holder_type,
980                    },
981                    name_hash: NameHash::from_name(&f.name),
982                })
983                .collect(),
984        }
985    }
986
987    /// Builds a `CompleteBitsetType` (with names + annotation placeholders).
988    #[must_use]
989    pub fn build_complete(&self) -> crate::type_object::complete::CompleteBitsetType {
990        use crate::type_object::common::{
991            AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
992            CompleteTypeDetail, OptionalAppliedAnnotationSeq,
993        };
994        use crate::type_object::complete::{CompleteBitfield, CompleteBitsetType};
995        use crate::type_object::flags::{BitfieldFlag, BitsetTypeFlag};
996        CompleteBitsetType {
997            bitset_flags: BitsetTypeFlag::default(),
998            detail: CompleteTypeDetail {
999                ann_builtin: AppliedBuiltinTypeAnnotations::default(),
1000                ann_custom: OptionalAppliedAnnotationSeq::default(),
1001                type_name: self.name.clone(),
1002            },
1003            field_seq: self
1004                .fields
1005                .iter()
1006                .map(|f| CompleteBitfield {
1007                    common: crate::type_object::minimal::CommonBitfield {
1008                        position: f.position,
1009                        flags: BitfieldFlag::default(),
1010                        bitcount: f.bitcount,
1011                        holder_type: f.holder_type,
1012                    },
1013                    detail: CompleteMemberDetail {
1014                        name: f.name.clone(),
1015                        ann_builtin: AppliedBuiltinMemberAnnotations::default(),
1016                        ann_custom: OptionalAppliedAnnotationSeq::default(),
1017                    },
1018                })
1019                .collect(),
1020        }
1021    }
1022
1023    /// Getter — mostly used via [`build_complete`].
1024    #[must_use]
1025    pub fn name(&self) -> &str {
1026        &self.name
1027    }
1028}
1029
1030// ============================================================================
1031// Entry points for the new builders
1032// ============================================================================
1033
1034impl TypeObjectBuilder {
1035    /// Starts a union builder. `discriminator_type` is typically
1036    /// an enum or an integer primitive.
1037    #[must_use]
1038    pub fn union_type(name: impl Into<String>, discriminator_type: TypeIdentifier) -> UnionBuilder {
1039        UnionBuilder {
1040            name: name.into(),
1041            extensibility: Extensibility::default(),
1042            discriminator_type,
1043            cases: Vec::new(),
1044        }
1045    }
1046
1047    /// Starts a sequence builder. `bound=0` = unbounded.
1048    #[must_use]
1049    pub fn sequence(element: TypeIdentifier, bound: u32) -> SequenceBuilder {
1050        SequenceBuilder { element, bound }
1051    }
1052
1053    /// Starts an array builder with the given list of dimensions.
1054    #[must_use]
1055    pub fn array(element: TypeIdentifier, dimensions: Vec<u32>) -> ArrayBuilder {
1056        ArrayBuilder {
1057            element,
1058            dimensions,
1059        }
1060    }
1061
1062    /// Starts a map builder.
1063    #[must_use]
1064    pub fn map(key: TypeIdentifier, value: TypeIdentifier, bound: u32) -> MapBuilder {
1065        MapBuilder { key, value, bound }
1066    }
1067
1068    /// Starts a bitmask builder.
1069    #[must_use]
1070    pub fn bitmask(name: impl Into<String>) -> BitmaskBuilder {
1071        BitmaskBuilder {
1072            name: name.into(),
1073            bit_bound: 32,
1074            flags: Vec::new(),
1075        }
1076    }
1077
1078    /// Starts a bitset builder.
1079    #[must_use]
1080    pub fn bitset(name: impl Into<String>) -> BitsetBuilder {
1081        BitsetBuilder {
1082            name: name.into(),
1083            fields: Vec::new(),
1084        }
1085    }
1086}
1087
1088// ============================================================================
1089// Tests
1090// ============================================================================
1091
1092#[cfg(test)]
1093#[allow(clippy::unwrap_used)]
1094mod tests {
1095    use super::*;
1096    use crate::type_identifier::PrimitiveKind;
1097    use crate::type_object::flags::StructTypeFlag;
1098
1099    #[test]
1100    fn struct_builder_basic_with_two_members() {
1101        let st = TypeObjectBuilder::struct_type("::chat::Chatter")
1102            .member(
1103                "sensor_id",
1104                TypeIdentifier::Primitive(PrimitiveKind::Int64),
1105                |m| m.key(),
1106            )
1107            .member("text", TypeIdentifier::String8Small { bound: 255 }, |m| m)
1108            .build_minimal();
1109
1110        assert_eq!(st.member_seq.len(), 2);
1111        assert_eq!(st.member_seq[0].common.member_id, 1);
1112        assert!(
1113            st.member_seq[0]
1114                .common
1115                .member_flags
1116                .has(StructMemberFlag::IS_KEY)
1117        );
1118        assert_eq!(st.member_seq[1].common.member_id, 2);
1119        // NameHash deterministisch: MD5("sensor_id")[0..4]
1120        assert_eq!(st.member_seq[0].detail, NameHash::from_name("sensor_id"));
1121        assert_eq!(st.member_seq[1].detail, NameHash::from_name("text"));
1122    }
1123
1124    #[test]
1125    fn struct_builder_explicit_ids_respected() {
1126        let st = TypeObjectBuilder::struct_type("::X")
1127            .member("a", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| {
1128                m.id(100)
1129            })
1130            .member("b", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| {
1131                m.id(200)
1132            })
1133            .build_minimal();
1134        assert_eq!(st.member_seq[0].common.member_id, 100);
1135        assert_eq!(st.member_seq[1].common.member_id, 200);
1136    }
1137
1138    #[test]
1139    fn mutable_map_extensibility_is_error() {
1140        // §7.4.3.5.3 Rules 11-16 — a MUTABLE map is forbidden (silent demotion).
1141        let res = TypeObjectBuilder::map(
1142            TypeIdentifier::Primitive(PrimitiveKind::Int32),
1143            TypeIdentifier::Primitive(PrimitiveKind::Int32),
1144            10,
1145        )
1146        .add_map_member(Extensibility::Mutable);
1147        assert!(matches!(
1148            res,
1149            Err(BuilderError::MutableMapExtensibilityNotAllowed)
1150        ));
1151    }
1152
1153    #[test]
1154    fn appendable_map_extensibility_is_ok() {
1155        let res = TypeObjectBuilder::map(
1156            TypeIdentifier::Primitive(PrimitiveKind::Int32),
1157            TypeIdentifier::Primitive(PrimitiveKind::Int32),
1158            10,
1159        )
1160        .add_map_member(Extensibility::Appendable);
1161        assert!(res.is_ok());
1162    }
1163
1164    #[test]
1165    fn final_map_extensibility_is_ok() {
1166        let res = TypeObjectBuilder::map(
1167            TypeIdentifier::Primitive(PrimitiveKind::Int32),
1168            TypeIdentifier::Primitive(PrimitiveKind::Int32),
1169            10,
1170        )
1171        .add_map_member(Extensibility::Final);
1172        assert!(res.is_ok());
1173    }
1174
1175    #[test]
1176    fn member_with_explicit_default_used_when_field_missing() {
1177        // §7.2.4.4.4.4.9 — `set_member_default` sets
1178        // `AppliedBuiltinMemberAnnotations.default_value`. The test ensures
1179        // that the value passes through the complete pipeline.
1180        let st = TypeObjectBuilder::struct_type("::S")
1181            .member("a", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| {
1182                m.set_member_default("42")
1183            })
1184            .build_complete();
1185        assert_eq!(
1186            st.member_seq[0].detail.ann_builtin.default_value.as_deref(),
1187            Some("42")
1188        );
1189    }
1190
1191    #[test]
1192    fn default_overrides_implicit_zero() {
1193        // Encoder-side hint: an explicit `@default("99")` must appear in the
1194        // complete TypeObject — the decoder uses it
1195        // when the field is missing for optional-mutable members, instead of the
1196        // type-implicit zero.
1197        let st = TypeObjectBuilder::struct_type("::S")
1198            .member("a", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| {
1199                m.set_member_default("99")
1200            })
1201            .member("b", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| m)
1202            .build_complete();
1203        assert_eq!(
1204            st.member_seq[0].detail.ann_builtin.default_value.as_deref(),
1205            Some("99")
1206        );
1207        assert_eq!(st.member_seq[1].detail.ann_builtin.default_value, None);
1208    }
1209
1210    #[test]
1211    fn default_value_roundtrips_via_encode_decode() {
1212        use crate::type_object::common::AppliedBuiltinMemberAnnotations;
1213        use alloc::vec;
1214        use zerodds_cdr::{BufferReader, BufferWriter, Endianness};
1215        let ann = AppliedBuiltinMemberAnnotations {
1216            unit: Some("meters".into()),
1217            min: Some(vec![0]),
1218            max: Some(vec![100]),
1219            hash_id: None,
1220            default_value: Some("17".into()),
1221        };
1222        let mut w = BufferWriter::new(Endianness::Little);
1223        ann.encode_into(&mut w).unwrap();
1224        let bytes = w.into_bytes();
1225        let mut r = BufferReader::new(&bytes, Endianness::Little);
1226        let decoded = AppliedBuiltinMemberAnnotations::decode_from(&mut r).unwrap();
1227        assert_eq!(decoded, ann);
1228    }
1229
1230    #[test]
1231    fn struct_builder_extensibility_mutable() {
1232        let st = TypeObjectBuilder::struct_type("::Y")
1233            .extensibility(Extensibility::Mutable)
1234            .build_minimal();
1235        assert!(st.struct_flags.has(StructTypeFlag::IS_MUTABLE));
1236        assert!(!st.struct_flags.has(StructTypeFlag::IS_APPENDABLE));
1237    }
1238
1239    #[test]
1240    fn complete_builder_preserves_names() {
1241        let st = TypeObjectBuilder::struct_type("::sensors::Chatter")
1242            .extensibility(Extensibility::Appendable)
1243            .member(
1244                "sensor_id",
1245                TypeIdentifier::Primitive(PrimitiveKind::Int64),
1246                |m| m.key().unit("celsius"),
1247            )
1248            .build_complete();
1249        assert_eq!(st.header.detail.type_name, "::sensors::Chatter");
1250        assert_eq!(st.member_seq[0].detail.name, "sensor_id");
1251        assert_eq!(
1252            st.member_seq[0].detail.ann_builtin.unit.as_deref(),
1253            Some("celsius")
1254        );
1255    }
1256
1257    #[test]
1258    fn struct_builder_autoid_hash_collision_free_for_distinct_names() {
1259        let st = TypeObjectBuilder::struct_type("::H")
1260            .autoid_hash()
1261            .member(
1262                "alpha",
1263                TypeIdentifier::Primitive(PrimitiveKind::Int32),
1264                |m| m,
1265            )
1266            .member(
1267                "beta",
1268                TypeIdentifier::Primitive(PrimitiveKind::Int32),
1269                |m| m,
1270            )
1271            .build_minimal();
1272        assert!(st.struct_flags.has(StructTypeFlag::IS_AUTOID_HASH));
1273        assert_ne!(
1274            st.member_seq[0].common.member_id,
1275            st.member_seq[1].common.member_id
1276        );
1277    }
1278
1279    #[test]
1280    fn enum_builder_roundtrip_ready() {
1281        let e = TypeObjectBuilder::enum_type("::Color")
1282            .bit_bound(16)
1283            .default_literal("RED", 0)
1284            .literal("GREEN", 1)
1285            .literal("BLUE", 2)
1286            .build_minimal();
1287        assert_eq!(e.header.common.bit_bound, 16);
1288        assert_eq!(e.literal_seq.len(), 3);
1289        assert!(e.literal_seq[0].common.flags.0 & EnumLiteralFlag::IS_DEFAULT_LITERAL != 0);
1290    }
1291
1292    #[test]
1293    fn alias_builder_minimal_and_complete() {
1294        let a_min =
1295            TypeObjectBuilder::alias("::Count", TypeIdentifier::Primitive(PrimitiveKind::UInt64))
1296                .build_minimal();
1297        assert!(matches!(
1298            a_min.body.common.related_type,
1299            TypeIdentifier::Primitive(PrimitiveKind::UInt64)
1300        ));
1301
1302        let a_cmp =
1303            TypeObjectBuilder::alias("::Count", TypeIdentifier::Primitive(PrimitiveKind::UInt64))
1304                .build_complete();
1305        assert_eq!(a_cmp.header.detail.type_name, "::Count");
1306    }
1307
1308    // ------------------------------------------------------------------
1309    // Neue Builder (T#2 — Union / Collections / Bitmask / Bitset)
1310    // ------------------------------------------------------------------
1311
1312    #[test]
1313    fn union_builder_minimal_with_two_cases_and_default() {
1314        let u = TypeObjectBuilder::union_type(
1315            "::Shape",
1316            TypeIdentifier::Primitive(PrimitiveKind::Int32),
1317        )
1318        .case(
1319            "circle",
1320            TypeIdentifier::Primitive(PrimitiveKind::Float64),
1321            alloc::vec![1, 2],
1322        )
1323        .default_case("other", TypeIdentifier::String8Small { bound: 64 })
1324        .build_minimal();
1325        assert_eq!(u.member_seq.len(), 2);
1326        assert_eq!(u.member_seq[0].common.label_seq, alloc::vec![1, 2]);
1327        // Default-Case hat IS_DEFAULT-Bit.
1328        assert!(
1329            u.member_seq[1].common.member_flags.0
1330                & crate::type_object::flags::UnionMemberFlag::IS_DEFAULT
1331                != 0
1332        );
1333    }
1334
1335    #[test]
1336    fn union_builder_complete_preserves_names_and_extensibility() {
1337        let u = TypeObjectBuilder::union_type(
1338            "::Shape",
1339            TypeIdentifier::Primitive(PrimitiveKind::Int32),
1340        )
1341        .extensibility(Extensibility::Mutable)
1342        .case(
1343            "a",
1344            TypeIdentifier::Primitive(PrimitiveKind::Int32),
1345            alloc::vec![1],
1346        )
1347        .build_complete();
1348        assert_eq!(u.header.detail.type_name, "::Shape");
1349        assert_eq!(u.member_seq[0].detail.name, "a");
1350        assert_eq!(
1351            u.union_flags.0 & StructTypeFlag::IS_MUTABLE,
1352            StructTypeFlag::IS_MUTABLE
1353        );
1354    }
1355
1356    #[test]
1357    fn sequence_builder_minimal() {
1358        let s = TypeObjectBuilder::sequence(TypeIdentifier::Primitive(PrimitiveKind::Int16), 100)
1359            .build_minimal();
1360        assert_eq!(s.bound, 100);
1361        assert!(matches!(
1362            s.element.common.type_id,
1363            TypeIdentifier::Primitive(PrimitiveKind::Int16)
1364        ));
1365    }
1366
1367    #[test]
1368    fn array_builder_3d() {
1369        let a = TypeObjectBuilder::array(
1370            TypeIdentifier::Primitive(PrimitiveKind::Float32),
1371            alloc::vec![4, 4, 4],
1372        )
1373        .build_minimal();
1374        assert_eq!(a.bound_seq, alloc::vec![4, 4, 4]);
1375    }
1376
1377    #[test]
1378    fn map_builder_string_to_int() {
1379        let m = TypeObjectBuilder::map(
1380            TypeIdentifier::String8Small { bound: 64 },
1381            TypeIdentifier::Primitive(PrimitiveKind::Int64),
1382            1_000,
1383        )
1384        .build_minimal();
1385        assert_eq!(m.bound, 1_000);
1386        assert!(matches!(
1387            m.key.common.type_id,
1388            TypeIdentifier::String8Small { bound: 64 }
1389        ));
1390    }
1391
1392    #[test]
1393    fn bitmask_builder_three_flags() {
1394        let b = TypeObjectBuilder::bitmask("::Permissions")
1395            .bit_bound(32)
1396            .flag("READ", 0)
1397            .flag("WRITE", 1)
1398            .flag("EXEC", 2)
1399            .build_minimal();
1400        assert_eq!(b.bit_bound, 32);
1401        assert_eq!(b.flag_seq.len(), 3);
1402        assert_eq!(b.flag_seq[0].common.position, 0);
1403        assert_eq!(b.flag_seq[2].common.position, 2);
1404    }
1405
1406    #[test]
1407    fn bitset_builder_two_fields() {
1408        let b = TypeObjectBuilder::bitset("::Packed")
1409            .field("header", 0, 4, 0x07) // TK_UINT32
1410            .field("body", 4, 28, 0x07)
1411            .build_minimal();
1412        assert_eq!(b.field_seq.len(), 2);
1413        assert_eq!(b.field_seq[0].common.bitcount, 4);
1414        assert_eq!(b.field_seq[1].common.position, 4);
1415    }
1416
1417    #[test]
1418    fn bitmask_builder_complete_preserves_names() {
1419        let b = TypeObjectBuilder::bitmask("::Perm")
1420            .bit_bound(16)
1421            .flag("READ", 0)
1422            .flag("WRITE", 1)
1423            .build_complete();
1424        assert_eq!(b.detail.type_name, "::Perm");
1425        assert_eq!(b.bit_bound, 16);
1426        assert_eq!(b.flag_seq.len(), 2);
1427        assert_eq!(b.flag_seq[0].detail.name, "READ");
1428        assert_eq!(b.flag_seq[1].detail.name, "WRITE");
1429    }
1430
1431    #[test]
1432    fn bitset_builder_complete_preserves_names() {
1433        let b = TypeObjectBuilder::bitset("::Packed")
1434            .field("header", 0, 4, 0x07)
1435            .field("body", 4, 28, 0x07)
1436            .build_complete();
1437        assert_eq!(b.detail.type_name, "::Packed");
1438        assert_eq!(b.field_seq.len(), 2);
1439        assert_eq!(b.field_seq[0].detail.name, "header");
1440        assert_eq!(b.field_seq[1].detail.name, "body");
1441    }
1442}