use anyhow::{Error, anyhow};
use clap::{Args, Parser, Subcommand};
const ABOUT: &str = r#"Bench Names:
Each benchmark has a unique name, this name is used by many options listed below.
The name is derived from an alphabetical sorting of its tags so you wont find it directly in the bench
implementation but it will be listed in the `list` command.
Tag Filters:
Many options below take tag filters that specify which benches to include.
Tag filters specify which benches to include and the filter results are unioned.
So:
* The filter "foo=some_value" will include only benches with the tag key `foo` and the tag value `some_value`
* The filter "foo=some_value bar=another_value" will include only benches that match "foo=some_value" and "bar=another_value"
* The filter "" will include all benches
A filters tags can also be separated by commas allowing names to function as filters.
So: foo=some_value,bar=another_value is a name but it can also be used where a filter is accepted."#;
#[derive(Subcommand, Clone)]
pub enum Command {
#[clap(verbatim_doc_comment)]
List,
#[clap(verbatim_doc_comment)]
CloudSetup {
#[clap(verbatim_doc_comment)]
filter: String,
},
#[clap(verbatim_doc_comment)]
CloudRun(RunArgs),
#[clap(verbatim_doc_comment)]
CloudCleanup,
#[clap(verbatim_doc_comment)]
CloudSetupRunCleanup(RunArgs),
#[clap(verbatim_doc_comment)]
LocalRun(RunArgs),
#[clap(verbatim_doc_comment)]
BaselineSet,
#[clap(verbatim_doc_comment)]
BaselineClear,
#[clap(verbatim_doc_comment)]
GenerateWebpage,
#[clap(verbatim_doc_comment)]
Results {
#[clap(long, verbatim_doc_comment)]
ignore_baseline: bool,
#[clap(verbatim_doc_comment)]
filter: Option<String>,
},
#[clap(verbatim_doc_comment)]
CompareByName { filter: String },
#[clap(verbatim_doc_comment)]
CompareByTags { filter: String },
#[clap(verbatim_doc_comment)]
InternalRun(RunArgs),
}
#[derive(Args, Clone)]
pub struct RunArgs {
#[clap(long, verbatim_doc_comment, value_delimiter = ',')]
pub profilers: Vec<String>,
#[clap(long, verbatim_doc_comment)]
pub bench_length_seconds: Option<u32>,
#[clap(long, verbatim_doc_comment)]
pub operations_per_second: Option<u64>,
#[clap(verbatim_doc_comment)]
pub filter: Option<String>,
}
impl RunArgs {
pub fn filter(&self) -> String {
match &self.filter {
Some(filter) => filter.replace(',', " "),
None => String::new(),
}
}
}
#[derive(Parser)]
#[clap(about=ABOUT)]
pub struct WindsockArgs {
#[command(subcommand)]
pub command: Option<Command>,
#[clap(long, hide(true))]
list: bool,
#[clap(long, hide(true))]
format: Option<NextestFormat>,
#[clap(long, hide(true))]
ignored: bool,
#[clap(long, hide(true))]
pub exact: Option<String>,
#[clap(long, hide(true))]
nocapture: bool,
}
#[derive(clap::ValueEnum, Clone, Copy)]
enum NextestFormat {
Terse,
}
impl WindsockArgs {
pub fn nextest_list(&self) -> bool {
self.list
}
pub fn nextest_list_all(&self) -> bool {
self.list && matches!(&self.format, Some(NextestFormat::Terse)) && !self.ignored
}
pub fn nextest_list_ignored(&self) -> bool {
self.list && matches!(&self.format, Some(NextestFormat::Terse)) && self.ignored
}
pub fn nextest_run_by_name(&self) -> Option<&str> {
if self.nocapture {
self.exact.as_deref()
} else {
None
}
}
pub fn nextest_invalid_args(&self) -> Option<Error> {
if self.format.is_some() && self.list {
Some(anyhow!(
"`--format` only exists for nextest compatibility and is not supported without `--list`"
))
} else if self.nocapture && self.exact.is_none() {
Some(anyhow!(
"`--nocapture` only exists for nextest compatibility and is not supported without `--exact`"
))
} else if self.exact.is_some() && !self.nocapture {
Some(anyhow!(
"`--exact` only exists for nextest compatibility and is not supported without `--nocapture`"
))
} else {
None
}
}
}