mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-12-24 19:42:07 +03:00
Simplify option parsing using clap apis.
This commit is contained in:
parent
c182a062ce
commit
ef0230d65a
@ -24,7 +24,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use atty::Stream;
|
use atty::Stream;
|
||||||
use clap::{ArgAction, ArgMatches, Command};
|
use clap::{value_parser, ArgAction, ArgMatches, Command};
|
||||||
|
|
||||||
use crate::cli;
|
use crate::cli;
|
||||||
use crate::cli::CliError;
|
use crate::cli::CliError;
|
||||||
@ -72,6 +72,17 @@ pub enum OutputType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn app(version: &str) -> Command {
|
pub fn app(version: &str) -> Command {
|
||||||
|
let ClientOptions {
|
||||||
|
connect_timeout: default_connect_timeout,
|
||||||
|
max_redirect: default_max_redirect,
|
||||||
|
timeout: default_timeout,
|
||||||
|
..
|
||||||
|
} = ClientOptions::default();
|
||||||
|
|
||||||
|
let default_connect_timeout = default_connect_timeout.as_secs();
|
||||||
|
let default_max_redirect = default_max_redirect.unwrap();
|
||||||
|
let default_timeout = default_timeout.as_secs();
|
||||||
|
|
||||||
Command::new("hurl")
|
Command::new("hurl")
|
||||||
.about("Run hurl FILE(s) or standard input")
|
.about("Run hurl FILE(s) or standard input")
|
||||||
.disable_colored_help(true)
|
.disable_colored_help(true)
|
||||||
@ -107,6 +118,8 @@ pub fn app(version: &str) -> Command {
|
|||||||
.long("connect-timeout")
|
.long("connect-timeout")
|
||||||
.value_name("SECONDS")
|
.value_name("SECONDS")
|
||||||
.help("Maximum time allowed for connection")
|
.help("Maximum time allowed for connection")
|
||||||
|
.default_value(default_connect_timeout.to_string())
|
||||||
|
.value_parser(value_parser!(u64))
|
||||||
.num_args(1)
|
.num_args(1)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -135,7 +148,7 @@ pub fn app(version: &str) -> Command {
|
|||||||
clap::Arg::new("file_root")
|
clap::Arg::new("file_root")
|
||||||
.long("file-root")
|
.long("file-root")
|
||||||
.value_name("DIR")
|
.value_name("DIR")
|
||||||
.help("Set root filesystem to import file (default is current directory)")
|
.help("Set root filesystem to import files (default is current directory)")
|
||||||
.num_args(1)
|
.num_args(1)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -149,7 +162,7 @@ pub fn app(version: &str) -> Command {
|
|||||||
clap::Arg::new("glob")
|
clap::Arg::new("glob")
|
||||||
.long("glob")
|
.long("glob")
|
||||||
.value_name("GLOB")
|
.value_name("GLOB")
|
||||||
.help("Specify input files that match the given blob. Multiple glob flags may be used")
|
.help("Specify input files that match the given GLOB. Multiple glob flags may be used")
|
||||||
.action(ArgAction::Append)
|
.action(ArgAction::Append)
|
||||||
.number_of_values(1)
|
.number_of_values(1)
|
||||||
)
|
)
|
||||||
@ -191,8 +204,10 @@ pub fn app(version: &str) -> Command {
|
|||||||
clap::Arg::new("max_redirects")
|
clap::Arg::new("max_redirects")
|
||||||
.long("max-redirs")
|
.long("max-redirs")
|
||||||
.value_name("NUM")
|
.value_name("NUM")
|
||||||
.help("Maximum number of redirects allowed")
|
.help("Maximum number of redirects allowed, -1 for unlimited redirects")
|
||||||
|
.default_value(default_max_redirect.to_string())
|
||||||
.allow_hyphen_values(true)
|
.allow_hyphen_values(true)
|
||||||
|
.value_parser(value_parser!(i32).range(-1..))
|
||||||
.num_args(1)
|
.num_args(1)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -201,7 +216,9 @@ pub fn app(version: &str) -> Command {
|
|||||||
.short('m')
|
.short('m')
|
||||||
.value_name("NUM")
|
.value_name("NUM")
|
||||||
.help("Maximum time allowed for the transfer")
|
.help("Maximum time allowed for the transfer")
|
||||||
|
.default_value(default_timeout.to_string())
|
||||||
.allow_hyphen_values(true)
|
.allow_hyphen_values(true)
|
||||||
|
.value_parser(value_parser!(u64))
|
||||||
.num_args(1)
|
.num_args(1)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -245,14 +262,14 @@ pub fn app(version: &str) -> Command {
|
|||||||
clap::Arg::new("junit")
|
clap::Arg::new("junit")
|
||||||
.long("report-junit")
|
.long("report-junit")
|
||||||
.value_name("FILE")
|
.value_name("FILE")
|
||||||
.help("Write a Junit XML report to the given file")
|
.help("Write a Junit XML report to FILE")
|
||||||
.num_args(1)
|
.num_args(1)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
clap::Arg::new("report_html")
|
clap::Arg::new("report_html")
|
||||||
.long("report-html")
|
.long("report-html")
|
||||||
.value_name("DIR")
|
.value_name("DIR")
|
||||||
.help("Generate HTML report to dir")
|
.help("Generate HTML report to DIR")
|
||||||
.num_args(1)
|
.num_args(1)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -267,6 +284,8 @@ pub fn app(version: &str) -> Command {
|
|||||||
.value_name("ENTRY_NUMBER")
|
.value_name("ENTRY_NUMBER")
|
||||||
.help("Execute Hurl file to ENTRY_NUMBER (starting at 1)")
|
.help("Execute Hurl file to ENTRY_NUMBER (starting at 1)")
|
||||||
.conflicts_with("interactive")
|
.conflicts_with("interactive")
|
||||||
|
.allow_hyphen_values(true)
|
||||||
|
.value_parser(value_parser!(u32).range(1..))
|
||||||
.num_args(1)
|
.num_args(1)
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
@ -316,8 +335,9 @@ pub fn app(version: &str) -> Command {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses command line options `matches`.
|
||||||
pub fn parse_options(matches: &ArgMatches) -> Result<CliOptions, CliError> {
|
pub fn parse_options(matches: &ArgMatches) -> Result<CliOptions, CliError> {
|
||||||
let cacert_file = match get_string(matches, "cacert_file") {
|
let cacert_file = match get::<String>(matches, "cacert_file") {
|
||||||
None => None,
|
None => None,
|
||||||
Some(filename) => {
|
Some(filename) => {
|
||||||
if !Path::new(&filename).is_file() {
|
if !Path::new(&filename).is_file() {
|
||||||
@ -330,24 +350,15 @@ pub fn parse_options(matches: &ArgMatches) -> Result<CliOptions, CliError> {
|
|||||||
};
|
};
|
||||||
let color = output_color(matches);
|
let color = output_color(matches);
|
||||||
let compressed = has_flag(matches, "compressed");
|
let compressed = has_flag(matches, "compressed");
|
||||||
let connect_timeout = match get_string(matches, "connect_timeout") {
|
let connect_timeout = get::<u64>(matches, "connect_timeout").unwrap();
|
||||||
None => ClientOptions::default().connect_timeout,
|
let connect_timeout = Duration::from_secs(connect_timeout);
|
||||||
Some(s) => match s.parse::<u64>() {
|
let cookie_input_file = get::<String>(matches, "cookies_input_file");
|
||||||
Ok(n) => Duration::from_secs(n),
|
let cookie_output_file = get::<String>(matches, "cookies_output_file");
|
||||||
Err(_) => {
|
|
||||||
return Err(CliError {
|
|
||||||
message: "connect-timeout option can not be parsed".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let cookie_input_file = get_string(matches, "cookies_input_file");
|
|
||||||
let cookie_output_file = get_string(matches, "cookies_output_file");
|
|
||||||
let fail_fast = !has_flag(matches, "fail_at_end");
|
let fail_fast = !has_flag(matches, "fail_at_end");
|
||||||
let file_root = get_string(matches, "file_root");
|
let file_root = get::<String>(matches, "file_root");
|
||||||
let follow_location = has_flag(matches, "follow_location");
|
let follow_location = has_flag(matches, "follow_location");
|
||||||
let glob_files = match_glob_files(matches)?;
|
let glob_files = match_glob_files(matches)?;
|
||||||
let report_html = get_string(matches, "report_html");
|
let report_html = get::<String>(matches, "report_html");
|
||||||
let html_dir = if let Some(dir) = report_html {
|
let html_dir = if let Some(dir) = report_html {
|
||||||
let path = Path::new(&dir);
|
let path = Path::new(&dir);
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
@ -373,21 +384,14 @@ pub fn parse_options(matches: &ArgMatches) -> Result<CliOptions, CliError> {
|
|||||||
let include = has_flag(matches, "include");
|
let include = has_flag(matches, "include");
|
||||||
let insecure = has_flag(matches, "insecure");
|
let insecure = has_flag(matches, "insecure");
|
||||||
let interactive = has_flag(matches, "interactive");
|
let interactive = has_flag(matches, "interactive");
|
||||||
let junit_file = get_string(matches, "junit");
|
let junit_file = get::<String>(matches, "junit");
|
||||||
let max_redirect = match get_string(matches, "max_redirects").as_deref() {
|
let max_redirect = get::<i32>(matches, "max_redirects").unwrap();
|
||||||
None => Some(50),
|
let max_redirect = match max_redirect {
|
||||||
Some("-1") => None,
|
m if m == -1 => None,
|
||||||
Some(s) => match s.parse::<usize>() {
|
m => Some(m as usize),
|
||||||
Ok(x) => Some(x),
|
|
||||||
Err(_) => {
|
|
||||||
return Err(CliError {
|
|
||||||
message: "max_redirs option can not be parsed".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
let no_proxy = get_string(matches, "proxy");
|
let no_proxy = get::<String>(matches, "proxy");
|
||||||
let output = get_string(matches, "output");
|
let output = get::<String>(matches, "output");
|
||||||
let test = has_flag(matches, "test");
|
let test = has_flag(matches, "test");
|
||||||
let output_type = if has_flag(matches, "json") {
|
let output_type = if has_flag(matches, "json") {
|
||||||
OutputType::Json
|
OutputType::Json
|
||||||
@ -396,22 +400,13 @@ pub fn parse_options(matches: &ArgMatches) -> Result<CliOptions, CliError> {
|
|||||||
} else {
|
} else {
|
||||||
OutputType::ResponseBody
|
OutputType::ResponseBody
|
||||||
};
|
};
|
||||||
let proxy = get_string(matches, "proxy");
|
let proxy = get::<String>(matches, "proxy");
|
||||||
let timeout = match get_string(matches, "max_time") {
|
let timeout = get::<u64>(matches, "max_time").unwrap();
|
||||||
None => ClientOptions::default().timeout,
|
let timeout = Duration::from_secs(timeout);
|
||||||
Some(s) => match s.parse::<u64>() {
|
let to_entry = get::<u32>(matches, "to_entry").map(|x| x as usize);
|
||||||
Ok(n) => Duration::from_secs(n),
|
let user = get::<String>(matches, "user");
|
||||||
Err(_) => {
|
let user_agent = get::<String>(matches, "user_agent");
|
||||||
return Err(CliError {
|
let variables = variables(matches)?;
|
||||||
message: "max_time option can not be parsed".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
let to_entry = to_entry(matches)?;
|
|
||||||
let user = get_string(matches, "user");
|
|
||||||
let user_agent = get_string(matches, "user_agent");
|
|
||||||
let variables = variables(matches.clone())?;
|
|
||||||
let very_verbose = has_flag(matches, "very_verbose");
|
let very_verbose = has_flag(matches, "very_verbose");
|
||||||
let verbose = has_flag(matches, "verbose") || has_flag(matches, "interactive") || very_verbose;
|
let verbose = has_flag(matches, "verbose") || has_flag(matches, "interactive") || very_verbose;
|
||||||
|
|
||||||
@ -464,23 +459,11 @@ pub fn output_color(matches: &ArgMatches) -> bool {
|
|||||||
atty::is(Stream::Stdout)
|
atty::is(Stream::Stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_entry(matches: &ArgMatches) -> Result<Option<usize>, CliError> {
|
/// Returns a map of variables from the command line options `matches`.
|
||||||
match get_string(matches, "to_entry") {
|
fn variables(matches: &ArgMatches) -> Result<HashMap<String, Value>, CliError> {
|
||||||
Some(value) => match value.parse() {
|
|
||||||
Ok(v) => Ok(Some(v)),
|
|
||||||
Err(_) => Err(CliError {
|
|
||||||
message: "Invalid value for option --to-entry - must be a positive integer!"
|
|
||||||
.to_string(),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
None => Ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn variables(matches: ArgMatches) -> Result<HashMap<String, Value>, CliError> {
|
|
||||||
let mut variables = HashMap::new();
|
let mut variables = HashMap::new();
|
||||||
|
|
||||||
// use environment variables prefix by HURL_
|
// Use environment variables prefix by HURL_
|
||||||
for (env_name, env_value) in env::vars() {
|
for (env_name, env_value) in env::vars() {
|
||||||
if let Some(name) = env_name.strip_prefix("HURL_") {
|
if let Some(name) = env_name.strip_prefix("HURL_") {
|
||||||
let value = cli::parse_variable_value(env_value.as_str())?;
|
let value = cli::parse_variable_value(env_value.as_str())?;
|
||||||
@ -488,7 +471,7 @@ fn variables(matches: ArgMatches) -> Result<HashMap<String, Value>, CliError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(filename) = get_string(&matches, "variables_file") {
|
if let Some(filename) = get::<String>(matches, "variables_file") {
|
||||||
let path = Path::new(&filename);
|
let path = Path::new(&filename);
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
return Err(CliError {
|
return Err(CliError {
|
||||||
@ -516,7 +499,7 @@ fn variables(matches: ArgMatches) -> Result<HashMap<String, Value>, CliError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(input) = get_strings(&matches, "variable") {
|
if let Some(input) = get_strings(matches, "variable") {
|
||||||
for s in input {
|
for s in input {
|
||||||
let (name, value) = cli::parse_variable(&s)?;
|
let (name, value) = cli::parse_variable(&s)?;
|
||||||
variables.insert(name.to_string(), value);
|
variables.insert(name.to_string(), value);
|
||||||
@ -526,12 +509,7 @@ fn variables(matches: ArgMatches) -> Result<HashMap<String, Value>, CliError> {
|
|||||||
Ok(variables)
|
Ok(variables)
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/// Returns a list of path names from the command line options `matches`.
|
||||||
/// Returns a list of path names that match `matches`.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
/// * `matches` - A pattern to be matched
|
|
||||||
///
|
|
||||||
fn match_glob_files(matches: &ArgMatches) -> Result<Vec<String>, CliError> {
|
fn match_glob_files(matches: &ArgMatches) -> Result<Vec<String>, CliError> {
|
||||||
let mut filenames = vec![];
|
let mut filenames = vec![];
|
||||||
if let Some(exprs) = get_strings(matches, "glob") {
|
if let Some(exprs) = get_strings(matches, "glob") {
|
||||||
@ -566,16 +544,19 @@ fn match_glob_files(matches: &ArgMatches) -> Result<Vec<String>, CliError> {
|
|||||||
Ok(filenames)
|
Ok(filenames)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_string(matches: &ArgMatches, name: &str) -> Option<String> {
|
/// Returns a optional value of type `T` from the command line `matches` given the option `name`.
|
||||||
matches.get_one::<String>(name).map(|x| x.to_string())
|
fn get<T: Clone + Send + Sync + 'static>(matches: &ArgMatches, name: &str) -> Option<T> {
|
||||||
|
matches.get_one::<T>(name).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a list of `String` from the command line options `matches` given the option `name`.
|
||||||
pub fn get_strings(matches: &ArgMatches, name: &str) -> Option<Vec<String>> {
|
pub fn get_strings(matches: &ArgMatches, name: &str) -> Option<Vec<String>> {
|
||||||
matches
|
matches
|
||||||
.get_many::<String>(name)
|
.get_many::<String>(name)
|
||||||
.map(|v| v.map(|x| x.to_string()).collect())
|
.map(|v| v.map(|x| x.to_string()).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the command line options `matches` has a given flag `name`.
|
||||||
pub fn has_flag(matches: &ArgMatches, name: &str) -> bool {
|
pub fn has_flag(matches: &ArgMatches, name: &str) -> bool {
|
||||||
matches.get_one::<bool>(name) == Some(&true)
|
matches.get_one::<bool>(name) == Some(&true)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user