use crate::api_client::QueryResponse;
use crate::csv_datasource::CsvApiClient;
use crate::datasource_trait::{DataSource, DataSourceQueryResponse};
use anyhow::Result;
use std::collections::HashMap;
use tracing::{debug, info, trace};
pub struct CsvDataSourceAdapter {
client: CsvApiClient,
}
impl CsvDataSourceAdapter {
#[must_use]
pub fn new(client: CsvApiClient) -> Self {
Self { client }
}
pub fn from_csv_path(path: &str, table_name: &str) -> Result<Self> {
info!(target: "datasource", "Loading CSV from path: {}", path);
let mut client = CsvApiClient::new();
client.load_csv(path, table_name)?;
if let Some(schema) = client.get_schema() {
if let Some(columns) = schema.get(table_name) {
info!(target: "datasource", "CSV loaded successfully: table='{}', columns={}",
table_name, columns.len());
debug!(target: "datasource", "Column names: {:?}", columns);
}
}
Ok(Self { client })
}
pub fn from_json_path(path: &str, table_name: &str) -> Result<Self> {
info!(target: "datasource", "Loading JSON from path: {}", path);
let mut client = CsvApiClient::new();
client.load_json(path, table_name)?;
if let Some(schema) = client.get_schema() {
if let Some(columns) = schema.get(table_name) {
info!(target: "datasource", "JSON loaded successfully: table='{}', columns={}",
table_name, columns.len());
debug!(target: "datasource", "Column names: {:?}", columns);
}
}
Ok(Self { client })
}
#[must_use]
pub fn inner(&self) -> &CsvApiClient {
&self.client
}
pub fn inner_mut(&mut self) -> &mut CsvApiClient {
&mut self.client
}
}
impl DataSource for CsvDataSourceAdapter {
fn query(&self, sql: &str) -> Result<DataSourceQueryResponse> {
debug!(target: "datasource", "Executing query: {}", sql);
let response = self.client.query_csv(sql)?;
let converted = convert_query_response(response);
info!(target: "datasource", "Query returned {} rows with {} columns",
converted.count, converted.columns.len());
trace!(target: "datasource", "Query columns: {:?}", converted.columns);
Ok(converted)
}
fn query_with_options(
&self,
sql: &str,
case_insensitive: bool,
) -> Result<DataSourceQueryResponse> {
let mut temp_client = self.client.clone();
temp_client.set_case_insensitive(case_insensitive);
let response = temp_client.query_csv(sql)?;
Ok(convert_query_response(response))
}
fn get_schema(&self) -> Option<HashMap<String, Vec<String>>> {
self.client.get_schema()
}
fn get_table_name(&self) -> String {
self.get_schema()
.and_then(|schema| schema.keys().next().cloned())
.unwrap_or_else(|| "data".to_string())
}
fn get_row_count(&self) -> usize {
self.query("SELECT * FROM data")
.map(|r| r.count)
.unwrap_or(0)
}
fn is_case_insensitive(&self) -> bool {
false
}
fn set_case_insensitive(&mut self, case_insensitive: bool) {
self.client.set_case_insensitive(case_insensitive);
}
fn clone_box(&self) -> Box<dyn DataSource> {
Box::new(Self {
client: self.client.clone(),
})
}
}
fn convert_query_response(response: QueryResponse) -> DataSourceQueryResponse {
let columns = if let Some(first_row) = response.data.first() {
if let Some(obj) = first_row.as_object() {
obj.keys().cloned().collect()
} else {
vec![]
}
} else {
vec![]
};
DataSourceQueryResponse {
data: response.data,
count: response.count,
columns,
table_name: "data".to_string(), }
}