sql_cli/sql/
hybrid_parser.rs1use crate::cursor_aware_parser::CursorAwareParser;
2use crate::recursive_parser::{detect_cursor_context, tokenize_query, CursorContext, LogicalOp};
3
4#[derive(Clone)]
5pub struct HybridParser {
6 parser: CursorAwareParser,
7}
8
9impl std::fmt::Debug for HybridParser {
10 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11 f.debug_struct("HybridParser")
12 .field("parser", &"<CursorAwareParser>")
13 .finish()
14 }
15}
16
17#[derive(Debug, Clone)]
18pub struct HybridResult {
19 pub suggestions: Vec<String>,
20 pub context: String,
21 pub parser_used: String,
22 pub recursive_context: String,
23 pub cursor_position: usize,
24 pub query_complexity: String,
25}
26
27impl HybridParser {
28 pub fn new() -> Self {
29 Self {
30 parser: CursorAwareParser::new(),
31 }
32 }
33
34 pub fn update_single_table(&mut self, table_name: String, columns: Vec<String>) {
35 self.parser.update_single_table(table_name, columns);
36 }
37
38 pub fn get_table_columns(&self, table_name: &str) -> Vec<String> {
39 self.parser.get_table_columns(table_name)
40 }
41
42 pub fn get_completions(&self, query: &str, cursor_pos: usize) -> HybridResult {
43 let result = self.parser.get_completions(query, cursor_pos);
45
46 let (cursor_context, _) = detect_cursor_context(query, cursor_pos);
48 let recursive_context = match cursor_context {
49 CursorContext::SelectClause => "SelectClause",
50 CursorContext::FromClause => "FromClause",
51 CursorContext::WhereClause => "WhereClause",
52 CursorContext::OrderByClause => "OrderByClause",
53 CursorContext::AfterColumn(_) => "AfterColumn",
54 CursorContext::AfterLogicalOp(LogicalOp::And) => "AfterAND",
55 CursorContext::AfterLogicalOp(LogicalOp::Or) => "AfterOR",
56 CursorContext::AfterComparisonOp(_, _) => "AfterComparisonOp",
57 CursorContext::InMethodCall(_, _) => "InMethodCall",
58 CursorContext::InExpression => "InExpression",
59 CursorContext::Unknown => "Unknown",
60 };
61
62 HybridResult {
63 suggestions: result.suggestions,
64 context: result.context.clone(),
65 parser_used: "RecursiveDescent".to_string(),
66 recursive_context: recursive_context.to_string(),
67 cursor_position: cursor_pos,
68 query_complexity: self.analyze_query_complexity(query),
69 }
70 }
71
72 fn analyze_query_complexity(&self, query: &str) -> String {
73 let mut complexity_factors = Vec::new();
74
75 let logical_ops = query.to_uppercase().matches(" AND ").count()
77 + query.to_uppercase().matches(" OR ").count();
78 if logical_ops > 0 {
79 complexity_factors.push(format!("{}x logical", logical_ops));
80 }
81
82 let method_calls = query.matches('.').count();
84 if method_calls > 0 {
85 complexity_factors.push(format!("{}x methods", method_calls));
86 }
87
88 let paren_depth = self.max_paren_depth(query);
90 if paren_depth > 1 {
91 complexity_factors.push(format!("{}lvl nested", paren_depth));
92 }
93
94 if query.to_uppercase().contains("SELECT")
96 && query.to_uppercase().matches("SELECT").count() > 1
97 {
98 complexity_factors.push("subquery".to_string());
99 }
100
101 if complexity_factors.is_empty() {
102 "simple".to_string()
103 } else {
104 complexity_factors.join(", ")
105 }
106 }
107
108 fn max_paren_depth(&self, query: &str) -> usize {
109 let mut max_depth = 0;
110 let mut current_depth: usize = 0;
111
112 for ch in query.chars() {
113 match ch {
114 '(' => {
115 current_depth += 1;
116 max_depth = max_depth.max(current_depth);
117 }
118 ')' => {
119 current_depth = current_depth.saturating_sub(1);
120 }
121 _ => {}
122 }
123 }
124
125 max_depth
126 }
127
128 pub fn debug_tree(&self, query: &str) -> String {
129 crate::recursive_parser::format_ast_tree(query)
131 }
132
133 pub fn get_detailed_debug_info(&self, query: &str, cursor_pos: usize) -> String {
134 let result = self.get_completions(query, cursor_pos);
135
136 let char_at_cursor = if cursor_pos < query.len() {
137 format!("'{}'", query.chars().nth(cursor_pos).unwrap_or(' '))
138 } else {
139 "EOF".to_string()
140 };
141
142 let tokens = tokenize_query(query);
144 let tokenized_output = if tokens.is_empty() {
145 " (no tokens)".to_string()
146 } else {
147 tokens
148 .iter()
149 .enumerate()
150 .map(|(i, t)| format!(" [{}] {}", i, t))
151 .collect::<Vec<_>>()
152 .join("\n")
153 };
154
155 let ast_tree = self.debug_tree(query);
156
157 let partial_word_info = if result.context.contains("(partial:") {
159 if let Some(start) = result.context.find("(partial: ") {
161 let substr = &result.context[start + 10..];
162 if let Some(end) = substr.find(')') {
163 substr[..end].to_string()
164 } else {
165 "None".to_string()
166 }
167 } else {
168 "None".to_string()
169 }
170 } else {
171 "None".to_string()
172 };
173
174 format!(
175 "========== PARSER DEBUG ==========\n\
176Query: '{}'\n\
177Query Length: {}\n\
178Cursor: {} {}\n\
179Partial Word: {}\n\
180Complexity: {}\n\
181Parser Used: {}\n\
182Parser Type: Recursive Descent\n\
183\n\
184TOKENIZED OUTPUT:\n{}\n\
185\n\
186CONTEXT: {}\n\
187RECURSIVE PARSER CONTEXT: {}\n\
188\n\
189SUGGESTIONS ({}):\n{}\n\
190\n\
191AST TREE:\n{}\n\
192==================================",
193 query,
194 query.len(),
195 cursor_pos,
196 char_at_cursor,
197 partial_word_info,
198 result.query_complexity,
199 result.parser_used,
200 tokenized_output,
201 result.context,
202 result.recursive_context,
203 result.suggestions.len(),
204 if result.suggestions.is_empty() {
205 " (no suggestions)".to_string()
206 } else {
207 result
208 .suggestions
209 .iter()
210 .enumerate()
211 .map(|(i, s)| format!(" {}: {}", i + 1, s))
212 .collect::<Vec<_>>()
213 .join("\n")
214 },
215 ast_tree
216 )
217 }
218}