Skip to content

Commit 59505b1

Browse files
authored
fix: Make sure table name handling is done by the crate instead of clap (#92)
* fix: Make sure table name handling is done by the crate instead of clap To allow supporting actual short hand we have to ensure that we use a custom TypedValueParser that is able to parse the refs correctly and align every abbrv with its long name and corresponding enum. I don't like this code because it's a lot of gymnastics for supporting command flags (I feel like clap is very much too powerful for our needs but maybe I am used to Go's stdlib provided argument handler). There's also some non-trivial complexity in the implementation (side effect of how clap does things). * fix: extend TypedValueParser with possible_values Allows us to clarify the possible values for the tables argument. * fix: remove hidden blocks since aliases are documented
1 parent f7b022d commit 59505b1

File tree

1 file changed

+67
-2
lines changed

1 file changed

+67
-2
lines changed

tpchgen-cli/src/main.rs

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@ use crate::parquet::*;
4040
use crate::statistics::WriteStatistics;
4141
use crate::tbl::*;
4242
use ::parquet::basic::Compression;
43+
use clap::builder::TypedValueParser;
4344
use clap::{Parser, ValueEnum};
4445
use log::{debug, info, LevelFilter};
4546
use std::fmt::Display;
4647
use std::fs::{self, File};
4748
use std::io::{self, BufWriter, Stdout, Write};
4849
use std::path::PathBuf;
50+
use std::str::FromStr;
4951
use std::time::Instant;
5052
use tpchgen::distribution::Distributions;
5153
use tpchgen::generators::{
@@ -71,7 +73,7 @@ struct Cli {
7173
output_dir: PathBuf,
7274

7375
/// Which tables to generate (default: all)
74-
#[arg(short = 'T', long = "tables", value_enum)]
76+
#[arg(short = 'T', long = "tables", value_delimiter = ',', value_parser = TableValueParser)]
7577
tables: Option<Vec<Table>>,
7678

7779
/// Number of parts to generate (manual parallel generation)
@@ -115,7 +117,7 @@ struct Cli {
115117
stdout: bool,
116118
}
117119

118-
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
120+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
119121
enum Table {
120122
Nation,
121123
Region,
@@ -133,6 +135,69 @@ impl Display for Table {
133135
}
134136
}
135137

138+
#[derive(Debug, Clone)]
139+
struct TableValueParser;
140+
141+
impl TypedValueParser for TableValueParser {
142+
type Value = Table;
143+
144+
/// Parse the value into a Table enum.
145+
fn parse_ref(
146+
&self,
147+
cmd: &clap::Command,
148+
_: Option<&clap::Arg>,
149+
value: &std::ffi::OsStr,
150+
) -> Result<Self::Value, clap::Error> {
151+
let value = value
152+
.to_str()
153+
.ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidValue).with_cmd(cmd))?;
154+
Table::from_str(value)
155+
.map_err(|_| clap::Error::new(clap::error::ErrorKind::InvalidValue).with_cmd(cmd))
156+
}
157+
158+
fn possible_values(
159+
&self,
160+
) -> Option<Box<dyn Iterator<Item = clap::builder::PossibleValue> + '_>> {
161+
Some(Box::new(
162+
[
163+
clap::builder::PossibleValue::new("Region").help("Region table (alias: r)"),
164+
clap::builder::PossibleValue::new("Nation").help("Nation table (alias: n)"),
165+
clap::builder::PossibleValue::new("Supplier").help("Supplier table (alias: s)"),
166+
clap::builder::PossibleValue::new("Customer").help("Customer table (alias: c)"),
167+
clap::builder::PossibleValue::new("Part").help("Part table (alias: P)"),
168+
clap::builder::PossibleValue::new("PartSupp").help("PartSupp table (alias: S)"),
169+
clap::builder::PossibleValue::new("Orders").help("Orders table (alias: O)"),
170+
clap::builder::PossibleValue::new("LineItem").help("LineItem table (alias: L)"),
171+
]
172+
.into_iter(),
173+
))
174+
}
175+
}
176+
177+
impl FromStr for Table {
178+
type Err = &'static str;
179+
180+
/// Returns the table enum value from the given string full name or abbreviation
181+
///
182+
/// The original dbgen tool allows some abbreviations to mean two different tables
183+
/// like 'p' which aliases to both 'part' and 'partsupp'. This implementation does
184+
/// not support this since it just adds unnecessary complexity and confusion so we
185+
/// only support the exclusive abbreviations.
186+
fn from_str(s: &str) -> Result<Self, Self::Err> {
187+
match s {
188+
"n" | "nation" => Ok(Table::Nation),
189+
"r" | "region" => Ok(Table::Region),
190+
"s" | "supplier" => Ok(Table::Supplier),
191+
"P" | "part" => Ok(Table::Part),
192+
"S" | "partsupp" => Ok(Table::Partsupp),
193+
"c" | "customer" => Ok(Table::Customer),
194+
"O" | "orders" => Ok(Table::Orders),
195+
"L" | "lineitem" => Ok(Table::Lineitem),
196+
_ => Err("Invalid table name {s}"),
197+
}
198+
}
199+
}
200+
136201
impl Table {
137202
fn name(&self) -> &'static str {
138203
match self {

0 commit comments

Comments
 (0)