Skip to main content

type_crawler/types/
union_decl.rs

1use 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}