type_crawler/types/
union_decl.rs1use std::fmt::Display;
2
3use crate::{
4 Env, Field, Types,
5 error::{
6 AlignofSnafu, InvalidAstSnafu, InvalidFieldsSnafu, ParseError, SizeofSnafu,
7 UnsupportedEntitySnafu, UnsupportedTypeSnafu,
8 },
9};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub struct UnionDecl {
13 pub(crate) name: Option<String>,
14 fields: Vec<Field>,
15 size: usize,
16 alignment: usize,
17}
18
19impl UnionDecl {
20 pub fn new(
21 env: &Env,
22 types: &Types,
23 name: Option<String>,
24 ty: clang::Type,
25 ) -> Result<Self, ParseError> {
26 if ty.get_kind() != clang::TypeKind::Record {
27 return InvalidAstSnafu { message: format!("Expected Record, found: {ty:?}") }.fail();
28 }
29
30 let display_name = name.as_deref().unwrap_or("<anon>");
31
32 let record_fields = ty.get_fields().ok_or_else(|| {
33 UnsupportedTypeSnafu { message: format!("Record type without fields: {ty:?}") }.build()
34 })?;
35 if record_fields.is_empty() {
36 let declaration = ty.get_declaration().ok_or_else(|| {
37 InvalidAstSnafu { message: format!("Record type without declaration: {ty:?}") }
38 .build()
39 })?;
40
41 let decl_children = declaration.get_children();
42 let invalid_fields = decl_children
43 .iter()
44 .enumerate()
45 .filter(|(_, c)| {
46 c.get_kind() == clang::EntityKind::FieldDecl && c.is_invalid_declaration()
47 })
48 .collect::<Vec<_>>();
49 if !invalid_fields.is_empty() {
50 return InvalidFieldsSnafu {
51 field_names: invalid_fields
52 .iter()
53 .map(|(i, c)| c.get_name().unwrap_or_else(|| format!("<index#{i}>")))
54 .collect::<Vec<_>>(),
55 struct_name: display_name.to_string(),
56 }
57 .fail();
58 }
59 }
60
61 let mut fields = Vec::new();
62 for field in &record_fields {
63 match field.get_kind() {
64 clang::EntityKind::FieldDecl => {
65 fields.push(Field::new(env, types, field)?);
66 }
67 _ => {
68 return UnsupportedEntitySnafu {
69 at: format!("union {display_name}"),
70 message: format!(
71 "Unsupported entity kind in union: {:?}",
72 field.get_kind()
73 ),
74 }
75 .fail();
76 }
77 }
78 }
79
80 let size = ty.get_sizeof().or_else(|e| {
81 if record_fields.is_empty() {
82 Ok(1)
83 } else {
84 SizeofSnafu { type_name: display_name.to_string(), error: e }.fail()
85 }
86 })?;
87 let alignment = ty.get_alignof().or_else(|e| {
88 if record_fields.is_empty() {
89 Ok(1)
90 } else {
91 AlignofSnafu { type_name: display_name.to_string(), error: e }.fail()
92 }
93 })?;
94
95 Ok(UnionDecl { name, fields, size, alignment })
96 }
97
98 pub fn size(&self) -> usize {
99 self.size
100 }
101
102 pub fn alignment(&self) -> usize {
103 self.alignment
104 }
105
106 pub fn name(&self) -> Option<&str> {
107 self.name.as_deref()
108 }
109
110 pub fn fields(&self) -> &[Field] {
111 &self.fields
112 }
113
114 pub fn get_field(&self, name: &str) -> Option<&Field> {
115 self.fields.iter().find(|f| f.name() == Some(name))
116 }
117}
118
119impl Display for UnionDecl {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121 writeln!(f, "{} {{", self.name.as_deref().unwrap_or("<anon>"))?;
122 for field in &self.fields {
123 writeln!(f, " {field}")?;
124 }
125 write!(f, "}}")?;
126 Ok(())
127 }
128}