llmcc_rust/descriptor/
structure.rs

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/// High level classification for a struct declaration.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum StructKind {
10    Named,
11    Tuple,
12    Unit,
13}
14
15/// Structured metadata for Rust structs.
16#[derive(Debug, Clone)]
17pub struct StructDescriptor {
18    pub hir_id: HirId,
19    pub name: String,
20    pub fqn: String,
21    pub visibility: FnVisibility,
22    pub generics: Option<String>,
23    pub fields: Vec<StructField>,
24    pub kind: StructKind,
25}
26
27/// Field metadata for named or tuple structs.
28#[derive(Debug, Clone)]
29pub struct StructField {
30    pub name: Option<String>,
31    pub ty: Option<TypeExpr>,
32}
33
34impl StructDescriptor {
35    pub fn from_struct<'tcx>(
36        unit: CompileUnit<'tcx>,
37        node: &HirNode<'tcx>,
38        fqn: String,
39    ) -> Option<Self> {
40        let ts_node = match node.inner_ts_node() {
41            ts if ts.kind() == "struct_item" => ts,
42            _ => return None,
43        };
44
45        #[cfg(test)]
46        eprintln!(
47            "struct_item fields? {} tuple? {} sexp: {}",
48            ts_node.child_by_field_name("fields").is_some(),
49            ts_node.child_by_field_name("body").is_some(),
50            ts_node.to_sexp()
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 (fields, kind) = parse_struct_fields(unit, ts_node);
65
66        Some(StructDescriptor {
67            hir_id: node.hir_id(),
68            name,
69            fqn,
70            visibility,
71            generics,
72            fields,
73            kind,
74        })
75    }
76}
77
78fn parse_struct_fields<'tcx>(
79    unit: CompileUnit<'tcx>,
80    node: Node<'tcx>,
81) -> (Vec<StructField>, StructKind) {
82    match node.kind() {
83        "field_declaration_list" => return (parse_named_fields(unit, node), StructKind::Named),
84        "tuple_field_declaration_list" | "ordered_field_declaration_list" => {
85            return (parse_tuple_fields(unit, node), StructKind::Tuple)
86        }
87        _ => {}
88    }
89
90    let mut named = Vec::new();
91    let mut tuple = Vec::new();
92    let child_count = node.child_count();
93    for i in 0..child_count {
94        if let Some(child) = node.child(i) {
95            match child.kind() {
96                "field_declaration_list" => {
97                    return (parse_named_fields(unit, child), StructKind::Named)
98                }
99                "tuple_field_declaration_list" | "ordered_field_declaration_list" => {
100                    return (parse_tuple_fields(unit, child), StructKind::Tuple)
101                }
102                "field_declaration" => named.push(parse_named_field_node(unit, child)),
103                "tuple_field_declaration" | "ordered_field_declaration" => {
104                    tuple.push(parse_tuple_field_node(unit, child))
105                }
106                _ => {
107                    let (fields, kind) = parse_struct_fields(unit, child);
108                    match kind {
109                        StructKind::Named if !fields.is_empty() => {
110                            return (fields, StructKind::Named)
111                        }
112                        StructKind::Tuple if !fields.is_empty() => {
113                            return (fields, StructKind::Tuple)
114                        }
115                        _ => {}
116                    }
117                }
118            }
119        }
120    }
121
122    if !named.is_empty() {
123        return (named, StructKind::Named);
124    }
125    if !tuple.is_empty() {
126        return (tuple, StructKind::Tuple);
127    }
128
129    (Vec::new(), StructKind::Unit)
130}
131
132fn parse_named_fields<'tcx>(unit: CompileUnit<'tcx>, list: Node<'tcx>) -> Vec<StructField> {
133    let mut fields = Vec::new();
134    let mut cursor = list.walk();
135    for child in list.named_children(&mut cursor) {
136        if child.kind() == "field_declaration" {
137            let name = child
138                .child_by_field_name("name")
139                .map(|n| clean(&node_text(unit, n)));
140            let ty = child
141                .child_by_field_name("type")
142                .map(|n| parse_type_expr(unit, n));
143            fields.push(StructField { name, ty });
144        }
145    }
146    fields
147}
148
149fn parse_tuple_fields<'tcx>(unit: CompileUnit<'tcx>, list: Node<'tcx>) -> Vec<StructField> {
150    let mut fields = Vec::new();
151    let mut cursor = list.walk();
152    for child in list.children(&mut cursor) {
153        match child.kind() {
154            "tuple_field_declaration" | "ordered_field_declaration" => {
155                let ty = child
156                    .child_by_field_name("type")
157                    .map(|n| parse_type_expr(unit, n))
158                    .or_else(|| {
159                        // ordered_field_declaration may expose type nodes directly
160                        child
161                            .children(&mut child.walk())
162                            .find_map(|n| match n.kind() {
163                                "type_identifier"
164                                | "primitive_type"
165                                | "scoped_type_identifier"
166                                | "generic_type"
167                                | "tuple_type"
168                                | "reference_type"
169                                | "impl_trait_type" => Some(parse_type_expr(unit, n)),
170                                _ => None,
171                            })
172                    });
173                fields.push(StructField { name: None, ty });
174            }
175            kind if is_type_kind(kind) => {
176                fields.push(StructField {
177                    name: None,
178                    ty: Some(parse_type_expr(unit, child)),
179                });
180            }
181            _ => {}
182        }
183    }
184    fields
185}
186
187fn is_type_kind(kind: &str) -> bool {
188    matches!(
189        kind,
190        "type_identifier"
191            | "primitive_type"
192            | "scoped_type_identifier"
193            | "generic_type"
194            | "tuple_type"
195            | "reference_type"
196            | "impl_trait_type"
197    )
198}
199
200fn node_text<'tcx>(unit: CompileUnit<'tcx>, node: Node<'tcx>) -> String {
201    unit.file().get_text(node.start_byte(), node.end_byte())
202}
203
204fn clean(text: &str) -> String {
205    let mut out = String::new();
206    let mut last_was_ws = false;
207    for ch in text.chars() {
208        if ch.is_whitespace() {
209            if !last_was_ws && !out.is_empty() {
210                out.push(' ');
211            }
212            last_was_ws = true;
213        } else {
214            out.push(ch);
215            last_was_ws = false;
216        }
217    }
218    out.trim().to_string()
219}
220
221fn parse_named_field_node<'tcx>(unit: CompileUnit<'tcx>, node: Node<'tcx>) -> StructField {
222    let name = node
223        .child_by_field_name("name")
224        .map(|n| clean(&node_text(unit, n)));
225    let ty = node
226        .child_by_field_name("type")
227        .map(|n| parse_type_expr(unit, n));
228    StructField { name, ty }
229}
230
231fn parse_tuple_field_node<'tcx>(unit: CompileUnit<'tcx>, node: Node<'tcx>) -> StructField {
232    let ty = node
233        .child_by_field_name("type")
234        .map(|n| parse_type_expr(unit, n));
235    StructField { name: None, ty }
236}