sql_cli/data/adapters/
csv_client_adapter.rs1use crate::api_client::QueryResponse;
7use crate::data::csv_datasource::CsvApiClient;
8use crate::data::data_provider::DataProvider;
9use std::fmt::Debug;
10
11pub struct CsvClientAdapter<'a> {
15 client: &'a CsvApiClient,
16 cached_response: Option<QueryResponse>,
17}
18
19impl<'a> CsvClientAdapter<'a> {
20 pub fn new(client: &'a CsvApiClient) -> Self {
23 Self {
24 client,
25 cached_response: None,
26 }
27 }
28
29 pub fn execute_query(&mut self, sql: &str) -> anyhow::Result<()> {
31 let response = self.client.query_csv(sql)?;
32 self.cached_response = Some(response);
33 Ok(())
34 }
35}
36
37impl<'a> Debug for CsvClientAdapter<'a> {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 f.debug_struct("CsvClientAdapter")
40 .field("row_count", &self.get_row_count())
41 .field("column_count", &self.get_column_count())
42 .field("has_data", &self.cached_response.is_some())
43 .finish()
44 }
45}
46
47impl<'a> DataProvider for CsvClientAdapter<'a> {
48 fn get_row(&self, index: usize) -> Option<Vec<String>> {
49 self.cached_response.as_ref().and_then(|response| {
50 response.data.get(index).map(|json_value| {
51 if let Some(obj) = json_value.as_object() {
53 let columns = self.get_column_names();
55 columns
56 .iter()
57 .map(|col| {
58 obj.get(col)
59 .map(|v| {
60 match v {
62 serde_json::Value::String(s) => s.clone(),
63 serde_json::Value::Null => String::new(),
64 other => other.to_string(),
65 }
66 })
67 .unwrap_or_default()
68 })
69 .collect()
70 } else {
71 vec![json_value.to_string()]
73 }
74 })
75 })
76 }
77
78 fn get_column_names(&self) -> Vec<String> {
79 if let Some(ref response) = self.cached_response {
81 if let Some(first_row) = response.data.first() {
83 if let Some(obj) = first_row.as_object() {
84 return obj.keys().map(|k| k.to_string()).collect();
85 }
86 }
87 }
88
89 self.client
91 .get_schema()
92 .and_then(|schema| schema.values().next().map(|headers| headers.clone()))
93 .unwrap_or_default()
94 }
95
96 fn get_row_count(&self) -> usize {
97 self.cached_response
98 .as_ref()
99 .map(|r| r.data.len())
100 .unwrap_or(0)
101 }
102
103 fn get_column_count(&self) -> usize {
104 self.get_column_names().len()
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use crate::data::csv_datasource::CsvApiClient;
112 use serde_json::json;
113 use std::fs;
114 use tempfile::tempdir;
115
116 #[test]
117 fn test_csv_client_adapter_basic() {
118 let temp_dir = tempdir().unwrap();
120 let json_path = temp_dir.path().join("test_data.json");
121
122 let test_data = json!([
123 {
124 "id": 1,
125 "name": "Alice",
126 "age": 30
127 },
128 {
129 "id": 2,
130 "name": "Bob",
131 "age": 25
132 },
133 {
134 "id": 3,
135 "name": "Charlie",
136 "age": 35
137 }
138 ]);
139
140 fs::write(&json_path, serde_json::to_string(&test_data).unwrap()).unwrap();
141
142 let mut client = CsvApiClient::new();
143 client.load_json(&json_path, "test").unwrap();
144
145 let mut adapter = CsvClientAdapter::new(&client);
147 adapter.execute_query("SELECT * FROM test").unwrap();
148
149 assert_eq!(adapter.get_row_count(), 3);
151 assert_eq!(adapter.get_column_count(), 3);
152
153 let col_names = adapter.get_column_names();
154 assert!(col_names.contains(&"id".to_string()));
155 assert!(col_names.contains(&"name".to_string()));
156 assert!(col_names.contains(&"age".to_string()));
157
158 let row = adapter.get_row(0).unwrap();
160 assert!(row.contains(&"1".to_string()));
161 assert!(row.contains(&"Alice".to_string()));
162 assert!(row.contains(&"30".to_string()));
163
164 let row = adapter.get_row(2).unwrap();
165 assert!(row.contains(&"3".to_string()));
166 assert!(row.contains(&"Charlie".to_string()));
167 assert!(row.contains(&"35".to_string()));
168
169 assert!(adapter.get_row(3).is_none());
171 }
172
173 #[test]
174 fn test_csv_client_adapter_empty() {
175 let client = CsvApiClient::new();
176 let adapter = CsvClientAdapter::new(&client);
177
178 assert_eq!(adapter.get_row_count(), 0);
180 assert_eq!(adapter.get_column_count(), 0);
181 assert!(adapter.get_row(0).is_none());
182 }
183
184 #[test]
185 fn test_csv_client_adapter_with_filter() {
186 let temp_dir = tempdir().unwrap();
188 let json_path = temp_dir.path().join("test_data.json");
189
190 let test_data = json!([
191 {
192 "id": 1,
193 "name": "Alice",
194 "status": "active"
195 },
196 {
197 "id": 2,
198 "name": "Bob",
199 "status": "inactive"
200 },
201 {
202 "id": 3,
203 "name": "Charlie",
204 "status": "active"
205 }
206 ]);
207
208 fs::write(&json_path, serde_json::to_string(&test_data).unwrap()).unwrap();
209
210 let mut client = CsvApiClient::new();
211 client.load_json(&json_path, "test").unwrap();
212
213 let mut adapter = CsvClientAdapter::new(&client);
215 adapter
216 .execute_query("SELECT * FROM test WHERE status = 'active'")
217 .unwrap();
218
219 assert_eq!(adapter.get_row_count(), 2);
221
222 for i in 0..adapter.get_row_count() {
224 let row = adapter.get_row(i).unwrap();
225 assert!(row.contains(&"active".to_string()));
226 }
227 }
228}