1use crate::config::get_data_dir;
21use clap::Parser;
22use std::path::{Path, PathBuf};
23
24const LONG_ABOUT: &str = "
25dft - DataFusion TUI
26
27CLI and terminal UI data analysis tool using Apache DataFusion as query
28execution engine.
29
30dft provides a rich terminal UI as well as a broad array of pre-integrated
31data sources and formats for querying and analyzing data.
32
33Environment Variables
34RUST_LOG { trace | debug | info | error }: Standard rust logging level. Default is info.
35";
36
37#[derive(Clone, Debug, Parser, Default)]
38#[command(author, version, about, long_about = LONG_ABOUT)]
39pub struct DftArgs {
40 #[clap(
41 short,
42 long,
43 num_args = 0..,
44 help = "Execute commands from file(s), then exit",
45 value_parser(parse_valid_file)
46 )]
47 pub files: Vec<PathBuf>,
48
49 #[clap(
50 short = 'c',
51 long,
52 num_args = 0..,
53 help = "Execute the given SQL string(s), then exit.",
54 value_parser(parse_command)
55 )]
56 pub commands: Vec<String>,
57
58 #[clap(long, help = "Path to the configuration file")]
59 pub config: Option<String>,
60
61 #[clap(long, help = "Use the FlightSQL client defined in your config")]
62 pub flightsql: bool,
63
64 #[clap(long, help = "Run DDL prior to executing")]
65 pub run_ddl: bool,
66
67 #[clap(long, short, help = "Only show how long the query took to run")]
68 pub time: bool,
69
70 #[cfg(feature = "experimental-flightsql-server")]
71 #[clap(long, help = "Start a FlightSQL server")]
72 pub serve: bool,
73
74 #[clap(long, short, help = "Benchmark the provided query")]
75 pub bench: bool,
76
77 #[clap(
78 long,
79 help = "Print a summary of the query's execution plan and statistics"
80 )]
81 pub analyze: bool,
82
83 #[clap(long, help = "Run the provided query before running the benchmark")]
84 pub run_before: Option<String>,
85
86 #[clap(long, help = "Save the benchmark results to a file")]
87 pub save: Option<PathBuf>,
88
89 #[clap(long, help = "Append the benchmark results to an existing file")]
90 pub append: bool,
91
92 #[clap(short = 'n', help = "Set the number of benchmark iterations to run")]
93 pub benchmark_iterations: Option<usize>,
94
95 #[cfg(any(feature = "flightsql", feature = "experimental-flightsql-server"))]
96 #[clap(long, help = "Set the host and port to be used for FlightSQL")]
97 pub flightsql_host: Option<String>,
98
99 #[clap(
100 long,
101 short,
102 help = "Path to save output to. Type is inferred from file suffix"
103 )]
104 pub output: Option<PathBuf>,
105}
106
107impl DftArgs {
108 pub fn config_path(&self) -> PathBuf {
109 if let Some(config) = self.config.as_ref() {
110 Path::new(config).to_path_buf()
111 } else {
112 let mut config = get_data_dir();
113 config.push("config.toml");
114 config
115 }
116 }
117}
118
119fn parse_valid_file(file: &str) -> std::result::Result<PathBuf, String> {
120 let path = PathBuf::from(file);
121 if !path.exists() {
122 Err(format!("File does not exist: '{file}'"))
123 } else if !path.is_file() {
124 Err(format!("Exists but is not a file: '{file}'"))
125 } else {
126 Ok(path)
127 }
128}
129
130fn parse_command(command: &str) -> std::result::Result<String, String> {
131 if !command.is_empty() {
132 Ok(command.to_string())
133 } else {
134 Err("-c flag expects only non empty commands".to_string())
135 }
136}