Some refacto on RunContext to better manage color/no_color.
This commit is contained in:
parent
1e96255d2c
commit
39986a9be2
|
|
@ -4,7 +4,8 @@ set -Eeuo pipefail
|
|||
valgrind --version
|
||||
cargo-valgrind --help
|
||||
|
||||
cat <<END | cargo valgrind run -p hurl -- --test
|
||||
GET https://unpkg.com/vue@3.4.27/dist/vue.global.prod.js
|
||||
HTTP 200
|
||||
END
|
||||
# Disable valgrind for the moment, see <https://github.com/jfrimmel/cargo-valgrind/issues/131>
|
||||
#cat <<END | cargo valgrind run -p hurl -- --test
|
||||
#GET https://unpkg.com/vue@3.4.27/dist/vue.global.prod.js
|
||||
#HTTP 200
|
||||
#END
|
||||
|
|
|
|||
|
|
@ -15,15 +15,18 @@
|
|||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Represents the context in whin is executed Hurl: the env variables, whether standard
|
||||
/// Represents the context in which is executed Hurl: the env variables, whether standard
|
||||
/// input is a terminal or not (when pipe or redirected to a file for instance), whether standard
|
||||
/// error is a terminal or not, whether Hurl is executed in a CI/CD environment, whether users has
|
||||
/// disallowed ANSI code color etc...
|
||||
pub struct RunContext {
|
||||
/// Are we allowed to ise ANSI escaoe codes or not.
|
||||
with_color: bool,
|
||||
env_vars: Vec<(String, String)>,
|
||||
/// Whether this running in a Continuous Integration environment.
|
||||
/// All the environment variables.
|
||||
env_vars: HashMap<String, String>,
|
||||
/// Whether we're running in a Continuous Integration environment or not.
|
||||
ci: bool,
|
||||
/// Is standard input a terminal or not?
|
||||
stdin_term: bool,
|
||||
|
|
@ -41,16 +44,26 @@ impl RunContext {
|
|||
/// Creates a new context. The environment is captured and will be seen as non-mutable for the
|
||||
/// execution with this context.
|
||||
pub fn new(
|
||||
with_color: bool,
|
||||
env_vars: Vec<(String, String)>,
|
||||
env_vars: HashMap<String, String>,
|
||||
stdin_term: bool,
|
||||
stdout_term: bool,
|
||||
stderr_term: bool,
|
||||
) -> Self {
|
||||
// Code borrowed from <https://github.com/rust-lang/cargo/blob/master/crates/cargo-util/src/lib.rs>
|
||||
let ci = env_vars
|
||||
.iter()
|
||||
.any(|(name, _)| name == "CI" || name == "TF_BUILD");
|
||||
let ci = env_vars.contains_key("CI") || env_vars.contains_key("TF_BUILD");
|
||||
|
||||
// According to the NO_COLOR spec, any presence of the variable should disable color, but to
|
||||
// maintain backward compatibility with code < 7.1.0, we check that the NO_COLOR env is at
|
||||
// least not empty.
|
||||
let with_color = if let Some(v) = env_vars.get("NO_COLOR") {
|
||||
if !v.is_empty() {
|
||||
false
|
||||
} else {
|
||||
stdout_term
|
||||
}
|
||||
} else {
|
||||
stdout_term
|
||||
};
|
||||
|
||||
RunContext {
|
||||
with_color,
|
||||
|
|
@ -67,11 +80,11 @@ impl RunContext {
|
|||
self.with_color
|
||||
}
|
||||
|
||||
/// Returns the list of Hurl variables injected by environment variables.
|
||||
/// Returns the map of Hurl variables injected by environment variables.
|
||||
///
|
||||
/// Environment variables are prefixed with `HURL_VARIABLE_` and returned values have their name
|
||||
/// stripped of this prefix.
|
||||
pub fn var_env_vars(&self) -> Vec<(&str, &str)> {
|
||||
pub fn var_env_vars(&self) -> HashMap<&str, &str> {
|
||||
self.env_vars
|
||||
.iter()
|
||||
.filter_map(|(name, value)| {
|
||||
|
|
@ -82,11 +95,11 @@ impl RunContext {
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// Returns the list of legacy Hurl variables injected by environment variables.
|
||||
/// Returns the map of legacy Hurl variables injected by environment variables.
|
||||
///
|
||||
/// Environment variables are prefixed with `HURL_` and returned values have their name
|
||||
/// stripped of this prefix.
|
||||
pub fn legacy_var_env_vars(&self) -> Vec<(&str, &str)> {
|
||||
pub fn legacy_var_env_vars(&self) -> HashMap<&str, &str> {
|
||||
self.env_vars
|
||||
.iter()
|
||||
.filter_map(|(name, value)| {
|
||||
|
|
@ -101,11 +114,11 @@ impl RunContext {
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// Returns the list of Hurl secrets injected by environment variables.
|
||||
/// Returns the map of Hurl secrets injected by environment variables.
|
||||
///
|
||||
/// Environment variables are prefixed with `HURL_SECRET_` and returned values have their name
|
||||
/// stripped of this prefix.
|
||||
pub fn secret_env_vars(&self) -> Vec<(&str, &str)> {
|
||||
pub fn secret_env_vars(&self) -> HashMap<&str, &str> {
|
||||
self.env_vars
|
||||
.iter()
|
||||
.filter_map(|(name, value)| {
|
||||
|
|
@ -141,35 +154,58 @@ impl RunContext {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::cli::options::context::RunContext;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn empty_variables_secrets_from_env() {
|
||||
let with_color = false;
|
||||
fn context_is_colored() {
|
||||
let stdin_term = true;
|
||||
let stdout_term = true;
|
||||
let stderr_term = true;
|
||||
|
||||
let env_vars = vec![
|
||||
let env_vars = HashMap::from([("A".to_string(), "B".to_string())]);
|
||||
|
||||
let ctx = RunContext::new(env_vars, stdin_term, stdout_term, stderr_term);
|
||||
assert!(ctx.is_with_color());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn context_respect_no_color() {
|
||||
let stdin_term = true;
|
||||
let stdout_term = true;
|
||||
let stderr_term = true;
|
||||
|
||||
let env_vars = HashMap::from([("NO_COLOR".to_string(), "1".to_string())]);
|
||||
|
||||
let ctx = RunContext::new(env_vars, stdin_term, stdout_term, stderr_term);
|
||||
assert!(!ctx.is_with_color());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_variables_secrets_from_env() {
|
||||
let stdin_term = true;
|
||||
let stdout_term = true;
|
||||
let stderr_term = true;
|
||||
|
||||
let env_vars = HashMap::from([
|
||||
("FOO".to_string(), "xxx".to_string()),
|
||||
("BAR".to_string(), "yyy".to_string()),
|
||||
("BAZ".to_string(), "yyy".to_string()),
|
||||
];
|
||||
]);
|
||||
|
||||
let ctx = RunContext::new(with_color, env_vars, stdin_term, stdout_term, stderr_term);
|
||||
let ctx = RunContext::new(env_vars, stdin_term, stdout_term, stderr_term);
|
||||
|
||||
assert_eq!(ctx.var_env_vars(), vec![]);
|
||||
assert_eq!(ctx.legacy_var_env_vars(), vec![]);
|
||||
assert_eq!(ctx.secret_env_vars(), vec![]);
|
||||
assert!(ctx.var_env_vars().is_empty());
|
||||
assert!(ctx.legacy_var_env_vars().is_empty());
|
||||
assert!(ctx.secret_env_vars().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variables_from_env() {
|
||||
let with_color = false;
|
||||
let stdin_term = true;
|
||||
let stdout_term = true;
|
||||
let stderr_term = true;
|
||||
|
||||
let env_vars = vec![
|
||||
let env_vars = HashMap::from([
|
||||
("FOO".to_string(), "xxx".to_string()),
|
||||
("BAR".to_string(), "yyy".to_string()),
|
||||
("BAZ".to_string(), "yyy".to_string()),
|
||||
|
|
@ -179,26 +215,26 @@ mod tests {
|
|||
("HURL_VARIABLE".to_string(), "1234".to_string()),
|
||||
("HURL_VARIABLE_".to_string(), "abcd".to_string()),
|
||||
("HURL_VARIABLE_FOO".to_string(), "def".to_string()),
|
||||
];
|
||||
]);
|
||||
|
||||
let ctx = RunContext::new(with_color, env_vars, stdin_term, stdout_term, stderr_term);
|
||||
let ctx = RunContext::new(env_vars, stdin_term, stdout_term, stderr_term);
|
||||
|
||||
assert_eq!(
|
||||
ctx.var_env_vars(),
|
||||
vec![("foo", "true"), ("id", "1234"), ("FOO", "def"),]
|
||||
);
|
||||
assert_eq!(ctx.legacy_var_env_vars(), vec![("VARIABLE", "1234"),]);
|
||||
assert_eq!(ctx.secret_env_vars(), vec![]);
|
||||
assert_eq!(ctx.var_env_vars().len(), 3);
|
||||
assert_eq!(ctx.var_env_vars()["foo"], "true");
|
||||
assert_eq!(ctx.var_env_vars()["id"], "1234");
|
||||
assert_eq!(ctx.var_env_vars()["FOO"], "def");
|
||||
assert_eq!(ctx.legacy_var_env_vars().len(), 1);
|
||||
assert_eq!(ctx.legacy_var_env_vars()["VARIABLE"], "1234");
|
||||
assert!(ctx.secret_env_vars().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_variables_from_env() {
|
||||
let with_color = false;
|
||||
let stdin_term = true;
|
||||
let stdout_term = true;
|
||||
let stderr_term = true;
|
||||
|
||||
let env_vars = vec![
|
||||
let env_vars = HashMap::from([
|
||||
("FOO".to_string(), "xxx".to_string()),
|
||||
("BAR".to_string(), "yyy".to_string()),
|
||||
("BAZ".to_string(), "yyy".to_string()),
|
||||
|
|
@ -209,41 +245,41 @@ mod tests {
|
|||
("HURL_".to_string(), "1234".to_string()),
|
||||
("HURL_".to_string(), "abcd".to_string()),
|
||||
("HURL_FOO".to_string(), "def".to_string()),
|
||||
];
|
||||
]);
|
||||
|
||||
let ctx = RunContext::new(with_color, env_vars, stdin_term, stdout_term, stderr_term);
|
||||
let ctx = RunContext::new(env_vars, stdin_term, stdout_term, stderr_term);
|
||||
|
||||
assert_eq!(ctx.var_env_vars(), vec![("bar", "def"),]);
|
||||
assert_eq!(
|
||||
ctx.legacy_var_env_vars(),
|
||||
vec![("foo", "true"), ("id", "1234"), ("FOO", "def"),]
|
||||
);
|
||||
assert_eq!(ctx.secret_env_vars(), vec![]);
|
||||
assert_eq!(ctx.var_env_vars().len(), 1);
|
||||
assert_eq!(ctx.var_env_vars()["bar"], "def");
|
||||
assert_eq!(ctx.legacy_var_env_vars().len(), 3);
|
||||
assert_eq!(ctx.legacy_var_env_vars()["foo"], "true");
|
||||
assert_eq!(ctx.legacy_var_env_vars()["id"], "1234");
|
||||
assert_eq!(ctx.legacy_var_env_vars()["FOO"], "def");
|
||||
assert!(ctx.secret_env_vars().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_secrets_from_env() {
|
||||
let with_color = false;
|
||||
let stdin_term = true;
|
||||
let stdout_term = true;
|
||||
let stderr_term = true;
|
||||
|
||||
let env_vars = vec![
|
||||
let env_vars = HashMap::from([
|
||||
("FOO".to_string(), "xxx".to_string()),
|
||||
("HURL_SECRET".to_string(), "48".to_string()),
|
||||
("HURL_SECRET_".to_string(), "48".to_string()),
|
||||
("HURL_SECRET_abcd".to_string(), "1234".to_string()),
|
||||
("HURL_SECRET_ABCD".to_string(), "5678".to_string()),
|
||||
("BAR".to_string(), "bar".to_string()),
|
||||
];
|
||||
]);
|
||||
|
||||
let ctx = RunContext::new(with_color, env_vars, stdin_term, stdout_term, stderr_term);
|
||||
let ctx = RunContext::new(env_vars, stdin_term, stdout_term, stderr_term);
|
||||
|
||||
assert_eq!(ctx.var_env_vars(), vec![]);
|
||||
assert_eq!(ctx.legacy_var_env_vars(), vec![("SECRET", "48"),]);
|
||||
assert_eq!(
|
||||
ctx.secret_env_vars(),
|
||||
vec![("abcd", "1234"), ("ABCD", "5678"),]
|
||||
);
|
||||
assert!(ctx.var_env_vars().is_empty());
|
||||
assert_eq!(ctx.legacy_var_env_vars().len(), 1);
|
||||
assert_eq!(ctx.legacy_var_env_vars()["SECRET"], "48");
|
||||
assert_eq!(ctx.secret_env_vars().len(), 2);
|
||||
assert_eq!(ctx.secret_env_vars()["abcd"], "1234");
|
||||
assert_eq!(ctx.secret_env_vars()["ABCD"], "5678");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,10 +24,9 @@ mod variables;
|
|||
mod variables_file;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::IsTerminal;
|
||||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use std::{env, io};
|
||||
|
||||
use clap::builder::styling::{AnsiColor, Effects};
|
||||
use clap::builder::Styles;
|
||||
|
|
@ -44,7 +43,7 @@ use hurl_core::input::{Input, InputKind};
|
|||
use hurl_core::types::{BytesPerSec, Count};
|
||||
|
||||
use crate::cli;
|
||||
use crate::cli::options::context::RunContext;
|
||||
pub use crate::cli::options::context::RunContext;
|
||||
use crate::runner::{RunnerOptions, RunnerOptionsBuilder, Value};
|
||||
|
||||
/// Represents the list of all options that can be used in Hurl command line.
|
||||
|
|
@ -179,11 +178,9 @@ fn get_version() -> String {
|
|||
)
|
||||
}
|
||||
|
||||
/// Parse the Hurl CLI options and returns a [`CliOptions`] result.
|
||||
///
|
||||
/// When a [`CliOptionsError::DisplayHelp`] variant is returned, `with_color` is used
|
||||
/// to print an ANSI color help or not.
|
||||
pub fn parse(with_color: bool) -> Result<CliOptions, CliOptionsError> {
|
||||
/// Parse the Hurl CLI options and returns a [`CliOptions`] result, given a run `context`
|
||||
/// (environment variables).
|
||||
pub fn parse(context: &RunContext) -> Result<CliOptions, CliOptionsError> {
|
||||
let styles = Styles::styled()
|
||||
.header(AnsiColor::Green.on_default() | Effects::BOLD)
|
||||
.usage(AnsiColor::Green.on_default() | Effects::BOLD)
|
||||
|
|
@ -277,20 +274,13 @@ pub fn parse(with_color: bool) -> Result<CliOptions, CliOptionsError> {
|
|||
let arg_matches = command.try_get_matches_from_mut(env::args_os());
|
||||
let arg_matches = match arg_matches {
|
||||
Ok(args) => args,
|
||||
Err(error) => return Err(CliOptionsError::from_clap(error, with_color)),
|
||||
Err(error) => return Err(CliOptionsError::from_clap(error, context.is_with_color())),
|
||||
};
|
||||
|
||||
// Construct the run context environment
|
||||
let env_vars = env::vars().collect();
|
||||
let stdin_term = io::stdin().is_terminal();
|
||||
let stdout_term = io::stdout().is_terminal();
|
||||
let stderr_term = io::stderr().is_terminal();
|
||||
let ctx = RunContext::new(with_color, env_vars, stdin_term, stdout_term, stderr_term);
|
||||
|
||||
// If we've no file input (either from the standard input or from the command line arguments),
|
||||
// we just print help and exit.
|
||||
if !matches::has_input_files(&arg_matches, &ctx) {
|
||||
let help = if with_color {
|
||||
if !matches::has_input_files(&arg_matches, context) {
|
||||
let help = if context.is_with_color() {
|
||||
command.render_help().ansi().to_string()
|
||||
} else {
|
||||
command.render_help().to_string()
|
||||
|
|
@ -298,7 +288,7 @@ pub fn parse(with_color: bool) -> Result<CliOptions, CliOptionsError> {
|
|||
return Err(CliOptionsError::NoInput(help));
|
||||
}
|
||||
|
||||
let opts = parse_matches(&arg_matches, &ctx)?;
|
||||
let opts = parse_matches(&arg_matches, context)?;
|
||||
if opts.input_files.is_empty() {
|
||||
return Err(CliOptionsError::Error(
|
||||
"No input files provided".to_string(),
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ use hurl::util::redacted::Redact;
|
|||
use hurl_core::input::Input;
|
||||
use hurl_core::text;
|
||||
|
||||
use crate::cli::options::{CliOptions, CliOptionsError};
|
||||
use crate::cli::options::{CliOptions, CliOptionsError, RunContext};
|
||||
use crate::cli::{BaseLogger, CliError};
|
||||
|
||||
const EXIT_OK: i32 = 0;
|
||||
|
|
@ -56,9 +56,17 @@ struct HurlRun {
|
|||
fn main() {
|
||||
text::init_crate_colored();
|
||||
|
||||
let with_color = is_color_allowed_from_env();
|
||||
// Construct the run context environment, this should be the sole place where we read
|
||||
// environment variables. The run context will be injected in functions that need to access
|
||||
// environment variables.
|
||||
// TODO: add `env::current_dir` to the run context
|
||||
let env_vars = env::vars().collect();
|
||||
let stdin_term = io::stdin().is_terminal();
|
||||
let stdout_term = io::stdout().is_terminal();
|
||||
let stderr_term = io::stderr().is_terminal();
|
||||
let ctx = RunContext::new(env_vars, stdin_term, stdout_term, stderr_term);
|
||||
|
||||
let opts = match cli::options::parse(with_color) {
|
||||
let opts = match cli::options::parse(&ctx) {
|
||||
Ok(v) => v,
|
||||
Err(e) => match e {
|
||||
CliOptionsError::DisplayHelp(e) | CliOptionsError::DisplayVersion(e) => {
|
||||
|
|
@ -155,18 +163,6 @@ fn has_report(opts: &CliOptions) -> bool {
|
|||
|| opts.cookie_output_file.is_some()
|
||||
}
|
||||
|
||||
/// Returns `true` if we can use ANSI color, solely base on the environment.
|
||||
///
|
||||
/// This function doesn't take CLI options into account.
|
||||
fn is_color_allowed_from_env() -> bool {
|
||||
if let Ok(v) = env::var("NO_COLOR") {
|
||||
if !v.is_empty() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
io::stdout().is_terminal()
|
||||
}
|
||||
|
||||
/// Writes `runs` results on file, in HTML, TAP, JUnit or Cookie file format.
|
||||
fn export_results(
|
||||
runs: &[HurlRun],
|
||||
|
|
|
|||
Loading…
Reference in New Issue