1use alloc::boxed::Box;
15use alloc::string::String;
16use alloc::vec::Vec;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23#[non_exhaustive]
24pub enum TypeKind {
25 NoType,
27 Boolean,
29 Byte,
31 Int8,
33 UInt8,
35 Int16,
37 UInt16,
39 Int32,
41 UInt32,
43 Int64,
45 UInt64,
47 Float32,
49 Float64,
51 Float128,
53 Char8,
55 Char16,
57 String8,
59 String16,
61 Enumeration,
63 Bitmask,
65 Alias,
67 Array,
69 Sequence,
71 Map,
73 Structure,
75 Union,
77 Bitset,
79 Annotation,
81}
82
83impl TypeKind {
84 #[must_use]
87 pub const fn is_primitive(self) -> bool {
88 matches!(
89 self,
90 Self::Boolean
91 | Self::Byte
92 | Self::Int8
93 | Self::UInt8
94 | Self::Int16
95 | Self::UInt16
96 | Self::Int32
97 | Self::UInt32
98 | Self::Int64
99 | Self::UInt64
100 | Self::Float32
101 | Self::Float64
102 | Self::Float128
103 | Self::Char8
104 | Self::Char16
105 )
106 }
107
108 #[must_use]
111 pub const fn is_aggregable(self) -> bool {
112 matches!(
113 self,
114 Self::Structure
115 | Self::Union
116 | Self::Annotation
117 | Self::Bitset
118 | Self::Bitmask
119 | Self::Enumeration
120 )
121 }
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
126pub enum ExtensibilityKind {
127 Final,
129 Appendable,
131 Mutable,
133}
134
135impl Default for ExtensibilityKind {
136 fn default() -> Self {
137 Self::Appendable
138 }
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub enum TryConstructKind {
147 Discard,
149 UseDefault,
151 Trim,
153}
154
155impl Default for TryConstructKind {
156 fn default() -> Self {
157 Self::Discard
158 }
159}
160
161pub type MemberId = u32;
163
164#[derive(Debug, Clone, PartialEq, Eq)]
173pub struct TypeDescriptor {
174 pub kind: TypeKind,
176 pub name: String,
178 pub base_type: Option<Box<TypeDescriptor>>,
180 pub discriminator_type: Option<Box<TypeDescriptor>>,
182 pub bound: Vec<u32>,
185 pub element_type: Option<Box<TypeDescriptor>>,
187 pub key_element_type: Option<Box<TypeDescriptor>>,
189 pub extensibility_kind: ExtensibilityKind,
191 pub is_nested: bool,
193}
194
195impl TypeDescriptor {
196 #[must_use]
198 pub fn primitive(kind: TypeKind, name: impl Into<String>) -> Self {
199 Self {
200 kind,
201 name: name.into(),
202 base_type: None,
203 discriminator_type: None,
204 bound: Vec::new(),
205 element_type: None,
206 key_element_type: None,
207 extensibility_kind: ExtensibilityKind::default(),
208 is_nested: false,
209 }
210 }
211
212 #[must_use]
214 pub fn structure(name: impl Into<String>) -> Self {
215 Self {
216 kind: TypeKind::Structure,
217 name: name.into(),
218 base_type: None,
219 discriminator_type: None,
220 bound: Vec::new(),
221 element_type: None,
222 key_element_type: None,
223 extensibility_kind: ExtensibilityKind::default(),
224 is_nested: false,
225 }
226 }
227
228 #[must_use]
230 pub fn union(name: impl Into<String>, discriminator: TypeDescriptor) -> Self {
231 Self {
232 kind: TypeKind::Union,
233 name: name.into(),
234 base_type: None,
235 discriminator_type: Some(Box::new(discriminator)),
236 bound: Vec::new(),
237 element_type: None,
238 key_element_type: None,
239 extensibility_kind: ExtensibilityKind::default(),
240 is_nested: false,
241 }
242 }
243
244 #[must_use]
246 pub fn sequence(name: impl Into<String>, element: TypeDescriptor, max: u32) -> Self {
247 Self {
248 kind: TypeKind::Sequence,
249 name: name.into(),
250 base_type: None,
251 discriminator_type: None,
252 bound: alloc::vec![max],
253 element_type: Some(Box::new(element)),
254 key_element_type: None,
255 extensibility_kind: ExtensibilityKind::default(),
256 is_nested: false,
257 }
258 }
259
260 #[must_use]
262 pub fn array(name: impl Into<String>, element: TypeDescriptor, dims: Vec<u32>) -> Self {
263 Self {
264 kind: TypeKind::Array,
265 name: name.into(),
266 base_type: None,
267 discriminator_type: None,
268 bound: dims,
269 element_type: Some(Box::new(element)),
270 key_element_type: None,
271 extensibility_kind: ExtensibilityKind::default(),
272 is_nested: false,
273 }
274 }
275
276 #[must_use]
278 pub fn map(
279 name: impl Into<String>,
280 key: TypeDescriptor,
281 element: TypeDescriptor,
282 max: u32,
283 ) -> Self {
284 Self {
285 kind: TypeKind::Map,
286 name: name.into(),
287 base_type: None,
288 discriminator_type: None,
289 bound: alloc::vec![max],
290 element_type: Some(Box::new(element)),
291 key_element_type: Some(Box::new(key)),
292 extensibility_kind: ExtensibilityKind::default(),
293 is_nested: false,
294 }
295 }
296
297 #[must_use]
299 pub fn string8(bound: u32) -> Self {
300 Self {
301 kind: TypeKind::String8,
302 name: alloc::format!("string<{bound}>"),
303 base_type: None,
304 discriminator_type: None,
305 bound: alloc::vec![bound],
306 element_type: None,
307 key_element_type: None,
308 extensibility_kind: ExtensibilityKind::default(),
309 is_nested: false,
310 }
311 }
312
313 #[must_use]
315 pub fn string16(bound: u32) -> Self {
316 Self {
317 kind: TypeKind::String16,
318 name: alloc::format!("wstring<{bound}>"),
319 base_type: None,
320 discriminator_type: None,
321 bound: alloc::vec![bound],
322 element_type: None,
323 key_element_type: None,
324 extensibility_kind: ExtensibilityKind::default(),
325 is_nested: false,
326 }
327 }
328
329 #[must_use]
331 pub fn enumeration(name: impl Into<String>) -> Self {
332 Self {
333 kind: TypeKind::Enumeration,
334 name: name.into(),
335 base_type: None,
336 discriminator_type: None,
337 bound: Vec::new(),
338 element_type: None,
339 key_element_type: None,
340 extensibility_kind: ExtensibilityKind::default(),
341 is_nested: false,
342 }
343 }
344
345 pub fn is_consistent(&self) -> Result<(), String> {
354 if self.name.is_empty() && self.kind != TypeKind::NoType {
358 return Err(String::from("descriptor without name"));
359 }
360 match self.kind {
361 TypeKind::Union => {
362 let Some(d) = &self.discriminator_type else {
363 return Err(String::from("union without discriminator_type"));
364 };
365 if !is_valid_discriminator(d.kind) {
366 return Err(alloc::format!(
367 "union discriminator must be int/enum/bool, got {:?}",
368 d.kind
369 ));
370 }
371 }
372 TypeKind::Array => {
373 if self.bound.is_empty() {
374 return Err(String::from("array without dimensions"));
375 }
376 if self.bound.contains(&0) {
377 return Err(String::from("array dimension must be > 0"));
378 }
379 if self.element_type.is_none() {
380 return Err(String::from("array without element_type"));
381 }
382 }
383 TypeKind::Sequence | TypeKind::String8 | TypeKind::String16 => {
384 if self.bound.len() != 1 {
385 return Err(String::from("sequence/string needs exactly 1 bound"));
386 }
387 if matches!(self.kind, TypeKind::Sequence) && self.element_type.is_none() {
388 return Err(String::from("sequence without element_type"));
389 }
390 }
391 TypeKind::Map => {
392 if self.bound.len() != 1 {
393 return Err(String::from("map needs exactly 1 bound"));
394 }
395 if self.element_type.is_none() {
396 return Err(String::from("map without value element_type"));
397 }
398 if self.key_element_type.is_none() {
399 return Err(String::from("map without key_element_type"));
400 }
401 }
402 _ => {}
403 }
404 if let Some(b) = &self.base_type {
407 if b.name == self.name && !self.name.is_empty() {
408 return Err(String::from("inheritance cycle: base_type == self"));
409 }
410 }
411 Ok(())
412 }
413}
414
415#[derive(Debug, Clone, PartialEq, Eq)]
421pub struct MemberDescriptor {
422 pub name: String,
424 pub id: MemberId,
426 pub member_type: Box<TypeDescriptor>,
428 pub default_value: Option<String>,
430 pub index: u32,
432 pub label: Vec<i64>,
434 pub try_construct: TryConstructKind,
436 pub is_key: bool,
438 pub is_optional: bool,
440 pub is_must_understand: bool,
442 pub is_shared: bool,
444 pub is_default_label: bool,
446 pub bit_bound: Option<u8>,
450}
451
452impl MemberDescriptor {
453 #[must_use]
456 pub fn new(name: impl Into<String>, id: MemberId, ty: TypeDescriptor) -> Self {
457 Self {
458 name: name.into(),
459 id,
460 member_type: Box::new(ty),
461 default_value: None,
462 index: 0,
463 label: Vec::new(),
464 try_construct: TryConstructKind::default(),
465 is_key: false,
466 is_optional: false,
467 is_must_understand: false,
468 is_shared: false,
469 is_default_label: false,
470 bit_bound: None,
471 }
472 }
473
474 pub fn is_consistent(&self) -> Result<(), String> {
479 if self.name.is_empty() {
480 return Err(String::from("member without name"));
481 }
482 self.member_type.is_consistent()?;
483 if self.is_default_label && !self.label.is_empty() {
484 return Err(String::from(
485 "member with is_default_label must not have explicit labels",
486 ));
487 }
488 Ok(())
489 }
490}
491
492const fn is_valid_discriminator(kind: TypeKind) -> bool {
495 matches!(
496 kind,
497 TypeKind::Boolean
498 | TypeKind::Byte
499 | TypeKind::Int8
500 | TypeKind::UInt8
501 | TypeKind::Int16
502 | TypeKind::UInt16
503 | TypeKind::Int32
504 | TypeKind::UInt32
505 | TypeKind::Int64
506 | TypeKind::UInt64
507 | TypeKind::Char8
508 | TypeKind::Char16
509 | TypeKind::Enumeration
510 )
511}
512
513#[cfg(test)]
514#[allow(clippy::unwrap_used)]
515mod tests {
516 use super::*;
517
518 #[test]
519 fn type_kind_primitive_set_matches_spec_table_10() {
520 for k in [
521 TypeKind::Boolean,
522 TypeKind::Byte,
523 TypeKind::Int8,
524 TypeKind::UInt8,
525 TypeKind::Int16,
526 TypeKind::UInt16,
527 TypeKind::Int32,
528 TypeKind::UInt32,
529 TypeKind::Int64,
530 TypeKind::UInt64,
531 TypeKind::Float32,
532 TypeKind::Float64,
533 TypeKind::Float128,
534 TypeKind::Char8,
535 TypeKind::Char16,
536 ] {
537 assert!(k.is_primitive(), "{k:?} should be primitive");
538 }
539 for k in [
540 TypeKind::Structure,
541 TypeKind::Union,
542 TypeKind::Sequence,
543 TypeKind::Array,
544 TypeKind::Map,
545 TypeKind::String8,
546 TypeKind::String16,
547 TypeKind::Alias,
548 ] {
549 assert!(!k.is_primitive(), "{k:?} should not be primitive");
550 }
551 }
552
553 #[test]
554 fn type_kind_aggregable_set() {
555 assert!(TypeKind::Structure.is_aggregable());
556 assert!(TypeKind::Union.is_aggregable());
557 assert!(TypeKind::Annotation.is_aggregable());
558 assert!(TypeKind::Bitset.is_aggregable());
559 assert!(TypeKind::Bitmask.is_aggregable());
560 assert!(TypeKind::Enumeration.is_aggregable());
561 assert!(!TypeKind::Int32.is_aggregable());
562 assert!(!TypeKind::Sequence.is_aggregable());
563 }
564
565 #[test]
566 fn descriptor_struct_passes_consistency() {
567 let s = TypeDescriptor::structure("::Foo");
568 assert!(s.is_consistent().is_ok());
569 }
570
571 #[test]
572 fn descriptor_union_without_discriminator_fails() {
573 let mut u = TypeDescriptor::structure("::U");
574 u.kind = TypeKind::Union;
575 let err = u.is_consistent().unwrap_err();
576 assert!(err.contains("discriminator"));
577 }
578
579 #[test]
580 fn descriptor_union_with_invalid_discriminator_fails() {
581 let bad_disc = TypeDescriptor::structure("::S");
582 let u = TypeDescriptor::union("::U", bad_disc);
583 let err = u.is_consistent().unwrap_err();
584 assert!(err.contains("discriminator"));
585 }
586
587 #[test]
588 fn descriptor_array_without_dims_fails() {
589 let mut a = TypeDescriptor::array(
590 "::A",
591 TypeDescriptor::primitive(TypeKind::Int32, "int32"),
592 alloc::vec![3, 3],
593 );
594 a.bound.clear();
595 let err = a.is_consistent().unwrap_err();
596 assert!(err.contains("dimensions"));
597 }
598
599 #[test]
600 fn descriptor_array_with_zero_dim_fails() {
601 let a = TypeDescriptor::array(
602 "::A",
603 TypeDescriptor::primitive(TypeKind::Int32, "int32"),
604 alloc::vec![3, 0, 4],
605 );
606 let err = a.is_consistent().unwrap_err();
607 assert!(err.contains("> 0"));
608 }
609
610 #[test]
611 fn descriptor_sequence_with_element_passes() {
612 let s = TypeDescriptor::sequence(
613 "::S",
614 TypeDescriptor::primitive(TypeKind::Int32, "int32"),
615 100,
616 );
617 assert!(s.is_consistent().is_ok());
618 }
619
620 #[test]
621 fn descriptor_map_requires_both_key_and_value() {
622 let mut m = TypeDescriptor::map(
623 "::M",
624 TypeDescriptor::string8(64),
625 TypeDescriptor::primitive(TypeKind::Int64, "int64"),
626 500,
627 );
628 assert!(m.is_consistent().is_ok());
629 m.key_element_type = None;
630 assert!(m.is_consistent().is_err());
631 }
632
633 #[test]
634 fn descriptor_inheritance_cycle_self_reference_rejected() {
635 let mut s = TypeDescriptor::structure("::Foo");
636 let cycle = TypeDescriptor::structure("::Foo");
637 s.base_type = Some(Box::new(cycle));
638 let err = s.is_consistent().unwrap_err();
639 assert!(err.contains("cycle"));
640 }
641
642 #[test]
643 fn member_descriptor_default_label_with_labels_rejected() {
644 let mut m =
645 MemberDescriptor::new("x", 1, TypeDescriptor::primitive(TypeKind::Int32, "int32"));
646 m.is_default_label = true;
647 m.label = alloc::vec![0];
648 let err = m.is_consistent().unwrap_err();
649 assert!(err.contains("default_label"));
650 }
651
652 #[test]
653 fn member_descriptor_empty_name_rejected() {
654 let m = MemberDescriptor::new("", 1, TypeDescriptor::primitive(TypeKind::Int32, "int32"));
655 assert!(m.is_consistent().is_err());
656 }
657
658 #[test]
659 fn try_construct_default_is_discard() {
660 assert_eq!(TryConstructKind::default(), TryConstructKind::Discard);
661 }
662
663 #[test]
664 fn extensibility_default_is_appendable() {
665 assert_eq!(ExtensibilityKind::default(), ExtensibilityKind::Appendable);
666 }
667}