use clap::Parser;
use std::path::PathBuf;
#[derive(Parser, Debug)]
#[command(
name = "sql-stream",
version,
author,
about = "Execute SQL queries against CSV/JSON files with streaming",
long_about = "A production-grade CLI tool that executes SQL queries against CSV/JSON files \
using Apache DataFusion and Apache Arrow with zero-copy, streaming architecture."
)]
pub struct CliArgs {
#[arg(
short = 'f',
long = "file",
value_name = "FILE",
help = "Path to CSV or JSON file",
required = true
)]
pub file: PathBuf,
#[arg(
short = 'q',
long = "query",
value_name = "SQL",
help = "SQL query string to execute",
required = true
)]
pub query: String,
#[arg(
short = 't',
long = "table-name",
value_name = "NAME",
help = "Table name to use in SQL queries",
default_value = "data"
)]
pub table_name: String,
#[arg(short = 'v', long = "verbose", help = "Enable verbose logging output")]
pub verbose: bool,
}
impl CliArgs {
pub fn parse() -> Self {
<Self as Parser>::parse()
}
pub fn validate(&self) -> Result<(), String> {
if !self.file.exists() {
return Err(format!("File not found: {}", self.file.display()));
}
let extension = self
.file
.extension()
.and_then(|ext| ext.to_str())
.ok_or_else(|| "File must have an extension (.csv or .json)".to_string())?;
match extension.to_lowercase().as_str() {
"csv" | "json" => Ok(()),
_ => Err(format!(
"Unsupported file extension: .{}. Supported: .csv, .json",
extension
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cli_structure() {
let args = CliArgs {
file: PathBuf::from("test.csv"),
query: "SELECT * FROM data".to_string(),
table_name: "data".to_string(),
verbose: false,
};
assert_eq!(args.table_name, "data");
assert_eq!(args.query, "SELECT * FROM data");
}
}