Skip to main content

sql_stream/
cli.rs

1//! CLI argument parsing and validation
2//!
3//! This module defines the command-line interface using `clap` with derive macros
4//! for a professional and user-friendly CLI experience.
5
6use clap::Parser;
7use std::path::PathBuf;
8
9/// SQL Stream - Execute SQL queries against CSV/JSON files
10///
11/// A high-performance CLI tool powered by Apache DataFusion for running
12/// SQL queries on CSV and JSON files using streaming architecture.
13#[derive(Parser, Debug)]
14#[command(
15    name = "sql-stream",
16    version,
17    author,
18    about = "Execute SQL queries against CSV/JSON files with streaming",
19    long_about = "A production-grade CLI tool that executes SQL queries against CSV/JSON files \
20                  using Apache DataFusion and Apache Arrow with zero-copy, streaming architecture."
21)]
22pub struct CliArgs {
23    /// Path to the CSV or JSON file to query
24    #[arg(
25        short = 'f',
26        long = "file",
27        value_name = "FILE",
28        help = "Path to CSV or JSON file",
29        required = true
30    )]
31    pub file: PathBuf,
32
33    /// SQL query to execute
34    #[arg(
35        short = 'q',
36        long = "query",
37        value_name = "SQL",
38        help = "SQL query string to execute",
39        required = true
40    )]
41    pub query: String,
42
43    /// Custom table name for the registered file
44    #[arg(
45        short = 't',
46        long = "table-name",
47        value_name = "NAME",
48        help = "Table name to use in SQL queries",
49        default_value = "data"
50    )]
51    pub table_name: String,
52
53    /// Enable verbose debug logging
54    #[arg(short = 'v', long = "verbose", help = "Enable verbose logging output")]
55    pub verbose: bool,
56}
57
58impl CliArgs {
59    /// Parse CLI arguments from command line
60    ///
61    /// # Example
62    ///
63    /// ```no_run
64    /// use sql_stream::CliArgs;
65    /// let args = CliArgs::parse();
66    /// ```
67    pub fn parse() -> Self {
68        <Self as Parser>::parse()
69    }
70
71    /// Validate CLI arguments
72    ///
73    /// Performs additional validation beyond what clap provides
74    ///
75    /// # Errors
76    ///
77    /// Returns an error message if validation fails
78    pub fn validate(&self) -> Result<(), String> {
79        // Check if file exists
80        if !self.file.exists() {
81            return Err(format!("File not found: {}", self.file.display()));
82        }
83
84        // Check if file has a valid extension
85        let extension = self
86            .file
87            .extension()
88            .and_then(|ext| ext.to_str())
89            .ok_or_else(|| "File must have an extension (.csv or .json)".to_string())?;
90
91        match extension.to_lowercase().as_str() {
92            "csv" | "json" => Ok(()),
93            _ => Err(format!(
94                "Unsupported file extension: .{}. Supported: .csv, .json",
95                extension
96            )),
97        }
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn test_cli_structure() {
107        // This test ensures the CLI structure is valid
108        // Actual parsing is tested via integration tests
109        let args = CliArgs {
110            file: PathBuf::from("test.csv"),
111            query: "SELECT * FROM data".to_string(),
112            table_name: "data".to_string(),
113            verbose: false,
114        };
115
116        assert_eq!(args.table_name, "data");
117        assert_eq!(args.query, "SELECT * FROM data");
118    }
119}