1use llmcc_core::context::CompileUnit;
2use llmcc_core::ir::{HirId, HirNode};
3use tree_sitter::Node;
4
5use super::function::{parse_type_expr, FnVisibility, TypeExpr};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum EnumVariantKind {
10 Unit,
11 Tuple,
12 Struct,
13}
14
15#[derive(Debug, Clone)]
17pub struct EnumVariant {
18 pub name: String,
19 pub kind: EnumVariantKind,
20 pub fields: Vec<EnumVariantField>,
21 pub discriminant: Option<String>,
22}
23
24#[derive(Debug, Clone)]
26pub struct EnumVariantField {
27 pub name: Option<String>,
28 pub ty: Option<TypeExpr>,
29}
30
31#[derive(Debug, Clone)]
33pub struct EnumDescriptor {
34 pub hir_id: HirId,
35 pub name: String,
36 pub fqn: String,
37 pub visibility: FnVisibility,
38 pub generics: Option<String>,
39 pub variants: Vec<EnumVariant>,
40}
41
42impl EnumDescriptor {
43 pub fn from_enum<'tcx>(
44 unit: CompileUnit<'tcx>,
45 node: &HirNode<'tcx>,
46 fqn: String,
47 ) -> Option<Self> {
48 let ts_node = match node.inner_ts_node() {
49 ts if ts.kind() == "enum_item" => ts,
50 _ => return None,
51 };
52
53 let name_node = ts_node.child_by_field_name("name")?;
54 let name = clean(&node_text(unit, name_node));
55 let header_text = unit
56 .file()
57 .get_text(ts_node.start_byte(), name_node.start_byte());
58 let visibility = FnVisibility::from_header(&header_text);
59
60 let generics = ts_node
61 .child_by_field_name("type_parameters")
62 .map(|n| clean(&node_text(unit, n)));
63
64 let variants = ts_node
65 .child_by_field_name("body")
66 .map(|body| parse_enum_variants(unit, body))
67 .unwrap_or_default();
68
69 Some(EnumDescriptor {
70 hir_id: node.hir_id(),
71 name,
72 fqn,
73 visibility,
74 generics,
75 variants,
76 })
77 }
78}
79
80fn parse_enum_variants<'tcx>(unit: CompileUnit<'tcx>, body: Node<'tcx>) -> Vec<EnumVariant> {
81 let mut variants = Vec::new();
82 let mut cursor = body.walk();
83 for child in body.named_children(&mut cursor) {
84 if child.kind() == "enum_variant" {
85 variants.push(parse_enum_variant(unit, child));
86 }
87 }
88 variants
89}
90
91fn parse_enum_variant<'tcx>(unit: CompileUnit<'tcx>, node: Node<'tcx>) -> EnumVariant {
92 let name_node = node
93 .child_by_field_name("name")
94 .unwrap_or_else(|| node.child(0).unwrap_or(node));
95 let name = clean(&node_text(unit, name_node));
96
97 let discriminant = node
98 .child_by_field_name("value")
99 .map(|n| clean(&node_text(unit, n)));
100
101 let (kind, fields) = match node.child_by_field_name("body") {
102 Some(body) => match body.kind() {
103 "field_declaration_list" => (
104 EnumVariantKind::Struct,
105 parse_named_variant_fields(unit, body),
106 ),
107 "ordered_field_declaration_list" | "tuple_field_declaration_list" => (
108 EnumVariantKind::Tuple,
109 parse_tuple_variant_fields(unit, body),
110 ),
111 other => parse_variant_body(unit, body, other),
112 },
113 None => (EnumVariantKind::Unit, Vec::new()),
114 };
115
116 EnumVariant {
117 name,
118 kind,
119 fields,
120 discriminant,
121 }
122}
123
124fn parse_variant_body<'tcx>(
125 unit: CompileUnit<'tcx>,
126 body: Node<'tcx>,
127 kind: &str,
128) -> (EnumVariantKind, Vec<EnumVariantField>) {
129 match kind {
130 "field_declaration_list" => (
131 EnumVariantKind::Struct,
132 parse_named_variant_fields(unit, body),
133 ),
134 "ordered_field_declaration_list" | "tuple_field_declaration_list" => (
135 EnumVariantKind::Tuple,
136 parse_tuple_variant_fields(unit, body),
137 ),
138 _ => (EnumVariantKind::Unit, Vec::new()),
139 }
140}
141
142fn parse_named_variant_fields<'tcx>(
143 unit: CompileUnit<'tcx>,
144 list: Node<'tcx>,
145) -> Vec<EnumVariantField> {
146 let mut fields = Vec::new();
147 let mut cursor = list.walk();
148 for child in list.named_children(&mut cursor) {
149 if child.kind() == "field_declaration" {
150 let name = child
151 .child_by_field_name("name")
152 .map(|n| clean(&node_text(unit, n)));
153 let ty = child
154 .child_by_field_name("type")
155 .map(|n| parse_type_expr(unit, n));
156 fields.push(EnumVariantField { name, ty });
157 }
158 }
159 fields
160}
161
162fn parse_tuple_variant_fields<'tcx>(
163 unit: CompileUnit<'tcx>,
164 list: Node<'tcx>,
165) -> Vec<EnumVariantField> {
166 let mut fields = Vec::new();
167 let mut cursor = list.walk();
168 for child in list.children(&mut cursor) {
169 match child.kind() {
170 "tuple_field_declaration" | "ordered_field_declaration" => {
171 let ty = child
172 .child_by_field_name("type")
173 .map(|n| parse_type_expr(unit, n))
174 .or_else(|| {
175 child
176 .children(&mut child.walk())
177 .find_map(|n| is_type_node(n.kind()).then(|| parse_type_expr(unit, n)))
178 });
179 fields.push(EnumVariantField { name: None, ty });
180 }
181 kind if is_type_node(kind) => {
182 fields.push(EnumVariantField {
183 name: None,
184 ty: Some(parse_type_expr(unit, child)),
185 });
186 }
187 _ => {}
188 }
189 }
190 fields
191}
192
193fn is_type_node(kind: &str) -> bool {
194 matches!(
195 kind,
196 "type_identifier"
197 | "primitive_type"
198 | "scoped_type_identifier"
199 | "generic_type"
200 | "tuple_type"
201 | "reference_type"
202 | "impl_trait_type"
203 )
204}
205
206fn node_text<'tcx>(unit: CompileUnit<'tcx>, node: Node<'tcx>) -> String {
207 unit.file().get_text(node.start_byte(), node.end_byte())
208}
209
210fn clean(text: &str) -> String {
211 let mut out = String::new();
212 let mut last_was_ws = false;
213 for ch in text.chars() {
214 if ch.is_whitespace() {
215 if !last_was_ws && !out.is_empty() {
216 out.push(' ');
217 }
218 last_was_ws = true;
219 } else {
220 out.push(ch);
221 last_was_ws = false;
222 }
223 }
224 out.trim().to_string()
225}