sql_cli/services/
data_loader_service.rs1use crate::data::csv_datasource::CsvApiClient;
2use crate::data::data_view::DataView;
3use crate::data::datatable::DataTable;
4use crate::ui::utils::enhanced_tui_helpers;
5use anyhow::Result;
6use std::path::Path;
7use std::sync::Arc;
8use tracing::{info, warn};
9
10pub struct DataLoaderService {
13 case_insensitive: bool,
14}
15
16impl DataLoaderService {
17 pub fn new(case_insensitive: bool) -> Self {
18 Self { case_insensitive }
19 }
20
21 pub fn load_file(&self, file_path: &str) -> Result<DataLoadResult> {
24 let path = Path::new(file_path);
25 let extension = path
26 .extension()
27 .and_then(|e| e.to_str())
28 .ok_or_else(|| anyhow::anyhow!("File has no extension: {}", file_path))?;
29
30 let raw_table_name = path
31 .file_stem()
32 .and_then(|s| s.to_str())
33 .unwrap_or("data")
34 .to_string();
35
36 let table_name = enhanced_tui_helpers::sanitize_table_name(&raw_table_name);
38
39 match extension.to_lowercase().as_str() {
40 "csv" => self.load_csv(file_path, &table_name, &raw_table_name),
41 "json" => self.load_json(file_path, &table_name, &raw_table_name),
42 _ => Err(anyhow::anyhow!(
43 "Unsupported file type: {}. Use .csv or .json files.",
44 extension
45 )),
46 }
47 }
48
49 fn load_csv(
51 &self,
52 file_path: &str,
53 table_name: &str,
54 raw_table_name: &str,
55 ) -> Result<DataLoadResult> {
56 info!("Loading CSV file: {}", file_path);
57 let start = std::time::Instant::now();
58
59 let datatable = match crate::data::advanced_csv_loader::AdvancedCsvLoader::new()
61 .load_csv_optimized(file_path, table_name)
62 {
63 Ok(dt) => {
64 info!("Successfully loaded CSV with advanced optimizations");
65 dt
66 }
67 Err(e) => {
68 warn!(
69 "Advanced CSV loader failed: {}, falling back to standard loader",
70 e
71 );
72 crate::data::datatable_loaders::load_csv_to_datatable(file_path, table_name)?
73 }
74 };
75
76 self.create_result(
77 datatable,
78 file_path.to_string(),
79 table_name.to_string(),
80 raw_table_name.to_string(),
81 start.elapsed(),
82 )
83 }
84
85 fn load_json(
87 &self,
88 file_path: &str,
89 table_name: &str,
90 raw_table_name: &str,
91 ) -> Result<DataLoadResult> {
92 info!("Loading JSON file: {}", file_path);
93 let start = std::time::Instant::now();
94
95 let datatable =
96 crate::data::datatable_loaders::load_json_to_datatable(file_path, table_name)?;
97
98 self.create_result(
99 datatable,
100 file_path.to_string(),
101 table_name.to_string(),
102 raw_table_name.to_string(),
103 start.elapsed(),
104 )
105 }
106
107 pub fn load_with_client(&self, file_path: &str) -> Result<DataLoadResult> {
109 let mut csv_client = CsvApiClient::new();
110 csv_client.set_case_insensitive(self.case_insensitive);
111
112 let path = Path::new(file_path);
113 let raw_table_name = path
114 .file_stem()
115 .and_then(|s| s.to_str())
116 .unwrap_or("data")
117 .to_string();
118
119 let table_name = enhanced_tui_helpers::sanitize_table_name(&raw_table_name);
121
122 let extension = path
123 .extension()
124 .and_then(|e| e.to_str())
125 .ok_or_else(|| anyhow::anyhow!("File has no extension: {}", file_path))?;
126
127 let start = std::time::Instant::now();
128
129 match extension.to_lowercase().as_str() {
130 "csv" => csv_client.load_csv(file_path, &table_name)?,
131 "json" => csv_client.load_json(file_path, &table_name)?,
132 _ => return Err(anyhow::anyhow!("Unsupported file type: {}", extension)),
133 }
134
135 let datatable = csv_client
137 .get_datatable()
138 .ok_or_else(|| anyhow::anyhow!("Failed to load data from {}", file_path))?;
139
140 self.create_result(
141 datatable,
142 file_path.to_string(),
143 table_name,
144 raw_table_name,
145 start.elapsed(),
146 )
147 }
148
149 fn create_result(
151 &self,
152 datatable: DataTable,
153 source_path: String,
154 table_name: String,
155 raw_table_name: String,
156 load_time: std::time::Duration,
157 ) -> Result<DataLoadResult> {
158 let initial_row_count = datatable.row_count();
160 let initial_column_count = datatable.column_count();
161
162 let dataview = DataView::new(Arc::new(datatable));
164
165 Ok(DataLoadResult {
166 dataview,
167 source_path,
168 table_name,
169 raw_table_name,
170 initial_row_count,
171 initial_column_count,
172 load_time,
173 })
174 }
175
176 pub fn set_case_insensitive(&mut self, case_insensitive: bool) {
178 self.case_insensitive = case_insensitive;
179 }
180}
181
182pub struct DataLoadResult {
184 pub dataview: DataView,
186
187 pub source_path: String,
189
190 pub table_name: String,
192
193 pub raw_table_name: String,
195
196 pub initial_row_count: usize,
198
199 pub initial_column_count: usize,
201
202 pub load_time: std::time::Duration,
204}
205
206impl DataLoadResult {
207 pub fn status_message(&self) -> String {
209 format!(
210 "Loaded {} ({} rows, {} columns) in {} ms",
211 self.source_path,
212 self.initial_row_count,
213 self.initial_column_count,
214 self.load_time.as_millis()
215 )
216 }
217}