Add --interactive option

This commit is contained in:
Fabrice Reix 2021-01-16 14:18:21 +01:00
parent d6767f7bc1
commit da2dc6493b
9 changed files with 119 additions and 5 deletions

28
Cargo.lock generated
View File

@ -333,6 +333,7 @@ dependencies = [
"regex",
"serde",
"serde_json",
"termion",
"url",
]
@ -456,6 +457,12 @@ dependencies = [
"autocfg 1.0.0",
]
[[package]]
name = "numtoa"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
[[package]]
name = "openssl-probe"
version = "0.1.2"
@ -687,6 +694,15 @@ version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
[[package]]
name = "redox_termios"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
dependencies = [
"redox_syscall",
]
[[package]]
name = "regex"
version = "1.3.4"
@ -807,6 +823,18 @@ dependencies = [
"winapi",
]
[[package]]
name = "termion"
version = "1.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c22cec9d8978d906be5ac94bceb5a010d885c626c4c8855721a4dbd20e3ac905"
dependencies = [
"libc",
"numtoa",
"redox_syscall",
"redox_termios",
]
[[package]]
name = "textwrap"
version = "0.11.0"

View File

@ -195,6 +195,11 @@ If you want to combine results from different Hurl executions in a unique html r
Include the HTTP headers in the output (last entry).
### --interactive {#interactive}
Stop between requests.
This is similar to a break point, You can then continue (Press C) or quit (Press Q).
### --json <file> {#json}

View File

@ -28,6 +28,7 @@ libxml = "0.2.12"
regex = "1.1.0"
serde = "1.0.104"
serde_json = "1.0.40"
termion = "1.5.5"
url = "2.1.0"

View File

@ -0,0 +1,42 @@
extern crate termion;
use std::io::{stderr, stdin, Write};
use termion::event::Key;
use termion::input::TermRead;
use termion::raw::IntoRawMode;
pub fn pre_entry() -> bool {
let stdin = stdin();
let mut stderr = stderr().into_raw_mode().unwrap();
eprintln!("\n\rinteractive mode:");
write!(
stderr,
"\rPress Q (Quit) or C (Continue)\n\n\r{}",
termion::cursor::Hide
)
.unwrap();
stderr.flush().unwrap();
let mut exit = false;
for c in stdin.keys() {
print!("\r");
match c.unwrap() {
Key::Char('q') => {
exit = true;
break;
}
Key::Char('c') => {
break;
}
_ => {}
}
}
print!("{}\r{}", termion::clear::CurrentLine, termion::cursor::Show);
exit
}
pub fn post_entry() -> bool {
false
}

View File

@ -22,4 +22,5 @@ pub use self::logger::{
};
mod color;
pub mod interactive;
mod logger;

View File

