sql_cli/services/
query_orchestrator.rs

1use crate::app_state_container::AppStateContainer;
2use crate::config::config::BehaviorConfig;
3use crate::data::data_view::DataView;
4use crate::services::{QueryExecutionResult, QueryExecutionService};
5use crate::ui::search::vim_search_adapter::VimSearchAdapter;
6use anyhow::Result;
7use std::cell::RefCell;
8use tracing::{debug, info};
9
10/// Orchestrates the entire query execution flow
11/// This handles all the side effects and state management around query execution
12pub struct QueryOrchestrator {
13    query_execution_service: QueryExecutionService,
14}
15
16impl QueryOrchestrator {
17    pub fn new(case_insensitive: bool, auto_hide_empty: bool) -> Self {
18        Self {
19            query_execution_service: QueryExecutionService::new(case_insensitive, auto_hide_empty),
20        }
21    }
22
23    pub fn with_behavior_config(behavior_config: BehaviorConfig) -> Self {
24        Self {
25            query_execution_service: QueryExecutionService::with_behavior_config(behavior_config),
26        }
27    }
28
29    /// Execute a query with all necessary state management
30    pub fn execute_query(
31        &mut self,
32        query: &str,
33        state_container: &mut AppStateContainer,
34        vim_search_adapter: &RefCell<VimSearchAdapter>,
35    ) -> Result<QueryExecutionContext> {
36        info!(target: "query", "Executing query: {}", query);
37
38        // 1. Clear all search-related state before executing new query
39        self.clear_all_search_state(state_container, vim_search_adapter);
40
41        // 2. Record the query being executed
42        self.record_query_execution(query, state_container);
43
44        // 3. Set executing status
45        state_container.set_status_message(format!("Executing query: '{}'...", query));
46
47        // 4. Execute the query
48        let current_dataview = state_container.get_buffer_dataview();
49        let original_source = state_container.get_original_source();
50
51        // Debug: log what we're passing to the query service
52        if let Some(ref orig) = original_source {
53            debug!(
54                "QueryOrchestrator: Have original source with {} columns",
55                orig.column_count()
56            );
57        } else {
58            debug!("QueryOrchestrator: WARNING - No original source available!");
59        }
60
61        if let Some(ref view) = current_dataview {
62            debug!(
63                "QueryOrchestrator: Current view has {} columns, source has {} columns",
64                view.column_count(),
65                view.source().column_count()
66            );
67        }
68
69        let result =
70            self.query_execution_service
71                .execute(query, current_dataview, original_source)?;
72
73        // 5. Clear any active filters (new query should start with clean state)
74        self.clear_all_filters(state_container);
75
76        // 6. Return the context for the TUI to apply
77        Ok(QueryExecutionContext {
78            result,
79            query: query.to_string(),
80        })
81    }
82
83    /// Clear all search-related state
84    fn clear_all_search_state(
85        &self,
86        state_container: &mut AppStateContainer,
87        vim_search_adapter: &RefCell<VimSearchAdapter>,
88    ) {
89        // Clear container search states
90        state_container.clear_search();
91        state_container.clear_column_search();
92
93        // Clear vim search adapter state
94        vim_search_adapter.borrow_mut().cancel_search();
95    }
96
97    /// Record that a query is being executed
98    fn record_query_execution(&self, query: &str, state_container: &mut AppStateContainer) {
99        state_container.set_last_query(query.to_string());
100        state_container.set_last_executed_query(query.to_string());
101    }
102
103    /// Clear all filter-related state
104    fn clear_all_filters(&self, state_container: &mut AppStateContainer) {
105        state_container.set_filter_pattern(String::new());
106        state_container.set_fuzzy_filter_pattern(String::new());
107        state_container.set_filter_active(false);
108        state_container.set_fuzzy_filter_active(false);
109    }
110
111    /// Update service configuration
112    pub fn set_case_insensitive(&mut self, case_insensitive: bool) {
113        self.query_execution_service
114            .set_case_insensitive(case_insensitive);
115    }
116
117    pub fn set_auto_hide_empty(&mut self, auto_hide: bool) {
118        self.query_execution_service.set_auto_hide_empty(auto_hide);
119    }
120}
121
122/// Context returned from query execution
123pub struct QueryExecutionContext {
124    pub result: QueryExecutionResult,
125    pub query: String,
126}
127
128impl QueryExecutionContext {
129    /// Apply this context to the TUI state
130    /// This is what the TUI will call to update its state after query execution
131    pub fn apply_to_tui(
132        self,
133        state_container: &mut AppStateContainer,
134        update_viewport: impl FnOnce(DataView),
135        calculate_widths: impl FnOnce(),
136        reset_table: impl FnOnce(),
137    ) -> Result<()> {
138        // Apply the new DataView
139        state_container.set_dataview(Some(self.result.dataview.clone()));
140
141        // Update viewport (delegate to TUI)
142        update_viewport(self.result.dataview.clone());
143
144        // Update navigation state
145        state_container
146            .update_data_size(self.result.stats.row_count, self.result.stats.column_count);
147
148        // Calculate column widths (delegate to TUI)
149        calculate_widths();
150
151        // Update status message
152        state_container.set_status_message(self.result.status_message());
153
154        // Add to history
155        state_container
156            .command_history_mut()
157            .add_entry_with_schema(
158                self.query.clone(),
159                true,
160                Some(self.result.stats.execution_time.as_millis() as u64),
161                self.result.column_names(),
162                Some(self.result.table_name()),
163            )?;
164
165        // Switch to results mode
166        state_container.set_mode(crate::buffer::AppMode::Results);
167
168        // Reset table (delegate to TUI)
169        reset_table();
170
171        Ok(())
172    }
173}