diff --git a/docs/spec/options/hurl/retry.option b/docs/spec/options/hurl/retry.option index e0700593d..26e918ed5 100644 --- a/docs/spec/options/hurl/retry.option +++ b/docs/spec/options/hurl/retry.option @@ -1,7 +1,6 @@ name: retry long: retry value: NUM -value_default: 0 value_parser: clap::value_parser!(i32).range(-1..) help: Maximum number of retries, 0 for no retries, -1 for unlimited retries --- diff --git a/integration/hurl/integration.py b/integration/hurl/integration.py index 97d1afbd1..989e07238 100755 --- a/integration/hurl/integration.py +++ b/integration/hurl/integration.py @@ -26,7 +26,7 @@ def main(): + get_files("tests_failed/*." + extension) + get_files("tests_failed_not_linted/*." + extension) + get_files("tests_error_parser/*." + extension) - # + get_files("ssl/*." + extension) + + get_files("ssl/*." + extension) ) for f in sorted(script_files): test_script.test(f) diff --git a/integration/hurl/tests_ok/help.out.pattern b/integration/hurl/tests_ok/help.out.pattern index ce41e5fbd..e0e2fc031 100644 --- a/integration/hurl/tests_ok/help.out.pattern +++ b/integration/hurl/tests_ok/help.out.pattern @@ -108,7 +108,7 @@ Options: --resolve Provide a custom address for a specific HOST and PORT pair --retry - Maximum number of retries, 0 for no retries, -1 for unlimited retries [default: 0] + Maximum number of retries, 0 for no retries, -1 for unlimited retries --retry-interval Interval in milliseconds before a retry [default: 1000] --ssl-no-revoke diff --git a/packages/hurl/src/cli/options/commands.rs b/packages/hurl/src/cli/options/commands.rs index 35020ccf9..665823a18 100644 --- a/packages/hurl/src/cli/options/commands.rs +++ b/packages/hurl/src/cli/options/commands.rs @@ -450,7 +450,6 @@ pub fn retry() -> clap::Arg { clap::Arg::new("retry") .long("retry") .value_name("NUM") - .default_value("0") .value_parser(clap::value_parser!(i32).range(-1..)) .allow_hyphen_values(true) .help("Maximum number of retries, 0 for no retries, -1 for unlimited retries") diff --git a/packages/hurl/src/cli/options/matches.rs b/packages/hurl/src/cli/options/matches.rs index f8867202c..34e739197 100644 --- a/packages/hurl/src/cli/options/matches.rs +++ b/packages/hurl/src/cli/options/matches.rs @@ -24,7 +24,7 @@ use std::{env, fs, io}; use clap::ArgMatches; use hurl::runner::{Input, Value}; -use hurl_core::ast::Retry; +use hurl_core::typing::Retry; use super::variables::{parse as parse_variable, parse_value}; use super::{CliOptionsError, Repeat}; @@ -378,11 +378,11 @@ pub fn resolves(arg_matches: &ArgMatches) -> Vec { get_strings(arg_matches, "resolve").unwrap_or_default() } -pub fn retry(arg_matches: &ArgMatches) -> Retry { - match get::(arg_matches, "retry").unwrap() { - -1 => Retry::Infinite, - 0 => Retry::None, - r => Retry::Finite(r as usize), +pub fn retry(arg_matches: &ArgMatches) -> Option { + match get::(arg_matches, "retry") { + Some(-1) => Some(Retry::Infinite), + Some(r) => Some(Retry::Finite(r as usize)), + None => None, } } diff --git a/packages/hurl/src/cli/options/mod.rs b/packages/hurl/src/cli/options/mod.rs index 3cf2afb8f..9dcebde8e 100644 --- a/packages/hurl/src/cli/options/mod.rs +++ b/packages/hurl/src/cli/options/mod.rs @@ -31,11 +31,12 @@ use hurl::http::RequestedHttpVersion; use hurl::runner::{Input, Output}; use hurl::util::logger::{LoggerOptions, LoggerOptionsBuilder, Verbosity}; use hurl::util::path::ContextDir; -use hurl_core::ast::{Entry, Retry}; +use hurl_core::ast::Entry; use crate::cli; use crate::runner::{RunnerOptions, RunnerOptionsBuilder, Value}; pub use error::CliOptionsError; +use hurl_core::typing::Retry; /// Represents the list of all options that can be used in Hurl command line. #[derive(Clone, Debug, PartialEq, Eq)] @@ -82,7 +83,7 @@ pub struct CliOptions { pub proxy: Option, pub repeat: Option, pub resolves: Vec, - pub retry: Retry, + pub retry: Option, pub retry_interval: Duration, pub ssl_no_revoke: bool, pub tap_file: Option, diff --git a/packages/hurl/src/runner/hurl_file.rs b/packages/hurl/src/runner/hurl_file.rs index c20c72199..6848dac05 100644 --- a/packages/hurl/src/runner/hurl_file.rs +++ b/packages/hurl/src/runner/hurl_file.rs @@ -22,10 +22,11 @@ use std::time::Instant; use chrono::Utc; use hurl_core::ast::VersionValue::VersionAnyLegacy; use hurl_core::ast::{ - Body, Bytes, Entry, MultilineString, OptionKind, Request, Response, Retry, SourceInfo, + Body, Bytes, Entry, MultilineString, OptionKind, Request, Response, SourceInfo, }; use hurl_core::error::DisplaySourceError; use hurl_core::parser; +use hurl_core::typing::Retry; use crate::http::{Call, Client}; use crate::runner::event::EventListener; @@ -290,7 +291,7 @@ fn run_request( let mut has_error = !result.errors.is_empty(); // The retry threshold can only be reached with a finite positive number of retries - let retry_max_reached = if let Retry::Finite(r) = options.retry { + let retry_max_reached = if let Some(Retry::Finite(r)) = options.retry { retry_count > r } else { false @@ -306,7 +307,7 @@ fn run_request( // We log eventual errors, only if we're not retrying the current entry... // The retry does not take into account a possible output Error - let retry = !matches!(options.retry, Retry::None) && !retry_max_reached && has_error; + let retry = options.retry.is_some() && !retry_max_reached && has_error; // When --output is overridden on a request level, we output the HTTP response only if the // call has succeeded. Output errors are not taken into account for retrying requests. @@ -484,7 +485,11 @@ fn get_non_default_options(options: &RunnerOptions) -> Vec<(&'static str, String } if options.retry != default_options.retry { - non_default_options.push(("retry", options.retry.to_string())); + let value = match options.retry { + Some(retry) => retry.to_string(), + None => "none".to_string(), + }; + non_default_options.push(("retry", value)); } if options.unix_socket != default_options.unix_socket { diff --git a/packages/hurl/src/runner/options.rs b/packages/hurl/src/runner/options.rs index 38d0e2ad2..a16f25805 100644 --- a/packages/hurl/src/runner/options.rs +++ b/packages/hurl/src/runner/options.rs @@ -20,8 +20,9 @@ use std::time::Duration; use hurl_core::ast::{ BooleanOption, Entry, EntryOption, Float, NaturalOption, Number as AstNumber, OptionKind, - Retry, RetryOption, SectionValue, VariableDefinition, VariableValue, + RetryOption, SectionValue, VariableDefinition, VariableValue, }; +use hurl_core::typing::Retry; use crate::http::{IpResolve, RequestedHttpVersion}; use crate::runner::template::{eval_expression, eval_template}; @@ -204,7 +205,7 @@ pub fn get_entry_options( } OptionKind::Retry(value) => { let value = eval_retry_option(value, variables)?; - entry_options.retry = value; + entry_options.retry = Some(value); } OptionKind::RetryInterval(value) => { let value = eval_natural_option(value, variables)?; @@ -356,9 +357,7 @@ fn eval_retry_option( Value::Number(Number::Integer(value)) => { if value == -1 { Ok(Retry::Infinite) - } else if value == 0 { - Ok(Retry::None) - } else if value > 0 { + } else if value >= 0 { Ok(Retry::Finite(value as usize)) } else { let kind = RunnerErrorKind::TemplateVariableInvalidType { diff --git a/packages/hurl/src/runner/runner_options.rs b/packages/hurl/src/runner/runner_options.rs index 2ff78cdc8..a03ade882 100644 --- a/packages/hurl/src/runner/runner_options.rs +++ b/packages/hurl/src/runner/runner_options.rs @@ -17,7 +17,8 @@ */ use std::time::Duration; -use hurl_core::ast::{Entry, Retry}; +use hurl_core::ast::Entry; +use hurl_core::typing::Retry; use crate::http::{IpResolve, RequestedHttpVersion}; use crate::runner::Output; @@ -54,7 +55,7 @@ pub struct RunnerOptionsBuilder { pre_entry: Option bool>, proxy: Option, resolves: Vec, - retry: Retry, + retry: Option, retry_interval: Duration, skip: bool, ssl_no_revoke: bool, @@ -98,7 +99,7 @@ impl Default for RunnerOptionsBuilder { pre_entry: None, proxy: None, resolves: vec![], - retry: Retry::None, + retry: None, retry_interval: Duration::from_millis(1000), skip: false, ssl_no_revoke: false, @@ -327,7 +328,7 @@ impl RunnerOptionsBuilder { /// Sets maximum number of retries. /// /// Default is 0. - pub fn retry(&mut self, retry: Retry) -> &mut Self { + pub fn retry(&mut self, retry: Option) -> &mut Self { self.retry = retry; self } @@ -461,7 +462,7 @@ pub struct RunnerOptions { pub(crate) pre_entry: Option bool>, pub(crate) proxy: Option, pub(crate) resolves: Vec, - pub(crate) retry: Retry, + pub(crate) retry: Option, pub(crate) retry_interval: Duration, pub(crate) skip: bool, pub(crate) ssl_no_revoke: bool, diff --git a/packages/hurl/tests/sample.rs b/packages/hurl/tests/sample.rs index c7d045688..e72566cf5 100644 --- a/packages/hurl/tests/sample.rs +++ b/packages/hurl/tests/sample.rs @@ -24,7 +24,6 @@ use hurl::runner; use hurl::runner::{EntryResult, HurlResult, RunnerOptionsBuilder}; use hurl::util::logger::LoggerOptionsBuilder; use hurl::util::path::ContextDir; -use hurl_core::ast::Retry; #[test] fn simple_sample() { @@ -117,7 +116,7 @@ fn simple_sample() { .post_entry(None) .pre_entry(None) .proxy(None) - .retry(Retry::None) + .retry(None) .retry_interval(Duration::from_secs(1)) .timeout(Duration::from_secs(300)) .to_entry(None) diff --git a/packages/hurl_core/src/ast/core.rs b/packages/hurl_core/src/ast/core.rs index df8d2c2b9..e18f77e1f 100644 --- a/packages/hurl_core/src/ast/core.rs +++ b/packages/hurl_core/src/ast/core.rs @@ -16,6 +16,7 @@ * */ use crate::ast::json; +use crate::typing::Retry; /// /// Hurl AST @@ -915,10 +916,3 @@ pub enum FilterValue { expr: Template, }, } - -#[derive(Clone, Debug, PartialEq, Eq, Copy)] -pub enum Retry { - None, - Finite(usize), - Infinite, -} diff --git a/packages/hurl_core/src/ast/display.rs b/packages/hurl_core/src/ast/display.rs index 993423d98..2e7ff2ab8 100644 --- a/packages/hurl_core/src/ast/display.rs +++ b/packages/hurl_core/src/ast/display.rs @@ -278,17 +278,6 @@ impl PredicateFuncValue { } } -impl fmt::Display for Retry { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let value = match self { - Retry::None => 0, - Retry::Finite(n) => *n as i32, - Retry::Infinite => -1, - }; - write!(f, "{}", value) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/packages/hurl_core/src/format/html.rs b/packages/hurl_core/src/format/html.rs index 54b4d0909..629036446 100644 --- a/packages/hurl_core/src/format/html.rs +++ b/packages/hurl_core/src/format/html.rs @@ -18,6 +18,7 @@ use std::fmt::Display; use crate::ast::*; +use crate::typing::Retry; /// Returns an HTML string of the Hurl file `hurl_file`. /// @@ -262,7 +263,6 @@ impl HtmlFormatter { match retry { Retry::Finite(n) => self.fmt_number(n), Retry::Infinite => self.fmt_number(-1), - Retry::None => self.fmt_number(0), }; } diff --git a/packages/hurl_core/src/lib.rs b/packages/hurl_core/src/lib.rs index 297375d91..b2945b69d 100644 --- a/packages/hurl_core/src/lib.rs +++ b/packages/hurl_core/src/lib.rs @@ -20,3 +20,4 @@ pub mod error; pub mod format; pub mod parser; pub mod text; +pub mod typing; diff --git a/packages/hurl_core/src/parser/option.rs b/packages/hurl_core/src/parser/option.rs index e00e4693c..7eb9065f8 100644 --- a/packages/hurl_core/src/parser/option.rs +++ b/packages/hurl_core/src/parser/option.rs @@ -23,6 +23,7 @@ use crate::parser::primitives::*; use crate::parser::reader::Reader; use crate::parser::string::*; use crate::parser::{expr, filename, filename_password, ParseResult}; +use crate::typing::Retry; /// Parse an option in an `[Options]` section. pub fn parse(reader: &mut Reader) -> ParseResult { @@ -255,9 +256,7 @@ fn retry(reader: &mut Reader) -> ParseResult { let value = nonrecover(integer, reader)?; if value == -1 { Ok(Retry::Infinite) - } else if value == 0 { - Ok(Retry::None) - } else if value > 0 { + } else if value >= 0 { Ok(Retry::Finite(value as usize)) } else { let kind = ParseErrorKind::Expecting { diff --git a/packages/hurl_core/src/typing/mod.rs b/packages/hurl_core/src/typing/mod.rs new file mode 100644 index 000000000..1dc04ee20 --- /dev/null +++ b/packages/hurl_core/src/typing/mod.rs @@ -0,0 +1,36 @@ +/* + * Hurl (https://hurl.dev) + * Copyright (C) 2024 Orange + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +use core::fmt; + +/// Represents a retry operation, either finite or infinite. +#[derive(Clone, Debug, PartialEq, Eq, Copy)] +pub enum Retry { + Finite(usize), + Infinite, +} + +impl fmt::Display for Retry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let value = match self { + Retry::Finite(n) => *n as i32, + Retry::Infinite => -1, + }; + write!(f, "{}", value) + } +} diff --git a/packages/hurlfmt/src/format/json.rs b/packages/hurlfmt/src/format/json.rs index 27fed7cd9..f80fb5079 100644 --- a/packages/hurlfmt/src/format/json.rs +++ b/packages/hurlfmt/src/format/json.rs @@ -18,6 +18,7 @@ use base64::engine::general_purpose; use base64::Engine; use hurl_core::ast::*; +use hurl_core::typing::Retry; use super::serialize_json::*; @@ -363,7 +364,6 @@ impl ToJson for RetryOption { impl ToJson for Retry { fn to_json(&self) -> JValue { match self { - Retry::None => JValue::Number("0".to_string()), Retry::Finite(value) => JValue::Number(value.to_string()), Retry::Infinite => JValue::Number("-1".to_string()), } diff --git a/packages/hurlfmt/src/format/token.rs b/packages/hurlfmt/src/format/token.rs index 35baaca73..b815a4542 100644 --- a/packages/hurlfmt/src/format/token.rs +++ b/packages/hurlfmt/src/format/token.rs @@ -16,6 +16,7 @@ * */ use hurl_core::ast::*; +use hurl_core::typing::Retry; #[derive(Clone, Debug, PartialEq, Eq)] pub enum Token { @@ -945,7 +946,6 @@ impl Tokenizable for RetryOption { impl Tokenizable for Retry { fn tokenize(&self) -> Vec { match self { - Retry::None => vec![Token::Number("0".to_string())], Retry::Finite(n) => vec![Token::Number(n.to_string())], Retry::Infinite => vec![Token::Number("-1".to_string())], }