sql_cli/data/
datasource_adapter.rs1use crate::api_client::QueryResponse;
2use crate::csv_datasource::CsvApiClient;
3use crate::datasource_trait::{DataSource, DataSourceQueryResponse};
4use anyhow::Result;
5use std::collections::HashMap;
6use tracing::{debug, info, trace};
7
8pub struct CsvDataSourceAdapter {
10 client: CsvApiClient,
11}
12
13impl CsvDataSourceAdapter {
14 pub fn new(client: CsvApiClient) -> Self {
15 Self { client }
16 }
17
18 pub fn from_csv_path(path: &str, table_name: &str) -> Result<Self> {
19 info!(target: "datasource", "Loading CSV from path: {}", path);
20 let mut client = CsvApiClient::new();
21 client.load_csv(path, table_name)?;
22
23 if let Some(schema) = client.get_schema() {
25 if let Some(columns) = schema.get(table_name) {
26 info!(target: "datasource", "CSV loaded successfully: table='{}', columns={}",
27 table_name, columns.len());
28 debug!(target: "datasource", "Column names: {:?}", columns);
29 }
30 }
31
32 Ok(Self { client })
33 }
34
35 pub fn from_json_path(path: &str, table_name: &str) -> Result<Self> {
36 info!(target: "datasource", "Loading JSON from path: {}", path);
37 let mut client = CsvApiClient::new();
38 client.load_json(path, table_name)?;
39
40 if let Some(schema) = client.get_schema() {
42 if let Some(columns) = schema.get(table_name) {
43 info!(target: "datasource", "JSON loaded successfully: table='{}', columns={}",
44 table_name, columns.len());
45 debug!(target: "datasource", "Column names: {:?}", columns);
46 }
47 }
48
49 Ok(Self { client })
50 }
51
52 pub fn inner(&self) -> &CsvApiClient {
54 &self.client
55 }
56
57 pub fn inner_mut(&mut self) -> &mut CsvApiClient {
58 &mut self.client
59 }
60}
61
62impl DataSource for CsvDataSourceAdapter {
63 fn query(&self, sql: &str) -> Result<DataSourceQueryResponse> {
64 debug!(target: "datasource", "Executing query: {}", sql);
65 let response = self.client.query_csv(sql)?;
66 let converted = convert_query_response(response);
67 info!(target: "datasource", "Query returned {} rows with {} columns",
68 converted.count, converted.columns.len());
69 trace!(target: "datasource", "Query columns: {:?}", converted.columns);
70 Ok(converted)
71 }
72
73 fn query_with_options(
74 &self,
75 sql: &str,
76 case_insensitive: bool,
77 ) -> Result<DataSourceQueryResponse> {
78 let mut temp_client = self.client.clone();
80 temp_client.set_case_insensitive(case_insensitive);
81 let response = temp_client.query_csv(sql)?;
82 Ok(convert_query_response(response))
83 }
84
85 fn get_schema(&self) -> Option<HashMap<String, Vec<String>>> {
86 self.client.get_schema()
87 }
88
89 fn get_table_name(&self) -> String {
90 self.get_schema()
92 .and_then(|schema| schema.keys().next().cloned())
93 .unwrap_or_else(|| "data".to_string())
94 }
95
96 fn get_row_count(&self) -> usize {
97 self.query("SELECT * FROM data")
100 .map(|r| r.count)
101 .unwrap_or(0)
102 }
103
104 fn is_case_insensitive(&self) -> bool {
105 false
108 }
109
110 fn set_case_insensitive(&mut self, case_insensitive: bool) {
111 self.client.set_case_insensitive(case_insensitive);
112 }
113
114 fn clone_box(&self) -> Box<dyn DataSource> {
115 Box::new(Self {
116 client: self.client.clone(),
117 })
118 }
119}
120
121fn convert_query_response(response: QueryResponse) -> DataSourceQueryResponse {
123 let columns = if let Some(first_row) = response.data.first() {
125 if let Some(obj) = first_row.as_object() {
126 obj.keys().cloned().collect()
127 } else {
128 vec![]
129 }
130 } else {
131 vec![]
132 };
133
134 DataSourceQueryResponse {
135 data: response.data,
136 count: response.count,
137 columns,
138 table_name: "data".to_string(), }
140}