ryo_analysis/query/
derive_index.rs1use super::{CodeGraphV2, TypeFlowGraphV2};
7use crate::ast::ASTRegistry;
8use crate::symbol::SymbolRegistry;
9use crate::SymbolId;
10use ryo_source::pure::{PureAttrMeta, PureItem};
11use serde::Serialize;
12use slotmap::SecondaryMap;
13use smallvec::SmallVec;
14
15#[derive(Clone, Default, Debug, Serialize)]
26pub struct DeriveIndex {
27 symbol_derives: SecondaryMap<SymbolId, SmallVec<[String; 4]>>,
30
31 field_type_names: SecondaryMap<SymbolId, SmallVec<[String; 8]>>,
34}
35
36impl DeriveIndex {
37 pub fn new() -> Self {
39 Self::default()
40 }
41
42 pub fn build(
44 ast_registry: &ASTRegistry,
45 code_graph: &CodeGraphV2,
46 typeflow: &TypeFlowGraphV2,
47 symbol_registry: &SymbolRegistry,
48 ) -> Self {
49 let mut index = Self::new();
50 index.rebuild_all(ast_registry, code_graph, typeflow, symbol_registry);
51 index
52 }
53
54 pub fn rebuild_all(
56 &mut self,
57 ast_registry: &ASTRegistry,
58 code_graph: &CodeGraphV2,
59 typeflow: &TypeFlowGraphV2,
60 symbol_registry: &SymbolRegistry,
61 ) {
62 self.symbol_derives.clear();
63 self.field_type_names.clear();
64
65 for (id, item) in ast_registry.iter() {
66 self.index_item(id, item, code_graph, typeflow, symbol_registry);
67 }
68 }
69
70 pub fn rebuild_for_symbols(
75 &mut self,
76 symbols: &[SymbolId],
77 ast_registry: &ASTRegistry,
78 code_graph: &CodeGraphV2,
79 typeflow: &TypeFlowGraphV2,
80 symbol_registry: &SymbolRegistry,
81 ) {
82 for &symbol_id in symbols {
83 self.symbol_derives.remove(symbol_id);
85 self.field_type_names.remove(symbol_id);
86
87 if let Some(item) = ast_registry.get(symbol_id) {
89 self.index_item(symbol_id, item, code_graph, typeflow, symbol_registry);
90 }
91 }
92 }
93
94 fn index_item(
96 &mut self,
97 id: SymbolId,
98 item: &PureItem,
99 code_graph: &CodeGraphV2,
100 typeflow: &TypeFlowGraphV2,
101 symbol_registry: &SymbolRegistry,
102 ) {
103 let attrs = match item {
104 PureItem::Struct(s) => &s.attrs,
105 PureItem::Enum(e) => &e.attrs,
106 _ => return,
107 };
108
109 let mut derives: SmallVec<[String; 4]> = SmallVec::new();
111 for attr in attrs {
112 if attr.path == "derive" {
113 if let PureAttrMeta::List(args) = &attr.meta {
114 for trait_name in args.split(',').map(|s| s.trim()) {
115 if !trait_name.is_empty() {
116 derives.push(trait_name.to_string());
117 }
118 }
119 }
120 }
121 }
122
123 if !derives.is_empty() {
124 self.symbol_derives.insert(id, derives);
125 }
126
127 let mut field_types: SmallVec<[String; 8]> = SmallVec::new();
129 for child_id in code_graph.children_of(id) {
130 for use_id in typeflow.types_used_by(child_id) {
132 if let Some(path) = symbol_registry.resolve(use_id) {
133 field_types.push(path.name().to_string());
134 break; }
136 }
137 }
138
139 if !field_types.is_empty() {
140 self.field_type_names.insert(id, field_types);
141 }
142 }
143
144 pub fn iter_derives(&self) -> impl Iterator<Item = (SymbolId, &SmallVec<[String; 4]>)> {
150 self.symbol_derives.iter()
151 }
152
153 pub fn get_derives(&self, id: SymbolId) -> Option<&SmallVec<[String; 4]>> {
155 self.symbol_derives.get(id)
156 }
157
158 pub fn get_field_types(&self, id: SymbolId) -> Option<&SmallVec<[String; 8]>> {
160 self.field_type_names.get(id)
161 }
162
163 pub fn has_derive(&self, id: SymbolId, trait_name: &str) -> bool {
165 self.symbol_derives
166 .get(id)
167 .map(|derives| derives.iter().any(|d| d == trait_name))
168 .unwrap_or(false)
169 }
170
171 pub fn symbols_deriving(&self, trait_name: &str) -> Vec<SymbolId> {
173 self.symbol_derives
174 .iter()
175 .filter(|(_, derives)| derives.iter().any(|d| d == trait_name))
176 .map(|(id, _)| id)
177 .collect()
178 }
179
180 pub fn stats(&self) -> DeriveIndexStats {
182 let total_derives: usize = self.symbol_derives.values().map(|v| v.len()).sum();
183 let total_fields: usize = self.field_type_names.values().map(|v| v.len()).sum();
184
185 DeriveIndexStats {
186 symbols_with_derives: self.symbol_derives.len(),
187 total_derives,
188 symbols_with_fields: self.field_type_names.len(),
189 total_field_types: total_fields,
190 }
191 }
192}
193
194#[derive(Debug, Clone)]
196pub struct DeriveIndexStats {
197 pub symbols_with_derives: usize,
199 pub total_derives: usize,
202 pub symbols_with_fields: usize,
205 pub total_field_types: usize,
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212
213 #[test]
214 fn test_derive_index_creation() {
215 let index = DeriveIndex::new();
216 assert_eq!(index.stats().symbols_with_derives, 0);
217 }
218}