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