datafusion_dft/
args.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Command line argument parsing: [`DftArgs`]
19
20use 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}