sql_cli/ui/operations/
simple_operations.rs1use crate::buffer::{BufferAPI, BufferManager};
7use crate::text_navigation::TextNavigator;
8
9pub struct TextNavigationContext<'a> {
11 pub query: &'a str,
12 pub cursor_pos: usize,
13}
14
15pub struct UndoContext<'a> {
17 pub buffer_manager: &'a mut BufferManager,
18}
19
20pub fn get_cursor_token_position(ctx: &TextNavigationContext) -> (usize, usize) {
23 TextNavigator::get_cursor_token_position(ctx.query, ctx.cursor_pos)
24}
25
26pub fn get_token_at_cursor(ctx: &TextNavigationContext) -> Option<String> {
29 TextNavigator::get_token_at_cursor(ctx.query, ctx.cursor_pos)
30}
31
32#[derive(Debug, PartialEq)]
34pub enum UndoResult {
35 Success,
37 NothingToUndo,
39 NoBuffer,
41}
42
43impl UndoResult {
44 pub fn status_message(&self) -> &'static str {
46 match self {
47 UndoResult::Success => "Undo performed",
48 UndoResult::NothingToUndo => "Nothing to undo",
49 UndoResult::NoBuffer => "No buffer available for undo",
50 }
51 }
52}
53
54pub fn perform_undo(ctx: &mut UndoContext) -> UndoResult {
56 if let Some(buffer) = ctx.buffer_manager.current_mut() {
57 if buffer.perform_undo() {
58 UndoResult::Success
59 } else {
60 UndoResult::NothingToUndo
61 }
62 } else {
63 UndoResult::NoBuffer
64 }
65}
66
67pub fn check_parser_error(query: &str) -> Option<String> {
70 let mut paren_depth = 0;
72 let mut in_string = false;
73 let mut escape_next = false;
74
75 for ch in query.chars() {
76 if escape_next {
77 escape_next = false;
78 continue;
79 }
80
81 match ch {
82 '\\' if in_string => escape_next = true,
83 '\'' => in_string = !in_string,
84 '(' if !in_string => paren_depth += 1,
85 ')' if !in_string => {
86 paren_depth -= 1;
87 if paren_depth < 0 {
88 return Some("Extra )".to_string());
89 }
90 }
91 _ => {}
92 }
93 }
94
95 if paren_depth > 0 {
96 return Some(format!("Missing {} )", paren_depth));
97 }
98
99 if in_string {
101 return Some("Unclosed string".to_string());
102 }
103
104 None
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_get_cursor_token_position() {
113 let ctx = TextNavigationContext {
114 query: "SELECT name FROM users",
115 cursor_pos: 2, };
117
118 let (start, end) = get_cursor_token_position(&ctx);
119 assert!(start <= ctx.cursor_pos);
122 assert!(end >= ctx.cursor_pos);
123 }
124
125 #[test]
126 fn test_get_token_at_cursor() {
127 let ctx = TextNavigationContext {
128 query: "SELECT name FROM users",
129 cursor_pos: 2, };
131
132 let token = get_token_at_cursor(&ctx);
133 assert!(token.is_some() || token.is_none()); }
137
138 #[test]
139 fn test_get_token_at_cursor_empty_query() {
140 let ctx = TextNavigationContext {
141 query: "",
142 cursor_pos: 0,
143 };
144
145 let token = get_token_at_cursor(&ctx);
146 assert!(token.is_none() || token.is_some()); }
149
150 #[test]
151 fn test_undo_result_status_messages() {
152 assert_eq!(UndoResult::Success.status_message(), "Undo performed");
153 assert_eq!(
154 UndoResult::NothingToUndo.status_message(),
155 "Nothing to undo"
156 );
157 assert_eq!(
158 UndoResult::NoBuffer.status_message(),
159 "No buffer available for undo"
160 );
161 }
162
163 #[test]
164 fn test_check_parser_error_valid_queries() {
165 assert_eq!(check_parser_error("SELECT * FROM users"), None);
166 assert_eq!(
167 check_parser_error("SELECT name FROM users WHERE id = 1"),
168 None
169 );
170 assert_eq!(
171 check_parser_error("SELECT (column1 + column2) FROM table"),
172 None
173 );
174 assert_eq!(check_parser_error("SELECT 'hello world' FROM dual"), None);
175 }
176
177 #[test]
178 fn test_check_parser_error_mismatched_parens() {
179 assert_eq!(
180 check_parser_error("SELECT (column FROM table"),
181 Some("Missing 1 )".to_string())
182 );
183 assert_eq!(
184 check_parser_error("SELECT ((column FROM table"),
185 Some("Missing 2 )".to_string())
186 );
187 assert_eq!(
188 check_parser_error("SELECT column) FROM table"),
189 Some("Extra )".to_string())
190 );
191 }
192
193 #[test]
194 fn test_check_parser_error_unclosed_string() {
195 assert_eq!(
196 check_parser_error("SELECT 'unclosed FROM table"),
197 Some("Unclosed string".to_string())
198 );
199 assert_eq!(
200 check_parser_error("SELECT name FROM users WHERE name = 'test"),
201 Some("Unclosed string".to_string())
202 );
203 }
204
205 #[test]
206 fn test_check_parser_error_escaped_quotes() {
207 assert_eq!(
209 check_parser_error("SELECT 'it\\'s a test' FROM table"),
210 None
211 );
212 }
213
214 #[test]
215 fn test_check_parser_error_parens_in_strings() {
216 assert_eq!(
218 check_parser_error("SELECT 'text with (parens)' FROM table"),
219 None
220 );
221 assert_eq!(
222 check_parser_error("SELECT 'text with (unclosed FROM table"),
223 Some("Unclosed string".to_string())
224 );
225 }
226
227 #[test]
228 fn test_check_parser_error_empty_query() {
229 assert_eq!(check_parser_error(""), None);
230 }
231
232 }