1use clap::ValueEnum;
2use serde::{Serialize, Serializer, ser::SerializeMap};
3
4use crate::model::{
5 Comment, Expression, FunctionDeclItem, FunctionDefItem, GObjectType, SourceLocation, Statement,
6 TypeDefItem, VariableDecl,
7};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, ValueEnum)]
12#[serde(rename_all = "snake_case")]
13pub enum TopLevelItemKind {
14 FunctionDefinition,
15 FunctionDeclaration,
16 Typedef,
17 Struct,
18 Enum,
19 Include,
20 Define,
21 GObjectType,
22 Conditional,
23 GObjectDeclsBlock,
24 Declaration,
25 Expression,
26 Other,
27}
28
29#[derive(Debug, Clone)]
31pub enum TopLevelItem {
32 Preprocessor(PreprocessorDirective),
34 TypeDefinition(TypeDefItem),
36 FunctionDeclaration(FunctionDeclItem),
38 FunctionDefinition(FunctionDefItem),
40 Declaration(Box<VariableDecl>),
42 Expression(Box<Expression>),
44 Comment(Comment),
46}
47
48impl TopLevelItem {
49 pub fn name(&self) -> Option<&str> {
51 match self {
52 Self::FunctionDefinition(f) => Some(&f.name),
53 Self::FunctionDeclaration(f) => Some(&f.name),
54 Self::TypeDefinition(td) => match td {
55 TypeDefItem::Typedef { name, .. } | TypeDefItem::Struct { name, .. } => Some(name),
56 TypeDefItem::Enum(enum_info) => enum_info.name.as_deref(),
57 },
58 Self::Preprocessor(PreprocessorDirective::Include { path, .. }) => Some(path),
59 Self::Preprocessor(PreprocessorDirective::Define { name, .. }) => Some(name),
60 Self::Preprocessor(PreprocessorDirective::GObjectType(gobject_type)) => {
61 Some(&gobject_type.type_name)
62 }
63 Self::Declaration(decl) => Some(&decl.name),
64 Self::Comment(_) => None,
65 _ => None,
66 }
67 }
68
69 pub fn kind(&self) -> TopLevelItemKind {
71 match self {
72 Self::FunctionDefinition(_) => TopLevelItemKind::FunctionDefinition,
73 Self::FunctionDeclaration(_) => TopLevelItemKind::FunctionDeclaration,
74 Self::TypeDefinition(TypeDefItem::Typedef { .. }) => TopLevelItemKind::Typedef,
75 Self::TypeDefinition(TypeDefItem::Struct { .. }) => TopLevelItemKind::Struct,
76 Self::TypeDefinition(TypeDefItem::Enum(_)) => TopLevelItemKind::Enum,
77 Self::Preprocessor(PreprocessorDirective::Include { .. }) => TopLevelItemKind::Include,
78 Self::Preprocessor(PreprocessorDirective::Define { .. }) => TopLevelItemKind::Define,
79 Self::Preprocessor(PreprocessorDirective::GObjectType { .. }) => {
80 TopLevelItemKind::GObjectType
81 }
82 Self::Preprocessor(PreprocessorDirective::Conditional { .. }) => {
83 TopLevelItemKind::Conditional
84 }
85 Self::Preprocessor(PreprocessorDirective::GObjectDeclsBlock { .. }) => {
86 TopLevelItemKind::GObjectDeclsBlock
87 }
88 Self::Declaration(_) => TopLevelItemKind::Declaration,
89 Self::Expression(_) => TopLevelItemKind::Expression,
90 Self::Comment(_) => TopLevelItemKind::Other,
91 Self::Preprocessor(_) => TopLevelItemKind::Other,
92 }
93 }
94
95 pub fn has_doc(&self) -> bool {
96 match self {
97 Self::FunctionDefinition(f) => f.doc.is_some(),
98 Self::FunctionDeclaration(f) => f.doc.is_some(),
99 Self::TypeDefinition(td) => match td {
100 TypeDefItem::Struct { doc, .. } | TypeDefItem::Typedef { doc, .. } => doc.is_some(),
101 TypeDefItem::Enum(e) => e.doc.is_some(),
102 },
103 _ => false,
104 }
105 }
106
107 pub(crate) fn is_comment_at_byte(&self, byte: usize) -> bool {
108 matches!(self, Self::Comment(c) if c.location.start_byte == byte)
109 }
110}
111
112impl Serialize for TopLevelItem {
113 fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
114 match self {
115 Self::Preprocessor(directive) => directive.serialize(s),
116 Self::TypeDefinition(v) => {
117 let mut m = s.serialize_map(Some(1))?;
118 m.serialize_entry("type_definition", v)?;
119 m.end()
120 }
121 Self::FunctionDeclaration(v) => {
122 let mut m = s.serialize_map(Some(1))?;
123 m.serialize_entry("function_declaration", v)?;
124 m.end()
125 }
126 Self::FunctionDefinition(v) => {
127 let mut m = s.serialize_map(Some(1))?;
128 m.serialize_entry("function_definition", v)?;
129 m.end()
130 }
131 Self::Declaration(v) => {
132 let mut m = s.serialize_map(Some(1))?;
133 m.serialize_entry("declaration", v)?;
134 m.end()
135 }
136 Self::Expression(v) => {
137 let mut m = s.serialize_map(Some(1))?;
138 m.serialize_entry("expression", v)?;
139 m.end()
140 }
141 Self::Comment(v) => {
142 let mut m = s.serialize_map(Some(1))?;
143 m.serialize_entry("comment", v)?;
144 m.end()
145 }
146 }
147 }
148}
149
150#[derive(Debug, Clone, Serialize)]
151#[serde(rename_all = "snake_case")]
152pub enum PragmaKind {
153 Once,
155 DiagnosticPush,
157 DiagnosticPop,
159 DiagnosticIgnored { warning: String },
161 Other {
163 name: String,
164 arguments: Option<String>,
165 },
166}
167
168#[derive(Debug, Clone, Serialize)]
169#[serde(rename_all = "snake_case")]
170pub enum PreprocessorDirective {
171 Include {
172 path: String,
173 is_system: bool,
174 location: SourceLocation,
175 },
176 Define {
177 name: String,
178 value: Option<DefineValue>,
179 location: SourceLocation,
180 },
181 Call {
182 directive: String,
183 location: SourceLocation,
184 },
185 Pragma {
186 kind: PragmaKind,
187 location: SourceLocation,
188 },
189 GObjectType(Box<GObjectType>),
191 AutoptrCleanupFunc {
193 type_name: String,
194 cleanup_function: String,
195 location: SourceLocation,
196 },
197 AutoCleanupClearFunc {
199 type_name: String,
200 cleanup_function: String,
201 location: SourceLocation,
202 },
203 MacroWithCode {
206 macro_name: String,
207 arguments: Vec<String>,
208 code_statements: Vec<Statement>,
209 location: SourceLocation,
210 },
211 Conditional {
212 kind: ConditionalKind,
213 condition: Option<String>,
214 body: Vec<TopLevelItem>,
215 location: SourceLocation,
216 },
217 GObjectDeclsBlock {
219 body: Vec<TopLevelItem>,
220 location: SourceLocation,
221 },
222}
223
224impl PreprocessorDirective {
225 pub fn location(&self) -> &SourceLocation {
226 match self {
227 Self::Include { location, .. }
228 | Self::Define { location, .. }
229 | Self::Call { location, .. }
230 | Self::Pragma { location, .. }
231 | Self::AutoptrCleanupFunc { location, .. }
232 | Self::AutoCleanupClearFunc { location, .. }
233 | Self::MacroWithCode { location, .. }
234 | Self::Conditional { location, .. }
235 | Self::GObjectDeclsBlock { location, .. } => location,
236 Self::GObjectType(gt) => >.location,
237 }
238 }
239}
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
242#[serde(rename_all = "snake_case")]
243pub enum ConditionalKind {
244 Ifdef,
245 Ifndef,
246 If,
247 Elif,
248 Else,
249}
250
251#[derive(Debug, Clone, Serialize)]
252#[serde(rename_all = "snake_case")]
253pub enum DefineValue {
254 StringLiteral(String),
255 Number(String),
256 Identifier(String),
257 Expression(String),
258}
259
260impl DefineValue {
261 pub fn parse(raw: &str) -> Self {
262 let trimmed = raw.trim();
263 if trimmed.starts_with('"') && trimmed.ends_with('"') {
264 return Self::StringLiteral(trimmed[1..trimmed.len() - 1].to_owned());
265 }
266 if trimmed
267 .bytes()
268 .all(|b| b.is_ascii_digit() || b == b'x' || b == b'X' || b == b'-')
269 && !trimmed.is_empty()
270 {
271 return Self::Number(trimmed.to_owned());
272 }
273 if trimmed
274 .bytes()
275 .all(|b| b.is_ascii_alphanumeric() || b == b'_')
276 && !trimmed.is_empty()
277 {
278 return Self::Identifier(trimmed.to_owned());
279 }
280 Self::Expression(trimmed.to_owned())
281 }
282
283 pub fn as_raw_str(&self) -> &str {
284 match self {
285 Self::StringLiteral(s)
286 | Self::Number(s)
287 | Self::Identifier(s)
288 | Self::Expression(s) => s,
289 }
290 }
291}