@ -29,6 +29,7 @@ use chrono::{DateTime, Local};
use clap::{AppSettings, ArgMatches};
use hurl::cli;
use hurl::cli::interactive;
use hurl::html;
use hurl::http;
use hurl::runner;
@ -44,6 +45,7 @@ pub struct CLIOptions {
pub color: bool,
pub fail_fast: bool,
pub insecure: bool,
pub interactive: bool,
pub variables: HashMap<String, String>,
pub to_entry: Option<usize>,
pub follow_location: bool,
@ -168,11 +170,23 @@ fn execute(
}
Some(filename) => filename,
};
let pre_entry = if cli_options.interactive {
interactive::pre_entry
} else {
|| false
};
let post_entry = if cli_options.interactive {
interactive::post_entry
} else {
|| false
};
let options = RunnerOptions {
fail_fast: cli_options.fail_fast,
variables: cli_options.variables,
to_entry: cli_options.to_entry,
context_dir,
pre_entry,
post_entry,
};
runner::run_hurl_file(
hurl_file,
@ -411,6 +425,12 @@ fn app() -> clap::App<'static, 'static> {
.long("insecure")
.help("Allow insecure SSL connections"),
)
.arg(
clap::Arg::with_name("interactive")
.long("interactive")
.conflicts_with("to_entry")
.help("Turn on interactive mode"),
)
.arg(
clap::Arg::with_name("json")
.long("json")
@ -470,6 +490,7 @@ fn app() -> clap::App<'static, 'static> {
clap::Arg::with_name("to_entry")
.long("to-entry")
.value_name("ENTRY_NUMBER")
.conflicts_with("interactive")
.help("Execute hurl file to ENTRY_NUMBER (starting at 1)")
.takes_value(true),
)
@ -519,7 +540,7 @@ pub fn unwrap_or_exit<T>(
}
fn parse_options(matches: ArgMatches) -> Result<CLIOptions, CLIError> {
let verbose = matches.is_present("verbose");
let verbose = matches.is_present("verbose") || matches.is_present("interactive");
let color = output_color(matches.clone());
let fail_fast = !matches.is_present("fail_at_end");
let variables = variables(matches.clone())?;
@ -569,7 +590,7 @@ fn parse_options(matches: ArgMatches) -> Result<CLIOptions, CLIError> {
};
let compressed = matches.is_present("compressed");
let user = matches.value_of("user").map(|x| x.to_string());
let interactive = matches.is_present("interactive");
Ok(CLIOptions {
verbose,
color,
@ -586,6 +607,7 @@ fn parse_options(matches: ArgMatches) -> Result<CLIOptions, CLIError> {
connect_timeout,
compressed,
user,
interactive,
})
}
@ -615,7 +637,7 @@ fn main() {
Some(value) => Some(value.to_string()),
_ => None,
};
let verbose = matches.is_present("verbose");
let verbose = matches.is_present("verbose") || matches.is_present("interactive");
let log_verbose = cli::make_logger_verbose(verbose);
let color = output_color(matches.clone());
let log_error_message = cli::make_logger_error_message(color);
@ -663,7 +685,7 @@ fn main() {
&log_error_message,
);
if hurl_result.errors().is_empty() {
if hurl_result.errors().is_empty() && !cli_options.interactive {
// default
// last entry + response + body
if let Some(entry_result) = hurl_result.entries.last() {

View File

@ -28,6 +28,8 @@ pub struct RunnerOptions {
pub variables: HashMap<String, String>,
pub to_entry: Option<usize>,
pub context_dir: String,
pub pre_entry: fn() -> bool,
pub post_entry: fn() -> bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]

View File

@ -70,6 +70,8 @@ use super::value::Value;
/// variables,
/// to_entry: None,
/// context_dir: "current_dir".to_string(),
/// pre_entry: || true,
/// post_entry: || true,
/// };
///
/// // Run the hurl file
@ -117,6 +119,11 @@ pub fn run(
.enumerate()
.collect::<Vec<(usize, Entry)>>()
{
let exit = (options.pre_entry)();
if exit {
break;
}
let entry_result = entry::run(
entry,
http_client,
@ -130,10 +137,12 @@ pub fn run(
for e in entry_result.errors.clone() {
log_error(&e, false);
}
if options.fail_fast && !entry_result.errors.is_empty() {
let exit = (options.post_entry)();
if exit || (options.fail_fast && !entry_result.errors.is_empty()) {
break;
}
}
let time_in_ms = start.elapsed().as_millis();
let success = entries
.iter()

View File

@ -67,6 +67,8 @@ fn test_hurl_file() {
variables,
to_entry: None,
context_dir: "current_dir".to_string(),
pre_entry: || true,
post_entry: || true,
};
let log_verbose: fn(&str) = log_verbose;
@ -206,6 +208,8 @@ fn test_hello() {
variables,
to_entry: None,
context_dir: "current_dir".to_string(),
pre_entry: || true,
post_entry: || true,
};
let log_verbose: fn(&str) = log_verbose;
let log_error_message: fn(bool, &str) = log_error_message;