Skip to content

Commit 5bec040

Browse files
authored
Allow for using other readers in ggsql cli (#251)
* Allow for other readers in cli * Update doc strings for help output
1 parent 75d6d1b commit 5bec040

1 file changed

Lines changed: 95 additions & 51 deletions

File tree

src/cli.rs

Lines changed: 95 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,11 @@ Provides commands for executing ggsql queries with various data sources and outp
55
*/
66

77
use clap::{Parser, Subcommand};
8+
use ggsql::reader::{Reader, Spec};
9+
use ggsql::validate::validate;
810
use ggsql::{parser, VERSION};
911
use std::path::PathBuf;
1012

11-
#[cfg(feature = "duckdb")]
12-
use ggsql::reader::{DuckDBReader, Reader};
13-
#[cfg(feature = "duckdb")]
14-
use ggsql::validate::validate;
15-
1613
#[cfg(feature = "vegalite")]
1714
use ggsql::writer::{VegaLiteWriter, Writer};
1815

@@ -32,11 +29,11 @@ pub enum Commands {
3229
/// The ggsql query to execute
3330
query: String,
3431

35-
/// Data source connection string
32+
/// Data source connection string (duckdb://, sqlite://, polars://)
3633
#[arg(long, default_value = "duckdb://memory")]
3734
reader: String,
3835

39-
/// Output format
36+
/// Output format (vegalite)
4037
#[arg(long, default_value = "vegalite")]
4138
writer: String,
4239

@@ -54,11 +51,11 @@ pub enum Commands {
5451
/// Path to .sql file containing ggsql query
5552
file: PathBuf,
5653

57-
/// Data source connection string
54+
/// Data source connection string (duckdb://, sqlite://, polars://)
5855
#[arg(long, default_value = "duckdb://memory")]
5956
reader: String,
6057

61-
/// Output format
58+
/// Output format (vegalite)
6259
#[arg(long, default_value = "vegalite")]
6360
writer: String,
6461

@@ -86,7 +83,7 @@ pub enum Commands {
8683
/// The ggsql query to validate
8784
query: String,
8885

89-
/// Data source connection string (needed for column validation)
86+
/// Data source connection string for column validation (duckdb://, sqlite://, polars://)
9087
#[arg(long)]
9188
reader: Option<String>,
9289
},
@@ -153,23 +150,75 @@ fn cmd_exec(query: String, reader: String, writer: String, output: Option<PathBu
153150
}
154151
}
155152

156-
// Setup reader
157-
#[cfg(feature = "duckdb")]
158-
if !reader.starts_with("duckdb://") {
159-
eprintln!("Unsupported reader: {}", reader);
160-
eprintln!("Currently only 'duckdb://' readers are supported");
153+
if reader.starts_with("duckdb://") {
154+
#[cfg(feature = "duckdb")]
155+
{
156+
let r = match ggsql::reader::DuckDBReader::from_connection_string(&reader) {
157+
Ok(r) => r,
158+
Err(e) => {
159+
eprintln!("Failed to create reader: {}", e);
160+
std::process::exit(1);
161+
}
162+
};
163+
exec_with_reader(&query, &r, &writer, output, verbose);
164+
}
165+
#[cfg(not(feature = "duckdb"))]
166+
{
167+
eprintln!("DuckDB reader not compiled in. Rebuild with --features duckdb");
168+
std::process::exit(1);
169+
}
170+
} else if reader.starts_with("polars://") {
171+
#[cfg(feature = "polars-sql")]
172+
{
173+
let r = match ggsql::reader::PolarsReader::from_connection_string(&reader) {
174+
Ok(r) => r,
175+
Err(e) => {
176+
eprintln!("Failed to create reader: {}", e);
177+
std::process::exit(1);
178+
}
179+
};
180+
exec_with_reader(&query, &r, &writer, output, verbose);
181+
}
182+
#[cfg(not(feature = "polars-sql"))]
183+
{
184+
eprintln!("Polars reader not compiled in. Rebuild with --features polars-sql");
185+
std::process::exit(1);
186+
}
187+
} else if reader.starts_with("sqlite://") {
188+
#[cfg(feature = "sqlite")]
189+
{
190+
let r = match ggsql::reader::SqliteReader::from_connection_string(&reader) {
191+
Ok(r) => r,
192+
Err(e) => {
193+
eprintln!("Failed to create reader: {}", e);
194+
std::process::exit(1);
195+
}
196+
};
197+
exec_with_reader(&query, &r, &writer, output, verbose);
198+
}
199+
#[cfg(not(feature = "sqlite"))]
200+
{
201+
eprintln!("SQLite reader not compiled in. Rebuild with --features sqlite");
202+
std::process::exit(1);
203+
}
204+
} else if reader.starts_with("postgres://") || reader.starts_with("postgresql://") {
205+
eprintln!("PostgreSQL reader is not yet implemented");
161206
std::process::exit(1);
162-
}
163-
164-
let db_reader = DuckDBReader::from_connection_string(&reader);
165-
if let Err(e) = db_reader {
166-
eprintln!("Failed to create DuckDB reader: {}", e);
207+
} else {
208+
eprintln!("Unsupported connection string: {}", reader);
167209
std::process::exit(1);
168210
}
169-
let db_reader = db_reader.unwrap();
211+
}
170212

213+
fn exec_with_reader<R: Reader>(
214+
query: &str,
215+
reader: &R,
216+
writer: &str,
217+
output: Option<PathBuf>,
218+
verbose: bool,
219+
) {
171220
// Use validate() to check if query has visualization
172-
let validated = match validate(&query) {
221+
let validated = match validate(query) {
173222
Ok(v) => v,
174223
Err(e) => {
175224
eprintln!("Failed to validate query: {}", e);
@@ -181,19 +230,23 @@ fn cmd_exec(query: String, reader: String, writer: String, output: Option<PathBu
181230
if verbose {
182231
eprintln!("Visualisation is empty. Printing table instead.");
183232
}
184-
print_table_fallback(&query, &db_reader, 100);
233+
print_table_fallback(query, reader, 100);
185234
return;
186235
}
187236

188237
// Execute ggsql query
189-
let spec = match db_reader.execute(&query) {
238+
let spec = match reader.execute(query) {
190239
Ok(s) => s,
191240
Err(e) => {
192241
eprintln!("Failed to execute query: {}", e);
193242
std::process::exit(1);
194243
}
195244
};
196245

246+
render_spec(spec, writer, output, verbose);
247+
}
248+
249+
fn render_spec(spec: Spec, writer: &str, output: Option<PathBuf>, verbose: bool) {
197250
if verbose {
198251
let metadata = spec.metadata();
199252
eprintln!("\nQuery executed:");
@@ -292,41 +345,32 @@ fn cmd_parse(query: String, format: String) {
292345
}
293346

294347
fn cmd_validate(query: String, _reader: Option<String>) {
295-
#[cfg(feature = "duckdb")]
296-
{
297-
match validate(&query) {
298-
Ok(validated) if validated.valid() => {
299-
println!("✓ Query syntax is valid");
348+
match validate(&query) {
349+
Ok(validated) if validated.valid() => {
350+
println!("✓ Query syntax is valid");
351+
}
352+
Ok(validated) => {
353+
println!("✗ Validation errors:");
354+
for err in validated.errors() {
355+
println!(" - {}", err.message);
300356
}
301-
Ok(validated) => {
302-
println!("✗ Validation errors:");
303-
for err in validated.errors() {
304-
println!(" - {}", err.message);
357+
if !validated.warnings().is_empty() {
358+
println!("\nWarnings:");
359+
for warning in validated.warnings() {
360+
println!(" - {}", warning.message);
305361
}
306-
if !validated.warnings().is_empty() {
307-
println!("\nWarnings:");
308-
for warning in validated.warnings() {
309-
println!(" - {}", warning.message);
310-
}
311-
}
312-
std::process::exit(1);
313-
}
314-
Err(e) => {
315-
eprintln!("Error during validation: {}", e);
316-
std::process::exit(1);
317362
}
363+
std::process::exit(1);
364+
}
365+
Err(e) => {
366+
eprintln!("Error during validation: {}", e);
367+
std::process::exit(1);
318368
}
319-
}
320-
321-
#[cfg(not(feature = "duckdb"))]
322-
{
323-
eprintln!("Validation requires the duckdb feature");
324-
std::process::exit(1);
325369
}
326370
}
327371

328372
// Prints a CSV-like output to stdout with aligned columns
329-
fn print_table_fallback(query: &str, reader: &DuckDBReader, max_rows: usize) {
373+
fn print_table_fallback<R: Reader>(query: &str, reader: &R, max_rows: usize) {
330374
let source_tree = match parser::SourceTree::new(query) {
331375
Ok(st) => st,
332376
Err(e) => {

0 commit comments

Comments
 (0)