Unify Retry and Repeat types with Count.

This commit is contained in:
Jean-Christophe Amiel 2024-07-06 21:34:10 +02:00
parent 25d6716145
commit 1cdc491867
No known key found for this signature in database
GPG Key ID: 07FF11CFD55356CC
16 changed files with 118 additions and 272 deletions

View File

@ -31,7 +31,7 @@ error: Invalid variable type
| GET http://localhost:8000/unused
| ...
29 | repeat: {{count}}
| ^^^^^ expecting integer, actual value is integer <-2>
| ^^^^^ expecting integer >= -1, actual value is integer <-2>
|
error: Invalid variable type
@ -58,6 +58,6 @@ error: Invalid variable type
| GET http://localhost:8000/unused
| ...
50 | retry: {{count}}
| ^^^^^ expecting integer, actual value is foo
| ^^^^^ expecting integer, actual value is string <foo>
|

View File

@ -24,7 +24,7 @@ use std::{env, fs, io};
use clap::ArgMatches;
use hurl::runner::{Input, Value};
use hurl_core::typing::{Repeat, Retry};
use hurl_core::typing::Count;
use super::variables::{parse as parse_variable, parse_value};
use super::CliOptionsError;
@ -366,10 +366,10 @@ pub fn proxy(arg_matches: &ArgMatches) -> Option<String> {
get::<String>(arg_matches, "proxy")
}
pub fn repeat(arg_matches: &ArgMatches) -> Option<Repeat> {
pub fn repeat(arg_matches: &ArgMatches) -> Option<Count> {
match get::<i32>(arg_matches, "repeat") {
Some(-1) => Some(Repeat::Forever),
Some(n) => Some(Repeat::Count(n as usize)),
Some(-1) => Some(Count::Infinite),
Some(n) => Some(Count::Finite(n as usize)),
None => None,
}
}
@ -378,10 +378,10 @@ pub fn resolves(arg_matches: &ArgMatches) -> Vec<String> {
get_strings(arg_matches, "resolve").unwrap_or_default()
}
pub fn retry(arg_matches: &ArgMatches) -> Option<Retry> {
pub fn retry(arg_matches: &ArgMatches) -> Option<Count> {
match get::<i32>(arg_matches, "retry") {
Some(-1) => Some(Retry::Infinite),
Some(r) => Some(Retry::Finite(r as usize)),
Some(-1) => Some(Count::Infinite),
Some(r) => Some(Count::Finite(r as usize)),
None => None,
}
}

View File

@ -36,7 +36,7 @@ use hurl_core::ast::Entry;
use crate::cli;
use crate::runner::{RunnerOptions, RunnerOptionsBuilder, Value};
pub use error::CliOptionsError;
use hurl_core::typing::{Repeat, Retry};
use hurl_core::typing::Count;
/// Represents the list of all options that can be used in Hurl command line.
#[derive(Clone, Debug, PartialEq, Eq)]
@ -81,9 +81,9 @@ pub struct CliOptions {
pub path_as_is: bool,
pub progress_bar: bool,
pub proxy: Option<String>,
pub repeat: Option<Repeat>,
pub repeat: Option<Count>,
pub resolves: Vec<String>,
pub retry: Option<Retry>,
pub retry: Option<Count>,
pub retry_interval: Duration,
pub ssl_no_revoke: bool,
pub tap_file: Option<PathBuf>,

View File

@ -15,7 +15,7 @@
* limitations under the License.
*
*/
use hurl_core::typing::Repeat;
use hurl_core::typing::Count;
use std::collections::HashMap;
use crate::runner::{HurlResult, Input, RunnerOptions, Value};
@ -87,14 +87,14 @@ pub struct JobQueue<'job> {
/// Current index of the job, referencing the input job list.
index: usize,
/// Repeat mode of this queue (finite or infinite).
repeat: Repeat,
repeat: Count,
/// Current index of the repeat.
repeat_index: usize,
}
impl<'job> JobQueue<'job> {
/// Create a new queue, with a list of `jobs` and a `repeat` mode.
pub fn new(jobs: &'job [Job], repeat: Repeat) -> Self {
pub fn new(jobs: &'job [Job], repeat: Count) -> Self {
JobQueue {
jobs,
index: 0,
@ -108,8 +108,8 @@ impl<'job> JobQueue<'job> {
/// If queue is created in loop forever mode ([`Repeat::Forever`]), returns `None`.
pub fn jobs_count(&self) -> Option<usize> {
match self.repeat {
Repeat::Count(n) => Some(self.jobs.len() * n),
Repeat::Forever => None,
Count::Finite(n) => Some(self.jobs.len() * n),
Count::Infinite => None,
}
}
@ -130,7 +130,7 @@ impl Iterator for JobQueue<'_> {
if self.index >= self.jobs.len() {
self.repeat_index = self.repeat_index.checked_add(1).unwrap_or(0);
match self.repeat {
Repeat::Count(n) => {
Count::Finite(n) => {
if self.repeat_index >= n {
None
} else {
@ -138,7 +138,7 @@ impl Iterator for JobQueue<'_> {
Some(self.job_at(0))
}
}
Repeat::Forever => {
Count::Infinite => {
self.index = 1;
Some(self.job_at(0))
}
@ -155,7 +155,7 @@ mod tests {
use crate::parallel::job::{Job, JobQueue};
use crate::runner::{Input, RunnerOptionsBuilder};
use crate::util::logger::LoggerOptionsBuilder;
use hurl_core::typing::Repeat;
use hurl_core::typing::Count;
use std::collections::HashMap;
fn new_job(file: &str, index: usize) -> Job {
@ -179,7 +179,7 @@ mod tests {
new_job("c.hurl", 2),
];
let mut queue = JobQueue::new(&jobs, Repeat::Count(2));
let mut queue = JobQueue::new(&jobs, Count::Finite(2));
assert_eq!(queue.next(), Some(new_job("a.hurl", 0)));
assert_eq!(queue.next(), Some(new_job("b.hurl", 1)));
@ -196,7 +196,7 @@ mod tests {
fn input_queue_is_infinite() {
let jobs = [new_job("foo.hurl", 0)];
let mut queue = JobQueue::new(&jobs, Repeat::Forever);
let mut queue = JobQueue::new(&jobs, Count::Infinite);
assert_eq!(queue.next(), Some(new_job("foo.hurl", 0)));
assert_eq!(queue.next(), Some(new_job("foo.hurl", 1)));
assert_eq!(queue.next(), Some(new_job("foo.hurl", 2)));

View File

@ -16,7 +16,7 @@
*
*/
use hurl_core::error::{DisplaySourceError, OutputFormat};
use hurl_core::typing::Repeat;
use hurl_core::typing::Count;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::{mpsc, Arc, Mutex};
@ -53,7 +53,7 @@ pub struct ParallelRunner {
/// Output type for each completed job on standard output.
output_type: OutputType,
/// Repeat mode for the runner: infinite or finite.
repeat: Repeat,
repeat: Count,
}
/// Represents a worker's state.
@ -106,7 +106,7 @@ impl ParallelRunner {
pub fn new(
workers_count: usize,
output_type: OutputType,
repeat: Repeat,
repeat: Count,
test: bool,
progress_bar: bool,
color: bool,

View File

@ -28,7 +28,7 @@ use hurl::runner::{HurlResult, Input, Output};
use hurl::util::term::{Stdout, WriteMode};
use hurl::{output, parallel, runner};
use hurl_core::error::{DisplaySourceError, OutputFormat};
use hurl_core::typing::Repeat;
use hurl_core::typing::Count;
/// Runs Hurl `files` sequentially, given a current directory and command-line options (see
/// [`crate::cli::options::CliOptions`]). This function returns a list of [`HurlRun`] results or
@ -40,7 +40,7 @@ pub fn run_seq(
) -> Result<Vec<HurlRun>, CliError> {
let mut runs = vec![];
let repeat = options.repeat.unwrap_or_default();
let repeat = options.repeat.unwrap_or(Count::Finite(1));
let queue = InputQueue::new(files, repeat);
// When dumped HTTP responses, we truncate existing output file on first save, then append
@ -165,8 +165,8 @@ pub fn run_par(
// We're going to use the right numbers of workers. We don't need to use more workers than there
// are input files (repeat option act as if we're dealing with a multiplied number of files)
let workers_count = match options.repeat {
Some(Repeat::Count(n)) => min(files.len() * n, workers_count),
Some(Repeat::Forever) => workers_count,
Some(Count::Finite(n)) => min(files.len() * n, workers_count),
Some(Count::Infinite) => workers_count,
None => min(files.len(), workers_count),
};
let variables = &options.variables;
@ -188,7 +188,7 @@ pub fn run_par(
let mut runner = ParallelRunner::new(
workers_count,
output_type,
options.repeat.unwrap_or_default(),
options.repeat.unwrap_or(Count::Finite(1)),
options.test,
options.progress_bar,
options.color,
@ -233,14 +233,14 @@ pub struct InputQueue<'input> {
/// Current index of the input, referencing the input list.
index: usize,
/// Repeat mode of this queue (finite or infinite).
repeat: Repeat,
repeat: Count,
/// Current index of the repeat.
repeat_index: usize,
}
impl<'input> InputQueue<'input> {
/// Create a new queue, with a list of `inputs` and a `repeat` mode.
pub fn new(inputs: &'input [Input], repeat: Repeat) -> Self {
pub fn new(inputs: &'input [Input], repeat: Count) -> Self {
InputQueue {
inputs,
index: 0,
@ -262,7 +262,7 @@ impl Iterator for InputQueue<'_> {
if self.index >= self.inputs.len() {
self.repeat_index = self.repeat_index.checked_add(1).unwrap_or(0);
match self.repeat {
Repeat::Count(n) => {
Count::Finite(n) => {
if self.repeat_index >= n {
None
} else {
@ -270,7 +270,7 @@ impl Iterator for InputQueue<'_> {
Some(self.input_at(0))
}
}
Repeat::Forever => {
Count::Infinite => {
self.index = 1;
Some(self.input_at(0))
}
@ -286,13 +286,13 @@ impl Iterator for InputQueue<'_> {
mod tests {
use crate::run::InputQueue;
use hurl::runner::Input;
use hurl_core::typing::Repeat;
use hurl_core::typing::Count;
#[test]
fn input_queue_is_finite() {
let files = [Input::new("a"), Input::new("b"), Input::new("c")];
let mut queue = InputQueue::new(&files, Repeat::Count(4));
let mut queue = InputQueue::new(&files, Count::Finite(4));
assert_eq!(queue.next(), Some(Input::new("a")));
assert_eq!(queue.next(), Some(Input::new("b")));
assert_eq!(queue.next(), Some(Input::new("c")));
@ -312,7 +312,7 @@ mod tests {
fn input_queue_is_infinite() {
let files = [Input::new("a")];
let mut queue = InputQueue::new(&files, Repeat::Forever);
let mut queue = InputQueue::new(&files, Count::Infinite);
assert_eq!(queue.next(), Some(Input::new("a")));
assert_eq!(queue.next(), Some(Input::new("a")));
assert_eq!(queue.next(), Some(Input::new("a")));

View File

@ -26,7 +26,7 @@ use hurl_core::ast::{
};
use hurl_core::error::DisplaySourceError;
use hurl_core::parser;
use hurl_core::typing::{Repeat, Retry};
use hurl_core::typing::Count;
use crate::http::{Call, Client};
use crate::runner::event::EventListener;
@ -217,7 +217,7 @@ pub fn run_entries(
}
// Repeat 0 is equivalent to skip.
if options.repeat == Some(Repeat::Count(0)) {
if options.repeat == Some(Count::Finite(0)) {
logger.debug("");
logger.debug_important(&format!("Entry {entry_index} is skipped (repeat 0 times)"));
entry_index += 1;
@ -268,7 +268,7 @@ pub fn run_entries(
repeat_count = 0;
entry_index += 1;
}
Some(Repeat::Count(n)) => {
Some(Count::Finite(n)) => {
if repeat_count >= n {
repeat_count = 0;
entry_index += 1;
@ -278,7 +278,7 @@ pub fn run_entries(
));
}
}
Some(Repeat::Forever) => {
Some(Count::Infinite) => {
logger.debug_important(&format!("Repeat entry {entry_index} (x{repeat_count})"));
}
}
@ -319,7 +319,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 Some(Retry::Finite(r)) = options.retry {
let retry_max_reached = if let Some(Count::Finite(r)) = options.retry {
retry_count > r
} else {
false

View File

@ -19,10 +19,10 @@ use std::collections::HashMap;
use std::time::Duration;
use hurl_core::ast::{
BooleanOption, Entry, EntryOption, Float, NaturalOption, Number as AstNumber, OptionKind,
RepeatOption, RetryOption, SectionValue, VariableDefinition, VariableValue,
BooleanOption, CountOption, Entry, EntryOption, Float, NaturalOption, Number as AstNumber,
OptionKind, SectionValue, VariableDefinition, VariableValue,
};
use hurl_core::typing::{Repeat, Retry};
use hurl_core::typing::Count;
use crate::http::{IpResolve, RequestedHttpVersion};
use crate::runner::template::{eval_expression, eval_template};
@ -200,7 +200,7 @@ pub fn get_entry_options(
entry_options.proxy = Some(value);
}
OptionKind::Repeat(value) => {
let value = eval_repeat_option(value, variables)?;
let value = eval_count_option(value, variables)?;
entry_options.repeat = Some(value);
}
OptionKind::Resolve(value) => {
@ -208,7 +208,7 @@ pub fn get_entry_options(
entry_options.resolves.push(value);
}
OptionKind::Retry(value) => {
let value = eval_retry_option(value, variables)?;
let value = eval_count_option(value, variables)?;
entry_options.retry = Some(value);
}
OptionKind::RetryInterval(value) => {
@ -320,7 +320,6 @@ fn eval_boolean_option(
}
}
/// Evals a natural option value (>=0), given a set of `variables`.
fn eval_natural_option(
natural_value: &NaturalOption,
variables: &HashMap<String, Value>,
@ -352,53 +351,18 @@ fn eval_natural_option(
}
}
/// Render an AST repeat option with a `variables` set.
fn eval_repeat_option(
repeat_option_value: &RepeatOption,
fn eval_count_option(
count_value: &CountOption,
variables: &HashMap<String, Value>,
) -> Result<Repeat, RunnerError> {
match repeat_option_value {
RepeatOption::Literal(repeat) => Ok(*repeat),
RepeatOption::Expression(expr) => match eval_expression(expr, variables)? {
) -> Result<Count, RunnerError> {
match count_value {
CountOption::Literal(repeat) => Ok(*repeat),
CountOption::Expression(expr) => match eval_expression(expr, variables)? {
Value::Number(Number::Integer(value)) => {
if value == -1 {
Ok(Repeat::Forever)
Ok(Count::Infinite)
} else if value >= 0 {
Ok(Repeat::Count(value as usize))
} else {
let kind = RunnerErrorKind::TemplateVariableInvalidType {
name: expr.variable.name.clone(),
value: format!("integer <{value}>"),
expecting: "integer".to_string(),
};
Err(RunnerError::new(expr.variable.source_info, kind, false))
}
}
v => {
let kind = RunnerErrorKind::TemplateVariableInvalidType {
name: expr.variable.name.clone(),
value: v.format(),
expecting: "integer".to_string(),
};
Err(RunnerError::new(expr.variable.source_info, kind, false))
}
},
}
}
/// Render an AST retry option with a `variables` set.
fn eval_retry_option(
retry_option_value: &RetryOption,
variables: &HashMap<String, Value>,
) -> Result<Retry, RunnerError> {
match retry_option_value {
RetryOption::Literal(retry) => Ok(*retry),
RetryOption::Expression(expr) => match eval_expression(expr, variables)? {
Value::Number(Number::Integer(value)) => {
if value == -1 {
Ok(Retry::Infinite)
} else if value >= 0 {
Ok(Retry::Finite(value as usize))
Ok(Count::Finite(value as usize))
} else {
let kind = RunnerErrorKind::TemplateVariableInvalidType {
name: expr.variable.name.clone(),
@ -411,7 +375,7 @@ fn eval_retry_option(
v => {
let kind = RunnerErrorKind::TemplateVariableInvalidType {
name: expr.variable.name.clone(),
value: v.to_string(),
value: v.format(),
expecting: "integer".to_string(),
};
Err(RunnerError::new(expr.variable.source_info, kind, false))

View File

@ -18,7 +18,7 @@
use std::time::Duration;
use hurl_core::ast::Entry;
use hurl_core::typing::{Repeat, Retry};
use hurl_core::typing::Count;
use crate::http::{IpResolve, RequestedHttpVersion};
use crate::runner::Output;
@ -54,9 +54,9 @@ pub struct RunnerOptionsBuilder {
post_entry: Option<fn() -> bool>,
pre_entry: Option<fn(Entry) -> bool>,
proxy: Option<String>,
repeat: Option<Repeat>,
repeat: Option<Count>,
resolves: Vec<String>,
retry: Option<Retry>,
retry: Option<Count>,
retry_interval: Duration,
skip: bool,
ssl_no_revoke: bool,
@ -322,7 +322,7 @@ impl RunnerOptionsBuilder {
}
/// Set the number of repetition for a given entry.
pub fn repeat(&mut self, repeat: Option<Repeat>) -> &mut Self {
pub fn repeat(&mut self, repeat: Option<Count>) -> &mut Self {
self.repeat = repeat;
self
}
@ -336,7 +336,7 @@ impl RunnerOptionsBuilder {
/// Sets maximum number of retries.
///
/// Default is 0.
pub fn retry(&mut self, retry: Option<Retry>) -> &mut Self {
pub fn retry(&mut self, retry: Option<Count>) -> &mut Self {
self.retry = retry;
self
}
@ -470,9 +470,9 @@ pub struct RunnerOptions {
pub(crate) post_entry: Option<fn() -> bool>,
pub(crate) pre_entry: Option<fn(Entry) -> bool>,
pub(crate) proxy: Option<String>,
pub(crate) repeat: Option<Repeat>,
pub(crate) repeat: Option<Count>,
pub(crate) resolves: Vec<String>,
pub(crate) retry: Option<Retry>,
pub(crate) retry: Option<Count>,
pub(crate) retry_interval: Duration,
pub(crate) skip: bool,
pub(crate) ssl_no_revoke: bool,

View File

@ -17,7 +17,7 @@
*/
use crate::ast::json;
use crate::reader::Pos;
use crate::typing::{Repeat, Retry};
use crate::typing::Count;
///
/// Hurl AST
@ -726,9 +726,9 @@ pub enum OptionKind {
Output(Template),
PathAsIs(BooleanOption),
Proxy(Template),
Repeat(RepeatOption),
Repeat(CountOption),
Resolve(Template),
Retry(RetryOption),
Retry(CountOption),
RetryInterval(NaturalOption),
Skip(BooleanOption),
UnixSocket(Template),
@ -831,14 +831,8 @@ pub enum NaturalOption {
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RepeatOption {
Literal(Repeat),
Expression(Expr),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RetryOption {
Literal(Retry),
pub enum CountOption {
Literal(Count),
Expression(Expr),
}

View File

@ -187,20 +187,11 @@ impl fmt::Display for NaturalOption {
}
}
impl fmt::Display for RepeatOption {
impl fmt::Display for CountOption {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RepeatOption::Literal(v) => write!(f, "{}", v),
RepeatOption::Expression(v) => write!(f, "{}", v),
}
}
}
impl fmt::Display for RetryOption {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RetryOption::Literal(v) => write!(f, "{}", v),
RetryOption::Expression(v) => write!(f, "{}", v),
CountOption::Literal(v) => write!(f, "{}", v),
CountOption::Expression(v) => write!(f, "{}", v),
}
}
}

View File

@ -18,7 +18,7 @@
use std::fmt::Display;
use crate::ast::*;
use crate::typing::{Repeat, Retry};
use crate::typing::Count;
/// Returns an HTML string of the Hurl file `hurl_file`.
///
@ -238,9 +238,9 @@ impl HtmlFormatter {
OptionKind::Output(filename) => self.fmt_filename(filename),
OptionKind::PathAsIs(value) => self.fmt_bool_option(value),
OptionKind::Proxy(value) => self.fmt_template(value),
OptionKind::Repeat(value) => self.fmt_repeat_option(value),
OptionKind::Repeat(value) => self.fmt_count_option(value),
OptionKind::Resolve(value) => self.fmt_template(value),
OptionKind::Retry(value) => self.fmt_retry_option(value),
OptionKind::Retry(value) => self.fmt_count_option(value),
OptionKind::RetryInterval(value) => self.fmt_natural_option(value),
OptionKind::Skip(value) => self.fmt_bool_option(value),
OptionKind::UnixSocket(value) => self.fmt_template(value),
@ -253,31 +253,17 @@ impl HtmlFormatter {
self.fmt_lt(&option.line_terminator0);
}
fn fmt_repeat_option(&mut self, repeat_option: &RepeatOption) {
match repeat_option {
RepeatOption::Literal(repeat) => self.fmt_repeat(repeat),
RepeatOption::Expression(expr) => self.fmt_expr(expr),
fn fmt_count_option(&mut self, count_option: &CountOption) {
match count_option {
CountOption::Literal(repeat) => self.fmt_count(*repeat),
CountOption::Expression(expr) => self.fmt_expr(expr),
}
}
fn fmt_repeat(&mut self, repeat: &Repeat) {
match repeat {
Repeat::Count(n) => self.fmt_number(n),
Repeat::Forever => self.fmt_number(-1),
};
}
fn fmt_retry_option(&mut self, retry_option: &RetryOption) {
match retry_option {
RetryOption::Literal(retry) => self.fmt_retry(retry),
RetryOption::Expression(expr) => self.fmt_expr(expr),
};
}
fn fmt_retry(&mut self, retry: &Retry) {
match retry {
Retry::Finite(n) => self.fmt_number(n),
Retry::Infinite => self.fmt_number(-1),
fn fmt_count(&mut self, count: Count) {
match count {
Count::Finite(n) => self.fmt_number(n),
Count::Infinite => self.fmt_number(-1),
};
}

View File

@ -23,7 +23,7 @@ use crate::parser::primitives::*;
use crate::parser::string::*;
use crate::parser::{expr, filename, filename_password, ParseResult};
use crate::reader::Reader;
use crate::typing::{Repeat, Retry};
use crate::typing::Count;
/// Parse an option in an `[Options]` section.
pub fn parse(reader: &mut Reader) -> ParseResult<EntryOption> {
@ -208,7 +208,7 @@ fn option_proxy(reader: &mut Reader) -> ParseResult<OptionKind> {
}
fn option_repeat(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = repeat_option(reader)?;
let value = non_recover(count_option, reader)?;
Ok(OptionKind::Repeat(value))
}
@ -218,7 +218,7 @@ fn option_resolve(reader: &mut Reader) -> ParseResult<OptionKind> {
}
fn option_retry(reader: &mut Reader) -> ParseResult<OptionKind> {
let value = retry_option(reader)?;
let value = non_recover(count_option, reader)?;
Ok(OptionKind::Retry(value))
}
@ -257,31 +257,16 @@ fn option_very_verbose(reader: &mut Reader) -> ParseResult<OptionKind> {
Ok(OptionKind::VeryVerbose(value))
}
fn repeat(reader: &mut Reader) -> ParseResult<Repeat> {
fn count(reader: &mut Reader) -> ParseResult<Count> {
let start = reader.cursor();
let value = non_recover(integer, reader)?;
if value == -1 {
Ok(Repeat::Forever)
Ok(Count::Infinite)
} else if value >= 0 {
Ok(Repeat::Count(value as usize))
Ok(Count::Finite(value as usize))
} else {
let kind = ParseErrorKind::Expecting {
value: "Expecting a repeat value".to_string(),
};
Err(ParseError::new(start.pos, false, kind))
}
}
fn retry(reader: &mut Reader) -> ParseResult<Retry> {
let start = reader.cursor();
let value = non_recover(integer, reader)?;
if value == -1 {
Ok(Retry::Infinite)
} else if value >= 0 {
Ok(Retry::Finite(value as usize))
} else {
let kind = ParseErrorKind::Expecting {
value: "Expecting a retry value".to_string(),
value: "Expecting a count value".to_string(),
};
Err(ParseError::new(start.pos, false, kind))
}
@ -321,27 +306,10 @@ fn natural_option(reader: &mut Reader) -> ParseResult<NaturalOption> {
}
}
fn repeat_option(reader: &mut Reader) -> ParseResult<RepeatOption> {
fn count_option(reader: &mut Reader) -> ParseResult<CountOption> {
let start = reader.cursor();
match repeat(reader) {
Ok(v) => Ok(RepeatOption::Literal(v)),
Err(_) => {
reader.seek(start);
let exp = expr::parse(reader).map_err(|e| {
let kind = ParseErrorKind::Expecting {
value: "integer".to_string(),
};
ParseError::new(e.pos, false, kind)
})?;
Ok(RepeatOption::Expression(exp))
}
}
}
fn retry_option(reader: &mut Reader) -> ParseResult<RetryOption> {
let start = reader.cursor();
match retry(reader) {
Ok(v) => Ok(RetryOption::Literal(v)),
match count(reader) {
Ok(v) => Ok(CountOption::Literal(v)),
Err(_) => {
reader.seek(start);
let exp = expr::parse(reader).map_err(|e| {
@ -350,7 +318,7 @@ fn retry_option(reader: &mut Reader) -> ParseResult<RetryOption> {
};
ParseError::new(e.pos, false, kind)
})?;
Ok(RetryOption::Expression(exp))
Ok(CountOption::Expression(exp))
}
}
}

View File

@ -18,43 +18,22 @@
//! Hurl common types.
use core::fmt;
/// Represents a repeat operation, either finite or infinite.
/// Represents a count operation, either finite or infinite.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Repeat {
Count(usize),
Forever,
}
impl Default for Repeat {
fn default() -> Self {
Repeat::Count(1)
}
}
impl fmt::Display for Repeat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let value = match self {
Repeat::Count(n) => *n as i64,
Repeat::Forever => -1,
};
write!(f, "{}", value)
}
}
/// Represents a retry operation (when an operation has failed), either finite or infinite.
/// Contrary to [`Repeat`], [`Retry`] has a notion of retry only on operation failure, while
/// [`Repeat`] is unconditional.
#[derive(Clone, Debug, PartialEq, Eq, Copy)]
pub enum Retry {
pub enum Count {
Finite(usize),
Infinite,
}
impl fmt::Display for Retry {
impl fmt::Display for Count {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let value = match self {
Retry::Finite(n) => *n as i64,
Retry::Infinite => -1,
};
write!(f, "{}", value)
match self {
Count::Finite(n) => {
write!(f, "{n}")
}
Count::Infinite => {
write!(f, "-1")
}
}
}
}

View File

@ -18,7 +18,7 @@
use base64::engine::general_purpose;
use base64::Engine;
use hurl_core::ast::*;
use hurl_core::typing::{Repeat, Retry};
use hurl_core::typing::Count;
use super::serialize_json::*;
@ -353,38 +353,20 @@ impl ToJson for NaturalOption {
}
}
impl ToJson for RepeatOption {
impl ToJson for CountOption {
fn to_json(&self) -> JValue {
match self {
RepeatOption::Literal(value) => value.to_json(),
RepeatOption::Expression(expr) => expr.to_json(),
CountOption::Literal(value) => value.to_json(),
CountOption::Expression(expr) => expr.to_json(),
}
}
}
impl ToJson for Repeat {
impl ToJson for Count {
fn to_json(&self) -> JValue {
match self {
Repeat::Count(n) => JValue::Number(n.to_string()),
Repeat::Forever => JValue::Number("-1".to_string()),
}
}
}
impl ToJson for RetryOption {
fn to_json(&self) -> JValue {
match self {
RetryOption::Literal(value) => value.to_json(),
RetryOption::Expression(expr) => expr.to_json(),
}
}
}
impl ToJson for Retry {
fn to_json(&self) -> JValue {
match self {
Retry::Finite(n) => JValue::Number(n.to_string()),
Retry::Infinite => JValue::Number("-1".to_string()),
Count::Finite(n) => JValue::Number(n.to_string()),
Count::Infinite => JValue::Number("-1".to_string()),
}
}
}

View File

@ -16,7 +16,7 @@
*
*/
use hurl_core::ast::*;
use hurl_core::typing::{Repeat, Retry};
use hurl_core::typing::Count;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Token {
@ -935,38 +935,20 @@ impl Tokenizable for NaturalOption {
}
}
impl Tokenizable for RepeatOption {
impl Tokenizable for CountOption {
fn tokenize(&self) -> Vec<Token> {
match self {
RepeatOption::Literal(retry) => retry.tokenize(),
RepeatOption::Expression(expr) => expr.tokenize(),
CountOption::Literal(retry) => retry.tokenize(),
CountOption::Expression(expr) => expr.tokenize(),
}
}
}
impl Tokenizable for Repeat {
impl Tokenizable for Count {
fn tokenize(&self) -> Vec<Token> {
match self {
Repeat::Count(n) => vec![Token::Number(n.to_string())],
Repeat::Forever => vec![Token::Number("-1".to_string())],
}
}
}
impl Tokenizable for RetryOption {
fn tokenize(&self) -> Vec<Token> {
match self {
RetryOption::Literal(retry) => retry.tokenize(),
RetryOption::Expression(expr) => expr.tokenize(),
}
}
}
impl Tokenizable for Retry {
fn tokenize(&self) -> Vec<Token> {
match self {
Retry::Finite(n) => vec![Token::Number(n.to_string())],
Retry::Infinite => vec![Token::Number("-1".to_string())],
Count::Finite(n) => vec![Token::Number(n.to_string())],
Count::Infinite => vec![Token::Number("-1".to_string())],
}
}
}