mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-12-24 03:21:40 +03:00
Add --interactive option
This commit is contained in:
parent
d6767f7bc1
commit
da2dc6493b
28
Cargo.lock
generated
28
Cargo.lock
generated
@ -333,6 +333,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"termion",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -456,6 +457,12 @@ dependencies = [
|
|||||||
"autocfg 1.0.0",
|
"autocfg 1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "numtoa"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openssl-probe"
|
name = "openssl-probe"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@ -687,6 +694,15 @@ version = "0.1.56"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
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]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.3.4"
|
version = "1.3.4"
|
||||||
@ -807,6 +823,18 @@ dependencies = [
|
|||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
@ -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).
|
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}
|
### --json <file> {#json}
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ libxml = "0.2.12"
|
|||||||
regex = "1.1.0"
|
regex = "1.1.0"
|
||||||
serde = "1.0.104"
|
serde = "1.0.104"
|
||||||
serde_json = "1.0.40"
|
serde_json = "1.0.40"
|
||||||
|
termion = "1.5.5"
|
||||||
url = "2.1.0"
|
url = "2.1.0"
|
||||||
|
|
||||||
|
|
||||||
|
42
packages/hurl/src/cli/interactive.rs
Normal file
42
packages/hurl/src/cli/interactive.rs
Normal 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
|
||||||
|
}
|
@ -22,4 +22,5 @@ pub use self::logger::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
mod color;
|
mod color;
|
||||||
|
pub mod interactive;
|
||||||
mod logger;
|
mod logger;
|
||||||
|
@ -29,6 +29,7 @@ use chrono::{DateTime, Local};
|
|||||||
use clap::{AppSettings, ArgMatches};
|
use clap::{AppSettings, ArgMatches};
|
||||||
|
|
||||||
use hurl::cli;
|
use hurl::cli;
|
||||||
|
use hurl::cli::interactive;
|
||||||
use hurl::html;
|
use hurl::html;
|
||||||
use hurl::http;
|
use hurl::http;
|
||||||
use hurl::runner;
|
use hurl::runner;
|
||||||
@ -44,6 +45,7 @@ pub struct CLIOptions {
|
|||||||
pub color: bool,
|
pub color: bool,
|
||||||
pub fail_fast: bool,
|
pub fail_fast: bool,
|
||||||
pub insecure: bool,
|
pub insecure: bool,
|
||||||
|
pub interactive: bool,
|
||||||
pub variables: HashMap<String, String>,
|
pub variables: HashMap<String, String>,
|
||||||
pub to_entry: Option<usize>,
|
pub to_entry: Option<usize>,
|
||||||
pub follow_location: bool,
|
pub follow_location: bool,
|
||||||
@ -168,11 +170,23 @@ fn execute(
|
|||||||
}
|
}
|
||||||
Some(filename) => filename,
|
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 {
|
let options = RunnerOptions {
|
||||||
fail_fast: cli_options.fail_fast,
|
fail_fast: cli_options.fail_fast,
|
||||||
variables: cli_options.variables,
|
variables: cli_options.variables,
|
||||||
to_entry: cli_options.to_entry,
|
to_entry: cli_options.to_entry,
|
||||||
context_dir,
|
context_dir,
|
||||||
|
pre_entry,
|
||||||
|
post_entry,
|
||||||
};
|
};
|
||||||
runner::run_hurl_file(
|
runner::run_hurl_file(
|
||||||
hurl_file,
|
hurl_file,
|
||||||
@ -411,6 +425,12 @@ fn app() -> clap::App<'static, 'static> {
|
|||||||
.long("insecure")
|
.long("insecure")
|
||||||
.help("Allow insecure SSL connections"),
|
.help("Allow insecure SSL connections"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
clap::Arg::with_name("interactive")
|
||||||
|
.long("interactive")
|
||||||
|
.conflicts_with("to_entry")
|
||||||
|
.help("Turn on interactive mode"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
clap::Arg::with_name("json")
|
clap::Arg::with_name("json")
|
||||||
.long("json")
|
.long("json")
|
||||||
@ -470,6 +490,7 @@ fn app() -> clap::App<'static, 'static> {
|
|||||||
clap::Arg::with_name("to_entry")
|
clap::Arg::with_name("to_entry")
|
||||||
.long("to-entry")
|
.long("to-entry")
|
||||||
.value_name("ENTRY_NUMBER")
|
.value_name("ENTRY_NUMBER")
|
||||||
|
.conflicts_with("interactive")
|
||||||
.help("Execute hurl file to ENTRY_NUMBER (starting at 1)")
|
.help("Execute hurl file to ENTRY_NUMBER (starting at 1)")
|
||||||
.takes_value(true),
|
.takes_value(true),
|
||||||
)
|
)
|
||||||
@ -519,7 +540,7 @@ pub fn unwrap_or_exit<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_options(matches: ArgMatches) -> Result<CLIOptions, CLIError> {
|
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 color = output_color(matches.clone());
|
||||||
let fail_fast = !matches.is_present("fail_at_end");
|
let fail_fast = !matches.is_present("fail_at_end");
|
||||||
let variables = variables(matches.clone())?;
|
let variables = variables(matches.clone())?;
|
||||||
@ -569,7 +590,7 @@ fn parse_options(matches: ArgMatches) -> Result<CLIOptions, CLIError> {
|
|||||||
};
|
};
|
||||||
let compressed = matches.is_present("compressed");
|
let compressed = matches.is_present("compressed");
|
||||||
let user = matches.value_of("user").map(|x| x.to_string());
|
let user = matches.value_of("user").map(|x| x.to_string());
|
||||||
|
let interactive = matches.is_present("interactive");
|
||||||
Ok(CLIOptions {
|
Ok(CLIOptions {
|
||||||
verbose,
|
verbose,
|
||||||
color,
|
color,
|
||||||
@ -586,6 +607,7 @@ fn parse_options(matches: ArgMatches) -> Result<CLIOptions, CLIError> {
|
|||||||
connect_timeout,
|
connect_timeout,
|
||||||
compressed,
|
compressed,
|
||||||
user,
|
user,
|
||||||
|
interactive,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,7 +637,7 @@ fn main() {
|
|||||||
Some(value) => Some(value.to_string()),
|
Some(value) => Some(value.to_string()),
|
||||||
_ => None,
|
_ => 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 log_verbose = cli::make_logger_verbose(verbose);
|
||||||
let color = output_color(matches.clone());
|
let color = output_color(matches.clone());
|
||||||
let log_error_message = cli::make_logger_error_message(color);
|
let log_error_message = cli::make_logger_error_message(color);
|
||||||
@ -663,7 +685,7 @@ fn main() {
|
|||||||
&log_error_message,
|
&log_error_message,
|
||||||
);
|
);
|
||||||
|
|
||||||
if hurl_result.errors().is_empty() {
|
if hurl_result.errors().is_empty() && !cli_options.interactive {
|
||||||
// default
|
// default
|
||||||
// last entry + response + body
|
// last entry + response + body
|
||||||
if let Some(entry_result) = hurl_result.entries.last() {
|
if let Some(entry_result) = hurl_result.entries.last() {
|
||||||
|
@ -28,6 +28,8 @@ pub struct RunnerOptions {
|
|||||||
pub variables: HashMap<String, String>,
|
pub variables: HashMap<String, String>,
|
||||||
pub to_entry: Option<usize>,
|
pub to_entry: Option<usize>,
|
||||||
pub context_dir: String,
|
pub context_dir: String,
|
||||||
|
pub pre_entry: fn() -> bool,
|
||||||
|
pub post_entry: fn() -> bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -70,6 +70,8 @@ use super::value::Value;
|
|||||||
/// variables,
|
/// variables,
|
||||||
/// to_entry: None,
|
/// to_entry: None,
|
||||||
/// context_dir: "current_dir".to_string(),
|
/// context_dir: "current_dir".to_string(),
|
||||||
|
/// pre_entry: || true,
|
||||||
|
/// post_entry: || true,
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// // Run the hurl file
|
/// // Run the hurl file
|
||||||
@ -117,6 +119,11 @@ pub fn run(
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.collect::<Vec<(usize, Entry)>>()
|
.collect::<Vec<(usize, Entry)>>()
|
||||||
{
|
{
|
||||||
|
let exit = (options.pre_entry)();
|
||||||
|
if exit {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
let entry_result = entry::run(
|
let entry_result = entry::run(
|
||||||
entry,
|
entry,
|
||||||
http_client,
|
http_client,
|
||||||
@ -130,10 +137,12 @@ pub fn run(
|
|||||||
for e in entry_result.errors.clone() {
|
for e in entry_result.errors.clone() {
|
||||||
log_error(&e, false);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let time_in_ms = start.elapsed().as_millis();
|
let time_in_ms = start.elapsed().as_millis();
|
||||||
let success = entries
|
let success = entries
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -67,6 +67,8 @@ fn test_hurl_file() {
|
|||||||
variables,
|
variables,
|
||||||
to_entry: None,
|
to_entry: None,
|
||||||
context_dir: "current_dir".to_string(),
|
context_dir: "current_dir".to_string(),
|
||||||
|
pre_entry: || true,
|
||||||
|
post_entry: || true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let log_verbose: fn(&str) = log_verbose;
|
let log_verbose: fn(&str) = log_verbose;
|
||||||
@ -206,6 +208,8 @@ fn test_hello() {
|
|||||||
variables,
|
variables,
|
||||||
to_entry: None,
|
to_entry: None,
|
||||||
context_dir: "current_dir".to_string(),
|
context_dir: "current_dir".to_string(),
|
||||||
|
pre_entry: || true,
|
||||||
|
post_entry: || true,
|
||||||
};
|
};
|
||||||
let log_verbose: fn(&str) = log_verbose;
|
let log_verbose: fn(&str) = log_verbose;
|
||||||
let log_error_message: fn(bool, &str) = log_error_message;
|
let log_error_message: fn(bool, &str) = log_error_message;
|
||||||
|
Loading…
Reference in New Issue
Block a user