use alloc::string::String;
use alloc::vec::Vec;
use crate::type_identifier::TypeIdentifier;
use crate::type_object::common::{
AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CommonStructMember,
CompleteMemberDetail, CompleteTypeDetail, NameHash, OptionalAppliedAnnotationSeq,
};
use crate::type_object::complete::{
CompleteAliasBody, CompleteAliasHeader, CompleteAliasType, CompleteEnumeratedHeader,
CompleteEnumeratedLiteral, CompleteEnumeratedType, CompleteStructHeader, CompleteStructMember,
CompleteStructType,
};
use crate::type_object::flags::{
AliasMemberFlag, AliasTypeFlag, EnumLiteralFlag, EnumTypeFlag, StructMemberFlag, StructTypeFlag,
};
use crate::type_object::minimal::CommonAliasBody;
use crate::type_object::minimal::{
CommonEnumeratedHeader, CommonEnumeratedLiteral, MinimalAliasBody, MinimalAliasType,
MinimalEnumeratedHeader, MinimalEnumeratedLiteral, MinimalEnumeratedType, MinimalStructHeader,
MinimalStructMember, MinimalStructType,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Extensibility {
Final,
Appendable,
Mutable,
}
impl Default for Extensibility {
fn default() -> Self {
Self::Appendable
}
}
impl Extensibility {
const fn to_flag_bits(self) -> u16 {
match self {
Self::Final => StructTypeFlag::IS_FINAL,
Self::Appendable => StructTypeFlag::IS_APPENDABLE,
Self::Mutable => StructTypeFlag::IS_MUTABLE,
}
}
}
pub struct TypeObjectBuilder;
impl TypeObjectBuilder {
#[must_use]
pub fn struct_type(name: impl Into<String>) -> StructBuilder {
StructBuilder {
name: name.into(),
extensibility: Extensibility::default(),
nested: false,
autoid_hash: false,
base_type: TypeIdentifier::None,
members: Vec::new(),
}
}
#[must_use]
pub fn enum_type(name: impl Into<String>) -> EnumBuilder {
EnumBuilder {
name: name.into(),
bit_bound: 32,
literals: Vec::new(),
}
}
#[must_use]
pub fn alias(name: impl Into<String>, target: TypeIdentifier) -> AliasBuilder {
AliasBuilder {
name: name.into(),
related_type: target,
}
}
}
pub struct StructBuilder {
name: String,
extensibility: Extensibility,
nested: bool,
autoid_hash: bool,
base_type: TypeIdentifier,
members: Vec<StructMemberSpec>,
}
pub struct StructMemberSpec {
name: String,
type_id: TypeIdentifier,
explicit_id: Option<u32>,
flags: u16,
unit: Option<String>,
min: Option<Vec<u8>>,
max: Option<Vec<u8>>,
hash_id: Option<String>,
default_value: Option<String>,
}
pub struct StructMemberBuilder<'a> {
spec: &'a mut StructMemberSpec,
}
impl StructMemberBuilder<'_> {
#[must_use]
pub fn key(self) -> Self {
self.spec.flags |= StructMemberFlag::IS_KEY;
self
}
#[must_use]
pub fn optional(self) -> Self {
self.spec.flags |= StructMemberFlag::IS_OPTIONAL;
self
}
#[must_use]
pub fn must_understand(self) -> Self {
self.spec.flags |= StructMemberFlag::IS_MUST_UNDERSTAND;
self
}
#[must_use]
pub fn external(self) -> Self {
self.spec.flags |= StructMemberFlag::IS_EXTERNAL;
self
}
#[must_use]
pub fn id(self, id: u32) -> Self {
self.spec.explicit_id = Some(id);
self
}
#[must_use]
pub fn unit(self, unit: impl Into<String>) -> Self {
self.spec.unit = Some(unit.into());
self
}
#[must_use]
pub fn min_bytes(self, min: Vec<u8>) -> Self {
self.spec.min = Some(min);
self
}
#[must_use]
pub fn max_bytes(self, max: Vec<u8>) -> Self {
self.spec.max = Some(max);
self
}
#[must_use]
pub fn hash_id(self, name: impl Into<String>) -> Self {
self.spec.hash_id = Some(name.into());
self
}
#[must_use]
pub fn set_member_default(self, value: impl Into<String>) -> Self {
self.spec.default_value = Some(value.into());
self
}
}
impl StructBuilder {
#[must_use]
pub fn extensibility(mut self, ext: Extensibility) -> Self {
self.extensibility = ext;
self
}
#[must_use]
pub fn nested(mut self) -> Self {
self.nested = true;
self
}
#[must_use]
pub fn autoid_hash(mut self) -> Self {
self.autoid_hash = true;
self
}
#[must_use]
pub fn base(mut self, base: TypeIdentifier) -> Self {
self.base_type = base;
self
}
#[must_use]
pub fn member<F>(mut self, name: impl Into<String>, ty: TypeIdentifier, f: F) -> Self
where
F: FnOnce(StructMemberBuilder<'_>) -> StructMemberBuilder<'_>,
{
let mut spec = StructMemberSpec {
name: name.into(),
type_id: ty,
explicit_id: None,
flags: 0,
unit: None,
min: None,
max: None,
hash_id: None,
default_value: None,
};
let _ = f(StructMemberBuilder { spec: &mut spec });
self.members.push(spec);
self
}
fn struct_flags(&self) -> StructTypeFlag {
let mut bits = self.extensibility.to_flag_bits();
if self.nested {
bits |= StructTypeFlag::IS_NESTED;
}
if self.autoid_hash {
bits |= StructTypeFlag::IS_AUTOID_HASH;
}
StructTypeFlag(bits)
}
fn resolve_member_ids(&self) -> Vec<u32> {
let mut ids = Vec::with_capacity(self.members.len());
let mut next_seq: u32 = 1;
for spec in &self.members {
let id = if let Some(explicit) = spec.explicit_id {
explicit
} else if self.autoid_hash {
let nh = NameHash::from_name(&spec.name);
(u32::from_le_bytes(nh.0) >> 4) & 0x00FF_FFFF
} else {
let v = next_seq;
next_seq += 1;
v
};
ids.push(id);
}
ids
}
fn member_common(spec: &StructMemberSpec, id: u32) -> CommonStructMember {
CommonStructMember {
member_id: id,
member_flags: StructMemberFlag(spec.flags),
member_type_id: spec.type_id.clone(),
}
}
fn member_detail(spec: &StructMemberSpec) -> CompleteMemberDetail {
CompleteMemberDetail {
name: spec.name.clone(),
ann_builtin: AppliedBuiltinMemberAnnotations {
unit: spec.unit.clone(),
min: spec.min.clone(),
max: spec.max.clone(),
hash_id: spec.hash_id.clone(),
default_value: spec.default_value.clone(),
},
ann_custom: OptionalAppliedAnnotationSeq::default(),
}
}
#[must_use]
pub fn build_minimal(&self) -> MinimalStructType {
let ids = self.resolve_member_ids();
let member_seq = self
.members
.iter()
.zip(ids.iter())
.map(|(spec, id)| MinimalStructMember {
common: Self::member_common(spec, *id),
detail: NameHash::from_name(&spec.name),
})
.collect();
MinimalStructType {
struct_flags: self.struct_flags(),
header: MinimalStructHeader {
base_type: self.base_type.clone(),
},
member_seq,
}
}
#[must_use]
pub fn build_complete(&self) -> CompleteStructType {
let ids = self.resolve_member_ids();
let member_seq = self
.members
.iter()
.zip(ids.iter())
.map(|(spec, id)| CompleteStructMember {
common: Self::member_common(spec, *id),
detail: Self::member_detail(spec),
})
.collect();
CompleteStructType {
struct_flags: self.struct_flags(),
header: CompleteStructHeader {
base_type: self.base_type.clone(),
detail: CompleteTypeDetail {
ann_builtin: AppliedBuiltinTypeAnnotations::default(),
ann_custom: OptionalAppliedAnnotationSeq::default(),
type_name: self.name.clone(),
},
},
member_seq,
}
}
}
pub struct EnumBuilder {
name: String,
bit_bound: u16,
literals: Vec<EnumLiteralSpec>,
}
struct EnumLiteralSpec {
name: String,
value: i32,
is_default: bool,
}
impl EnumBuilder {
#[must_use]
pub fn bit_bound(mut self, bits: u16) -> Self {
self.bit_bound = bits;
self
}
#[must_use]
pub fn literal(mut self, name: impl Into<String>, value: i32) -> Self {
self.literals.push(EnumLiteralSpec {
name: name.into(),
value,
is_default: false,
});
self
}
#[must_use]
pub fn default_literal(mut self, name: impl Into<String>, value: i32) -> Self {
self.literals.push(EnumLiteralSpec {
name: name.into(),
value,
is_default: true,
});
self
}
#[must_use]
pub fn build_minimal(&self) -> MinimalEnumeratedType {
MinimalEnumeratedType {
enum_flags: EnumTypeFlag::default(),
header: MinimalEnumeratedHeader {
common: CommonEnumeratedHeader {
bit_bound: self.bit_bound,
},
},
literal_seq: self
.literals
.iter()
.map(|l| MinimalEnumeratedLiteral {
common: CommonEnumeratedLiteral {
value: l.value,
flags: EnumLiteralFlag(if l.is_default {
EnumLiteralFlag::IS_DEFAULT_LITERAL
} else {
0
}),
},
detail: NameHash::from_name(&l.name),
})
.collect(),
}
}
#[must_use]
pub fn build_complete(&self) -> CompleteEnumeratedType {
CompleteEnumeratedType {
enum_flags: EnumTypeFlag::default(),
header: CompleteEnumeratedHeader {
common: CommonEnumeratedHeader {
bit_bound: self.bit_bound,
},
detail: CompleteTypeDetail {
ann_builtin: AppliedBuiltinTypeAnnotations::default(),
ann_custom: OptionalAppliedAnnotationSeq::default(),
type_name: self.name.clone(),
},
},
literal_seq: self
.literals
.iter()
.map(|l| CompleteEnumeratedLiteral {
common: CommonEnumeratedLiteral {
value: l.value,
flags: EnumLiteralFlag(if l.is_default {
EnumLiteralFlag::IS_DEFAULT_LITERAL
} else {
0
}),
},
detail: CompleteMemberDetail {
name: l.name.clone(),
ann_builtin: AppliedBuiltinMemberAnnotations::default(),
ann_custom: OptionalAppliedAnnotationSeq::default(),
},
})
.collect(),
}
}
}
pub struct AliasBuilder {
name: String,
related_type: TypeIdentifier,
}
impl AliasBuilder {
#[must_use]
pub fn build_minimal(&self) -> MinimalAliasType {
MinimalAliasType {
alias_flags: AliasTypeFlag::default(),
body: MinimalAliasBody {
common: CommonAliasBody {
related_flags: AliasMemberFlag::default(),
related_type: self.related_type.clone(),
},
},
}
}
#[must_use]
pub fn build_complete(&self) -> CompleteAliasType {
CompleteAliasType {
alias_flags: AliasTypeFlag::default(),
header: CompleteAliasHeader {
detail: CompleteTypeDetail {
ann_builtin: AppliedBuiltinTypeAnnotations::default(),
ann_custom: OptionalAppliedAnnotationSeq::default(),
type_name: self.name.clone(),
},
},
body: CompleteAliasBody {
related_flags: AliasMemberFlag::default(),
related_type: self.related_type.clone(),
ann_builtin: AppliedBuiltinMemberAnnotations::default(),
ann_custom: OptionalAppliedAnnotationSeq::default(),
},
}
}
}
pub struct UnionBuilder {
name: String,
extensibility: Extensibility,
discriminator_type: TypeIdentifier,
cases: Vec<UnionCaseSpec>,
}
struct UnionCaseSpec {
name: String,
member_id: Option<u32>,
type_id: TypeIdentifier,
labels: Vec<i32>,
is_default: bool,
}
impl UnionBuilder {
#[must_use]
pub fn extensibility(mut self, ext: Extensibility) -> Self {
self.extensibility = ext;
self
}
#[must_use]
pub fn case(mut self, name: impl Into<String>, ty: TypeIdentifier, labels: Vec<i32>) -> Self {
self.cases.push(UnionCaseSpec {
name: name.into(),
member_id: None,
type_id: ty,
labels,
is_default: false,
});
self
}
#[must_use]
pub fn default_case(mut self, name: impl Into<String>, ty: TypeIdentifier) -> Self {
self.cases.push(UnionCaseSpec {
name: name.into(),
member_id: None,
type_id: ty,
labels: Vec::new(),
is_default: true,
});
self
}
fn union_flags(&self) -> crate::type_object::flags::UnionTypeFlag {
use crate::type_object::flags::UnionTypeFlag;
UnionTypeFlag(match self.extensibility {
Extensibility::Final => StructTypeFlag::IS_FINAL,
Extensibility::Appendable => StructTypeFlag::IS_APPENDABLE,
Extensibility::Mutable => StructTypeFlag::IS_MUTABLE,
})
}
fn resolve_case_ids(&self) -> Vec<u32> {
let mut ids = Vec::with_capacity(self.cases.len());
let mut next: u32 = 1;
for c in &self.cases {
ids.push(c.member_id.unwrap_or_else(|| {
let v = next;
next += 1;
v
}));
}
ids
}
#[must_use]
pub fn build_minimal(&self) -> crate::type_object::minimal::MinimalUnionType {
use crate::type_object::common::CommonUnionMember;
use crate::type_object::flags::{UnionDiscriminatorFlag, UnionMemberFlag};
use crate::type_object::minimal::{
CommonDiscriminatorMember, MinimalDiscriminatorMember, MinimalUnionMember,
MinimalUnionType,
};
let ids = self.resolve_case_ids();
let member_seq = self
.cases
.iter()
.zip(ids.iter())
.map(|(c, id)| MinimalUnionMember {
common: CommonUnionMember {
member_id: *id,
member_flags: UnionMemberFlag(if c.is_default {
UnionMemberFlag::IS_DEFAULT
} else {
0
}),
type_id: c.type_id.clone(),
label_seq: c.labels.clone(),
},
detail: NameHash::from_name(&c.name),
})
.collect();
MinimalUnionType {
union_flags: self.union_flags(),
discriminator: MinimalDiscriminatorMember {
common: CommonDiscriminatorMember {
member_flags: UnionDiscriminatorFlag::default(),
type_id: self.discriminator_type.clone(),
},
},
member_seq,
}
}
#[must_use]
pub fn build_complete(&self) -> crate::type_object::complete::CompleteUnionType {
use crate::type_object::common::CommonUnionMember;
use crate::type_object::complete::{
CompleteDiscriminatorMember, CompleteUnionHeader, CompleteUnionMember,
CompleteUnionType,
};
use crate::type_object::flags::{UnionDiscriminatorFlag, UnionMemberFlag};
use crate::type_object::minimal::CommonDiscriminatorMember;
let ids = self.resolve_case_ids();
let member_seq = self
.cases
.iter()
.zip(ids.iter())
.map(|(c, id)| CompleteUnionMember {
common: CommonUnionMember {
member_id: *id,
member_flags: UnionMemberFlag(if c.is_default {
UnionMemberFlag::IS_DEFAULT
} else {
0
}),
type_id: c.type_id.clone(),
label_seq: c.labels.clone(),
},
detail: CompleteMemberDetail {
name: c.name.clone(),
ann_builtin: AppliedBuiltinMemberAnnotations::default(),
ann_custom: OptionalAppliedAnnotationSeq::default(),
},
})
.collect();
CompleteUnionType {
union_flags: self.union_flags(),
header: CompleteUnionHeader {
detail: CompleteTypeDetail {
ann_builtin: AppliedBuiltinTypeAnnotations::default(),
ann_custom: OptionalAppliedAnnotationSeq::default(),
type_name: self.name.clone(),
},
},
discriminator: CompleteDiscriminatorMember {
common: CommonDiscriminatorMember {
member_flags: UnionDiscriminatorFlag::default(),
type_id: self.discriminator_type.clone(),
},
ann_builtin: AppliedBuiltinTypeAnnotations::default(),
ann_custom: OptionalAppliedAnnotationSeq::default(),
},
member_seq,
}
}
}
pub struct SequenceBuilder {
element: TypeIdentifier,
bound: u32,
}
impl SequenceBuilder {
#[must_use]
pub fn build_minimal(&self) -> crate::type_object::minimal::MinimalSequenceType {
use crate::type_object::flags::{CollectionElementFlag, CollectionTypeFlag};
use crate::type_object::minimal::{
CommonCollectionElement, MinimalCollectionElement, MinimalSequenceType,
};
MinimalSequenceType {
collection_flag: CollectionTypeFlag::default(),
bound: self.bound,
element: MinimalCollectionElement {
common: CommonCollectionElement {
element_flags: CollectionElementFlag::default(),
type_id: self.element.clone(),
},
},
}
}
}
pub struct ArrayBuilder {
element: TypeIdentifier,
dimensions: Vec<u32>,
}
impl ArrayBuilder {
#[must_use]
pub fn build_minimal(&self) -> crate::type_object::minimal::MinimalArrayType {
use crate::type_object::flags::{CollectionElementFlag, CollectionTypeFlag};
use crate::type_object::minimal::{
CommonCollectionElement, MinimalArrayType, MinimalCollectionElement,
};
MinimalArrayType {
collection_flag: CollectionTypeFlag::default(),
bound_seq: self.dimensions.clone(),
element: MinimalCollectionElement {
common: CommonCollectionElement {
element_flags: CollectionElementFlag::default(),
type_id: self.element.clone(),
},
},
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BuilderError {
MutableMapExtensibilityNotAllowed,
}
pub struct MapBuilder {
key: TypeIdentifier,
value: TypeIdentifier,
bound: u32,
}
impl MapBuilder {
pub fn add_map_member(
self,
ext: Extensibility,
) -> Result<crate::type_object::minimal::MinimalMapType, BuilderError> {
if matches!(ext, Extensibility::Mutable) {
return Err(BuilderError::MutableMapExtensibilityNotAllowed);
}
Ok(self.build_minimal())
}
#[must_use]
pub fn build_minimal(&self) -> crate::type_object::minimal::MinimalMapType {
use crate::type_object::flags::{CollectionElementFlag, CollectionTypeFlag};
use crate::type_object::minimal::{
CommonCollectionElement, MinimalCollectionElement, MinimalMapType,
};
let element = MinimalCollectionElement {
common: CommonCollectionElement {
element_flags: CollectionElementFlag::default(),
type_id: self.value.clone(),
},
};
let key = MinimalCollectionElement {
common: CommonCollectionElement {
element_flags: CollectionElementFlag::default(),
type_id: self.key.clone(),
},
};
MinimalMapType {
collection_flag: CollectionTypeFlag::default(),
bound: self.bound,
key,
element,
}
}
}
pub struct BitmaskBuilder {
name: String,
bit_bound: u16,
flags: Vec<(String, u16)>,
}
impl BitmaskBuilder {
#[must_use]
pub fn bit_bound(mut self, bits: u16) -> Self {
self.bit_bound = bits;
self
}
#[must_use]
pub fn flag(mut self, name: impl Into<String>, position: u16) -> Self {
self.flags.push((name.into(), position));
self
}
#[must_use]
pub fn build_minimal(&self) -> crate::type_object::minimal::MinimalBitmaskType {
use crate::type_object::flags::{BitflagFlag, BitmaskTypeFlag};
use crate::type_object::minimal::{CommonBitflag, MinimalBitflag, MinimalBitmaskType};
MinimalBitmaskType {
bitmask_flags: BitmaskTypeFlag::default(),
bit_bound: self.bit_bound,
flag_seq: self
.flags
.iter()
.map(|(n, p)| MinimalBitflag {
common: CommonBitflag {
position: *p,
flags: BitflagFlag::default(),
},
detail: NameHash::from_name(n),
})
.collect(),
}
}
#[must_use]
pub fn build_complete(&self) -> crate::type_object::complete::CompleteBitmaskType {
use crate::type_object::common::{
AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
CompleteTypeDetail, OptionalAppliedAnnotationSeq,
};
use crate::type_object::complete::{CompleteBitflag, CompleteBitmaskType};
use crate::type_object::flags::{BitflagFlag, BitmaskTypeFlag};
CompleteBitmaskType {
bitmask_flags: BitmaskTypeFlag::default(),
bit_bound: self.bit_bound,
detail: CompleteTypeDetail {
ann_builtin: AppliedBuiltinTypeAnnotations::default(),
ann_custom: OptionalAppliedAnnotationSeq::default(),
type_name: self.name.clone(),
},
flag_seq: self
.flags
.iter()
.map(|(n, p)| CompleteBitflag {
common: crate::type_object::minimal::CommonBitflag {
position: *p,
flags: BitflagFlag::default(),
},
detail: CompleteMemberDetail {
name: n.clone(),
ann_builtin: AppliedBuiltinMemberAnnotations::default(),
ann_custom: OptionalAppliedAnnotationSeq::default(),
},
})
.collect(),
}
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
}
pub struct BitsetBuilder {
name: String,
fields: Vec<BitfieldSpec>,
}
struct BitfieldSpec {
name: String,
position: u16,
bitcount: u8,
holder_type: u8,
}
impl BitsetBuilder {
#[must_use]
pub fn field(
mut self,
name: impl Into<String>,
position: u16,
bitcount: u8,
holder_type: u8,
) -> Self {
self.fields.push(BitfieldSpec {
name: name.into(),
position,
bitcount,
holder_type,
});
self
}
#[must_use]
pub fn build_minimal(&self) -> crate::type_object::minimal::MinimalBitsetType {
use crate::type_object::flags::{BitfieldFlag, BitsetTypeFlag};
use crate::type_object::minimal::{CommonBitfield, MinimalBitfield, MinimalBitsetType};
MinimalBitsetType {
bitset_flags: BitsetTypeFlag::default(),
field_seq: self
.fields
.iter()
.map(|f| MinimalBitfield {
common: CommonBitfield {
position: f.position,
flags: BitfieldFlag::default(),
bitcount: f.bitcount,
holder_type: f.holder_type,
},
name_hash: NameHash::from_name(&f.name),
})
.collect(),
}
}
#[must_use]
pub fn build_complete(&self) -> crate::type_object::complete::CompleteBitsetType {
use crate::type_object::common::{
AppliedBuiltinMemberAnnotations, AppliedBuiltinTypeAnnotations, CompleteMemberDetail,
CompleteTypeDetail, OptionalAppliedAnnotationSeq,
};
use crate::type_object::complete::{CompleteBitfield, CompleteBitsetType};
use crate::type_object::flags::{BitfieldFlag, BitsetTypeFlag};
CompleteBitsetType {
bitset_flags: BitsetTypeFlag::default(),
detail: CompleteTypeDetail {
ann_builtin: AppliedBuiltinTypeAnnotations::default(),
ann_custom: OptionalAppliedAnnotationSeq::default(),
type_name: self.name.clone(),
},
field_seq: self
.fields
.iter()
.map(|f| CompleteBitfield {
common: crate::type_object::minimal::CommonBitfield {
position: f.position,
flags: BitfieldFlag::default(),
bitcount: f.bitcount,
holder_type: f.holder_type,
},
detail: CompleteMemberDetail {
name: f.name.clone(),
ann_builtin: AppliedBuiltinMemberAnnotations::default(),
ann_custom: OptionalAppliedAnnotationSeq::default(),
},
})
.collect(),
}
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
}
impl TypeObjectBuilder {
#[must_use]
pub fn union_type(name: impl Into<String>, discriminator_type: TypeIdentifier) -> UnionBuilder {
UnionBuilder {
name: name.into(),
extensibility: Extensibility::default(),
discriminator_type,
cases: Vec::new(),
}
}
#[must_use]
pub fn sequence(element: TypeIdentifier, bound: u32) -> SequenceBuilder {
SequenceBuilder { element, bound }
}
#[must_use]
pub fn array(element: TypeIdentifier, dimensions: Vec<u32>) -> ArrayBuilder {
ArrayBuilder {
element,
dimensions,
}
}
#[must_use]
pub fn map(key: TypeIdentifier, value: TypeIdentifier, bound: u32) -> MapBuilder {
MapBuilder { key, value, bound }
}
#[must_use]
pub fn bitmask(name: impl Into<String>) -> BitmaskBuilder {
BitmaskBuilder {
name: name.into(),
bit_bound: 32,
flags: Vec::new(),
}
}
#[must_use]
pub fn bitset(name: impl Into<String>) -> BitsetBuilder {
BitsetBuilder {
name: name.into(),
fields: Vec::new(),
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use crate::type_identifier::PrimitiveKind;
use crate::type_object::flags::StructTypeFlag;
#[test]
fn struct_builder_basic_with_two_members() {
let st = TypeObjectBuilder::struct_type("::chat::Chatter")
.member(
"sensor_id",
TypeIdentifier::Primitive(PrimitiveKind::Int64),
|m| m.key(),
)
.member("text", TypeIdentifier::String8Small { bound: 255 }, |m| m)
.build_minimal();
assert_eq!(st.member_seq.len(), 2);
assert_eq!(st.member_seq[0].common.member_id, 1);
assert!(
st.member_seq[0]
.common
.member_flags
.has(StructMemberFlag::IS_KEY)
);
assert_eq!(st.member_seq[1].common.member_id, 2);
assert_eq!(st.member_seq[0].detail, NameHash::from_name("sensor_id"));
assert_eq!(st.member_seq[1].detail, NameHash::from_name("text"));
}
#[test]
fn struct_builder_explicit_ids_respected() {
let st = TypeObjectBuilder::struct_type("::X")
.member("a", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| {
m.id(100)
})
.member("b", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| {
m.id(200)
})
.build_minimal();
assert_eq!(st.member_seq[0].common.member_id, 100);
assert_eq!(st.member_seq[1].common.member_id, 200);
}
#[test]
fn mutable_map_extensibility_is_error() {
let res = TypeObjectBuilder::map(
TypeIdentifier::Primitive(PrimitiveKind::Int32),
TypeIdentifier::Primitive(PrimitiveKind::Int32),
10,
)
.add_map_member(Extensibility::Mutable);
assert!(matches!(
res,
Err(BuilderError::MutableMapExtensibilityNotAllowed)
));
}
#[test]
fn appendable_map_extensibility_is_ok() {
let res = TypeObjectBuilder::map(
TypeIdentifier::Primitive(PrimitiveKind::Int32),
TypeIdentifier::Primitive(PrimitiveKind::Int32),
10,
)
.add_map_member(Extensibility::Appendable);
assert!(res.is_ok());
}
#[test]
fn final_map_extensibility_is_ok() {
let res = TypeObjectBuilder::map(
TypeIdentifier::Primitive(PrimitiveKind::Int32),
TypeIdentifier::Primitive(PrimitiveKind::Int32),
10,
)
.add_map_member(Extensibility::Final);
assert!(res.is_ok());
}
#[test]
fn member_with_explicit_default_used_when_field_missing() {
let st = TypeObjectBuilder::struct_type("::S")
.member("a", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| {
m.set_member_default("42")
})
.build_complete();
assert_eq!(
st.member_seq[0].detail.ann_builtin.default_value.as_deref(),
Some("42")
);
}
#[test]
fn default_overrides_implicit_zero() {
let st = TypeObjectBuilder::struct_type("::S")
.member("a", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| {
m.set_member_default("99")
})
.member("b", TypeIdentifier::Primitive(PrimitiveKind::Int32), |m| m)
.build_complete();
assert_eq!(
st.member_seq[0].detail.ann_builtin.default_value.as_deref(),
Some("99")
);
assert_eq!(st.member_seq[1].detail.ann_builtin.default_value, None);
}
#[test]
fn default_value_roundtrips_via_encode_decode() {
use crate::type_object::common::AppliedBuiltinMemberAnnotations;
use alloc::vec;
use zerodds_cdr::{BufferReader, BufferWriter, Endianness};
let ann = AppliedBuiltinMemberAnnotations {
unit: Some("meters".into()),
min: Some(vec![0]),
max: Some(vec![100]),
hash_id: None,
default_value: Some("17".into()),
};
let mut w = BufferWriter::new(Endianness::Little);
ann.encode_into(&mut w).unwrap();
let bytes = w.into_bytes();
let mut r = BufferReader::new(&bytes, Endianness::Little);
let decoded = AppliedBuiltinMemberAnnotations::decode_from(&mut r).unwrap();
assert_eq!(decoded, ann);
}
#[test]
fn struct_builder_extensibility_mutable() {
let st = TypeObjectBuilder::struct_type("::Y")
.extensibility(Extensibility::Mutable)
.build_minimal();
assert!(st.struct_flags.has(StructTypeFlag::IS_MUTABLE));
assert!(!st.struct_flags.has(StructTypeFlag::IS_APPENDABLE));
}
#[test]
fn complete_builder_preserves_names() {
let st = TypeObjectBuilder::struct_type("::sensors::Chatter")
.extensibility(Extensibility::Appendable)
.member(
"sensor_id",
TypeIdentifier::Primitive(PrimitiveKind::Int64),
|m| m.key().unit("celsius"),
)
.build_complete();
assert_eq!(st.header.detail.type_name, "::sensors::Chatter");
assert_eq!(st.member_seq[0].detail.name, "sensor_id");
assert_eq!(
st.member_seq[0].detail.ann_builtin.unit.as_deref(),
Some("celsius")
);
}
#[test]
fn struct_builder_autoid_hash_collision_free_for_distinct_names() {
let st = TypeObjectBuilder::struct_type("::H")
.autoid_hash()
.member(
"alpha",
TypeIdentifier::Primitive(PrimitiveKind::Int32),
|m| m,
)
.member(
"beta",
TypeIdentifier::Primitive(PrimitiveKind::Int32),
|m| m,
)
.build_minimal();
assert!(st.struct_flags.has(StructTypeFlag::IS_AUTOID_HASH));
assert_ne!(
st.member_seq[0].common.member_id,
st.member_seq[1].common.member_id
);
}
#[test]
fn enum_builder_roundtrip_ready() {
let e = TypeObjectBuilder::enum_type("::Color")
.bit_bound(16)
.default_literal("RED", 0)
.literal("GREEN", 1)
.literal("BLUE", 2)
.build_minimal();
assert_eq!(e.header.common.bit_bound, 16);
assert_eq!(e.literal_seq.len(), 3);
assert!(e.literal_seq[0].common.flags.0 & EnumLiteralFlag::IS_DEFAULT_LITERAL != 0);
}
#[test]
fn alias_builder_minimal_and_complete() {
let a_min =
TypeObjectBuilder::alias("::Count", TypeIdentifier::Primitive(PrimitiveKind::UInt64))
.build_minimal();
assert!(matches!(
a_min.body.common.related_type,
TypeIdentifier::Primitive(PrimitiveKind::UInt64)
));
let a_cmp =
TypeObjectBuilder::alias("::Count", TypeIdentifier::Primitive(PrimitiveKind::UInt64))
.build_complete();
assert_eq!(a_cmp.header.detail.type_name, "::Count");
}
#[test]
fn union_builder_minimal_with_two_cases_and_default() {
let u = TypeObjectBuilder::union_type(
"::Shape",
TypeIdentifier::Primitive(PrimitiveKind::Int32),
)
.case(
"circle",
TypeIdentifier::Primitive(PrimitiveKind::Float64),
alloc::vec![1, 2],
)
.default_case("other", TypeIdentifier::String8Small { bound: 64 })
.build_minimal();
assert_eq!(u.member_seq.len(), 2);
assert_eq!(u.member_seq[0].common.label_seq, alloc::vec![1, 2]);
assert!(
u.member_seq[1].common.member_flags.0
& crate::type_object::flags::UnionMemberFlag::IS_DEFAULT
!= 0
);
}
#[test]
fn union_builder_complete_preserves_names_and_extensibility() {
let u = TypeObjectBuilder::union_type(
"::Shape",
TypeIdentifier::Primitive(PrimitiveKind::Int32),
)
.extensibility(Extensibility::Mutable)
.case(
"a",
TypeIdentifier::Primitive(PrimitiveKind::Int32),
alloc::vec![1],
)
.build_complete();
assert_eq!(u.header.detail.type_name, "::Shape");
assert_eq!(u.member_seq[0].detail.name, "a");
assert_eq!(
u.union_flags.0 & StructTypeFlag::IS_MUTABLE,
StructTypeFlag::IS_MUTABLE
);
}
#[test]
fn sequence_builder_minimal() {
let s = TypeObjectBuilder::sequence(TypeIdentifier::Primitive(PrimitiveKind::Int16), 100)
.build_minimal();
assert_eq!(s.bound, 100);
assert!(matches!(
s.element.common.type_id,
TypeIdentifier::Primitive(PrimitiveKind::Int16)
));
}
#[test]
fn array_builder_3d() {
let a = TypeObjectBuilder::array(
TypeIdentifier::Primitive(PrimitiveKind::Float32),
alloc::vec![4, 4, 4],
)
.build_minimal();
assert_eq!(a.bound_seq, alloc::vec![4, 4, 4]);
}
#[test]
fn map_builder_string_to_int() {
let m = TypeObjectBuilder::map(
TypeIdentifier::String8Small { bound: 64 },
TypeIdentifier::Primitive(PrimitiveKind::Int64),
1_000,
)
.build_minimal();
assert_eq!(m.bound, 1_000);
assert!(matches!(
m.key.common.type_id,
TypeIdentifier::String8Small { bound: 64 }
));
}
#[test]
fn bitmask_builder_three_flags() {
let b = TypeObjectBuilder::bitmask("::Permissions")
.bit_bound(32)
.flag("READ", 0)
.flag("WRITE", 1)
.flag("EXEC", 2)
.build_minimal();
assert_eq!(b.bit_bound, 32);
assert_eq!(b.flag_seq.len(), 3);
assert_eq!(b.flag_seq[0].common.position, 0);
assert_eq!(b.flag_seq[2].common.position, 2);
}
#[test]
fn bitset_builder_two_fields() {
let b = TypeObjectBuilder::bitset("::Packed")
.field("header", 0, 4, 0x07) .field("body", 4, 28, 0x07)
.build_minimal();
assert_eq!(b.field_seq.len(), 2);
assert_eq!(b.field_seq[0].common.bitcount, 4);
assert_eq!(b.field_seq[1].common.position, 4);
}
#[test]
fn bitmask_builder_complete_preserves_names() {
let b = TypeObjectBuilder::bitmask("::Perm")
.bit_bound(16)
.flag("READ", 0)
.flag("WRITE", 1)
.build_complete();
assert_eq!(b.detail.type_name, "::Perm");
assert_eq!(b.bit_bound, 16);
assert_eq!(b.flag_seq.len(), 2);
assert_eq!(b.flag_seq[0].detail.name, "READ");
assert_eq!(b.flag_seq[1].detail.name, "WRITE");
}
#[test]
fn bitset_builder_complete_preserves_names() {
let b = TypeObjectBuilder::bitset("::Packed")
.field("header", 0, 4, 0x07)
.field("body", 4, 28, 0x07)
.build_complete();
assert_eq!(b.detail.type_name, "::Packed");
assert_eq!(b.field_seq.len(), 2);
assert_eq!(b.field_seq[0].detail.name, "header");
assert_eq!(b.field_seq[1].detail.name, "body");
}
}