mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-12-31 23:15:53 +03:00
Add option variable in Hurl file
This commit is contained in:
parent
c64888d713
commit
59d32dc46d
@ -28,6 +28,7 @@ use super::request::eval_request;
|
||||
use super::response::{eval_asserts, eval_captures};
|
||||
use super::value::Value;
|
||||
use crate::runner::request::{cookie_storage_clear, cookie_storage_set};
|
||||
use crate::runner::template::eval_template;
|
||||
|
||||
/// Runs an `entry` with `http_client` and returns one or more
|
||||
/// [`EntryResult`] (if following redirect).
|
||||
@ -253,14 +254,16 @@ fn log_request_spec(request: &http::RequestSpec, logger: &Logger) {
|
||||
|
||||
/// Returns a new [`RunnerOptions`] based on the `entry` optional Options section
|
||||
/// and a default `runner_options`.
|
||||
/// The [`variables`] can also be updated if `variable` keys are present in the section.
|
||||
pub fn get_entry_options(
|
||||
entry: &Entry,
|
||||
runner_options: &RunnerOptions,
|
||||
variables: &mut HashMap<String, Value>,
|
||||
logger: &Logger,
|
||||
) -> RunnerOptions {
|
||||
) -> Result<RunnerOptions, Error> {
|
||||
let mut runner_options = runner_options.clone();
|
||||
if !has_options(entry) {
|
||||
return runner_options;
|
||||
return Ok(runner_options);
|
||||
}
|
||||
|
||||
logger.debug("");
|
||||
@ -290,6 +293,13 @@ pub fn get_entry_options(
|
||||
runner_options.max_redirect = Some(option.value);
|
||||
logger.debug(format!("max-redirs: {}", option.value).as_str());
|
||||
}
|
||||
EntryOption::Variable(VariableOption {
|
||||
value: VariableDefinition { name, value, .. },
|
||||
..
|
||||
}) => {
|
||||
let value = eval_variable_value(value, variables)?;
|
||||
variables.insert(name.clone(), value);
|
||||
}
|
||||
EntryOption::Verbose(option) => {
|
||||
runner_options.verbosity = if option.value {
|
||||
Some(Verbosity::Verbose)
|
||||
@ -311,7 +321,23 @@ pub fn get_entry_options(
|
||||
}
|
||||
}
|
||||
}
|
||||
runner_options
|
||||
Ok(runner_options)
|
||||
}
|
||||
|
||||
fn eval_variable_value(
|
||||
variable_value: &VariableValue,
|
||||
variables: &mut HashMap<String, Value>,
|
||||
) -> Result<Value, Error> {
|
||||
match variable_value {
|
||||
VariableValue::Null {} => Ok(Value::Null),
|
||||
VariableValue::Bool(v) => Ok(Value::Bool(*v)),
|
||||
VariableValue::Integer(v) => Ok(Value::Integer(*v)),
|
||||
VariableValue::Float(Float { value, .. }) => Ok(Value::Float(*value)),
|
||||
VariableValue::String(template) => {
|
||||
let s = eval_template(template, variables)?;
|
||||
Ok(Value::String(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [`true`] if this `entry` has an Option section, [`false`] otherwise.
|
||||
|
@ -126,10 +126,23 @@ pub fn run(
|
||||
);
|
||||
logger.debug_important(format!("Executing entry {}", entry_index + 1).as_str());
|
||||
|
||||
let runner_options = entry::get_entry_options(entry, runner_options, logger);
|
||||
|
||||
let entry_results = entry::run(entry, http_client, &mut variables, &runner_options, logger);
|
||||
|
||||
let entry_results =
|
||||
match entry::get_entry_options(entry, runner_options, &mut variables, logger) {
|
||||
Ok(runner_options) => {
|
||||
entry::run(entry, http_client, &mut variables, &runner_options, logger)
|
||||
}
|
||||
Err(error) => {
|
||||
vec![EntryResult {
|
||||
request: None,
|
||||
response: None,
|
||||
captures: vec![],
|
||||
asserts: vec![],
|
||||
errors: vec![error],
|
||||
time_in_ms: 0,
|
||||
compressed: false,
|
||||
}]
|
||||
}
|
||||
};
|
||||
for entry_result in &entry_results {
|
||||
for e in &entry_result.errors {
|
||||
logger.error_rich(e);
|
||||
|
@ -672,6 +672,7 @@ pub enum EntryOption {
|
||||
Insecure(InsecureOption),
|
||||
FollowLocation(FollowLocationOption),
|
||||
MaxRedirect(MaxRedirectOption),
|
||||
Variable(VariableOption),
|
||||
Verbose(VerboseOption),
|
||||
VeryVerbose(VeryVerboseOption),
|
||||
}
|
||||
@ -745,3 +746,30 @@ pub struct MaxRedirectOption {
|
||||
pub value: usize,
|
||||
pub line_terminator0: LineTerminator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct VariableOption {
|
||||
pub line_terminators: Vec<LineTerminator>,
|
||||
pub space0: Whitespace,
|
||||
pub space1: Whitespace,
|
||||
pub space2: Whitespace,
|
||||
pub value: VariableDefinition,
|
||||
pub line_terminator0: LineTerminator,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct VariableDefinition {
|
||||
pub name: String,
|
||||
pub space0: Whitespace,
|
||||
pub space1: Whitespace,
|
||||
pub value: VariableValue,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum VariableValue {
|
||||
Null {},
|
||||
Bool(bool),
|
||||
Integer(i64),
|
||||
Float(Float),
|
||||
String(Template),
|
||||
}
|
||||
|
@ -237,6 +237,7 @@ impl Htmlable for EntryOption {
|
||||
EntryOption::Insecure(option) => option.to_html(),
|
||||
EntryOption::FollowLocation(option) => option.to_html(),
|
||||
EntryOption::MaxRedirect(option) => option.to_html(),
|
||||
EntryOption::Variable(option) => option.to_html(),
|
||||
EntryOption::Verbose(option) => option.to_html(),
|
||||
EntryOption::VeryVerbose(option) => option.to_html(),
|
||||
}
|
||||
@ -328,6 +329,45 @@ impl Htmlable for MaxRedirectOption {
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for VariableOption {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
add_line_terminators(&mut buffer, self.line_terminators.clone());
|
||||
buffer.push_str("<span class=\"line\">");
|
||||
buffer.push_str(self.space0.to_html().as_str());
|
||||
buffer.push_str("<span class=\"string\">variable</span>");
|
||||
buffer.push_str(self.space1.to_html().as_str());
|
||||
buffer.push_str("<span>:</span>");
|
||||
buffer.push_str(self.space2.to_html().as_str());
|
||||
buffer.push_str(self.value.to_html().as_str());
|
||||
buffer.push_str("</span>");
|
||||
buffer.push_str(self.line_terminator0.to_html().as_str());
|
||||
buffer
|
||||
}
|
||||
}
|
||||
impl Htmlable for VariableDefinition {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
buffer.push_str(self.name.as_str());
|
||||
buffer.push_str(self.space1.to_html().as_str());
|
||||
buffer.push_str("<span>=</span>");
|
||||
buffer.push_str(self.value.to_html().as_str());
|
||||
buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for VariableValue {
|
||||
fn to_html(&self) -> String {
|
||||
match self {
|
||||
VariableValue::Null { .. } => "<span class=\"null\">null</span>".to_string(),
|
||||
VariableValue::Bool(v) => format!("<span class=\"boolean\">{}</span>", v),
|
||||
VariableValue::Integer(v) => format!("<span class=\"number\">{}</span>", v),
|
||||
VariableValue::Float(v) => format!("<span class=\"number\">{}</span>", v),
|
||||
VariableValue::String(t) => t.to_html(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for VerboseOption {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
|
@ -349,6 +349,7 @@ fn option(reader: &mut Reader) -> ParseResult<'static, EntryOption> {
|
||||
option_insecure,
|
||||
option_follow_location,
|
||||
option_max_redirect,
|
||||
option_variable,
|
||||
option_verbose,
|
||||
option_very_verbose,
|
||||
],
|
||||
@ -468,6 +469,94 @@ fn option_max_redirect(reader: &mut Reader) -> ParseResult<'static, EntryOption>
|
||||
Ok(EntryOption::MaxRedirect(option))
|
||||
}
|
||||
|
||||
fn option_variable(reader: &mut Reader) -> ParseResult<'static, EntryOption> {
|
||||
let line_terminators = optional_line_terminators(reader)?;
|
||||
let space0 = zero_or_more_spaces(reader)?;
|
||||
try_literal("variable", reader)?;
|
||||
let space1 = zero_or_more_spaces(reader)?;
|
||||
try_literal(":", reader)?;
|
||||
let space2 = zero_or_more_spaces(reader)?;
|
||||
let value = variable_definition(reader)?;
|
||||
let line_terminator0 = line_terminator(reader)?;
|
||||
let option = VariableOption {
|
||||
line_terminators,
|
||||
space0,
|
||||
space1,
|
||||
space2,
|
||||
value,
|
||||
line_terminator0,
|
||||
};
|
||||
Ok(EntryOption::Variable(option))
|
||||
}
|
||||
|
||||
fn variable_definition(reader: &mut Reader) -> ParseResult<'static, VariableDefinition> {
|
||||
let name = variable_name(reader)?;
|
||||
let space0 = zero_or_more_spaces(reader)?;
|
||||
literal("=", reader)?;
|
||||
let space1 = zero_or_more_spaces(reader)?;
|
||||
let value = variable_value(reader)?;
|
||||
Ok(VariableDefinition {
|
||||
name,
|
||||
space0,
|
||||
space1,
|
||||
value,
|
||||
})
|
||||
}
|
||||
|
||||
fn variable_name(reader: &mut Reader) -> ParseResult<'static, String> {
|
||||
let start = reader.state.clone();
|
||||
let name = reader.read_while(|c| c.is_alphanumeric() || *c == '_' || *c == '-');
|
||||
if name.is_empty() {
|
||||
return Err(Error {
|
||||
pos: start.pos,
|
||||
recoverable: false,
|
||||
inner: ParseError::Expecting {
|
||||
value: "variable name".to_string(),
|
||||
},
|
||||
});
|
||||
}
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
fn variable_value(reader: &mut Reader) -> ParseResult<'static, VariableValue> {
|
||||
choice(
|
||||
vec![
|
||||
|p1| match null(p1) {
|
||||
Ok(()) => Ok(VariableValue::Null {}),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
|p1| match boolean(p1) {
|
||||
Ok(value) => Ok(VariableValue::Bool(value)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
|p1| match float(p1) {
|
||||
Ok(value) => Ok(VariableValue::Float(value)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
|p1| match integer(p1) {
|
||||
Ok(value) => Ok(VariableValue::Integer(value)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
|p1| match quoted_template(p1) {
|
||||
Ok(value) => Ok(VariableValue::String(value)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
|p1| match unquoted_template(p1) {
|
||||
Ok(value) => Ok(VariableValue::String(value)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
],
|
||||
reader,
|
||||
)
|
||||
.map_err(|e| Error {
|
||||
pos: e.pos,
|
||||
recoverable: false,
|
||||
inner: ParseError::Expecting {
|
||||
value: "variable value".to_string(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn option_verbose(reader: &mut Reader) -> ParseResult<'static, EntryOption> {
|
||||
let line_terminators = optional_line_terminators(reader)?;
|
||||
let space0 = zero_or_more_spaces(reader)?;
|
||||
@ -772,9 +861,9 @@ mod tests {
|
||||
start: Pos { line: 1, column: 9 },
|
||||
end: Pos {
|
||||
line: 1,
|
||||
column: 27
|
||||
}
|
||||
}
|
||||
column: 27,
|
||||
},
|
||||
},
|
||||
},
|
||||
line_terminator0: LineTerminator {
|
||||
space0: Whitespace {
|
||||
@ -816,6 +905,81 @@ mod tests {
|
||||
assert_eq!(error.recoverable, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variable_definition() {
|
||||
let mut reader = Reader::init("a=1");
|
||||
assert_eq!(
|
||||
variable_definition(&mut reader).unwrap(),
|
||||
VariableDefinition {
|
||||
name: "a".to_string(),
|
||||
space0: Whitespace {
|
||||
value: "".to_string(),
|
||||
source_info: SourceInfo {
|
||||
start: Pos { line: 1, column: 2 },
|
||||
end: Pos { line: 1, column: 2 },
|
||||
},
|
||||
},
|
||||
space1: Whitespace {
|
||||
value: "".to_string(),
|
||||
source_info: SourceInfo {
|
||||
start: Pos { line: 1, column: 3 },
|
||||
end: Pos { line: 1, column: 3 },
|
||||
},
|
||||
},
|
||||
value: VariableValue::Integer(1),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variable_value() {
|
||||
let mut reader = Reader::init("null");
|
||||
assert_eq!(variable_value(&mut reader).unwrap(), VariableValue::Null {});
|
||||
|
||||
let mut reader = Reader::init("true");
|
||||
assert_eq!(
|
||||
variable_value(&mut reader).unwrap(),
|
||||
VariableValue::Bool(true)
|
||||
);
|
||||
|
||||
let mut reader = Reader::init("1");
|
||||
assert_eq!(
|
||||
variable_value(&mut reader).unwrap(),
|
||||
VariableValue::Integer(1)
|
||||
);
|
||||
|
||||
let mut reader = Reader::init("toto");
|
||||
assert_eq!(
|
||||
variable_value(&mut reader).unwrap(),
|
||||
VariableValue::String(Template {
|
||||
quotes: false,
|
||||
elements: vec![TemplateElement::String {
|
||||
value: "toto".to_string(),
|
||||
encoded: "toto".to_string()
|
||||
}],
|
||||
source_info: SourceInfo {
|
||||
start: Pos { line: 1, column: 1 },
|
||||
end: Pos { line: 1, column: 5 },
|
||||
}
|
||||
})
|
||||
);
|
||||
let mut reader = Reader::init("\"123\"");
|
||||
assert_eq!(
|
||||
variable_value(&mut reader).unwrap(),
|
||||
VariableValue::String(Template {
|
||||
quotes: true,
|
||||
elements: vec![TemplateElement::String {
|
||||
value: "123".to_string(),
|
||||
encoded: "123".to_string()
|
||||
}],
|
||||
source_info: SourceInfo {
|
||||
start: Pos { line: 1, column: 1 },
|
||||
end: Pos { line: 1, column: 6 },
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cookie_error() {
|
||||
let mut reader = Reader::init("Foo: {{Bar");
|
||||
|
@ -823,6 +823,7 @@ impl Tokenizable for EntryOption {
|
||||
EntryOption::Insecure(option) => option.tokenize(),
|
||||
EntryOption::FollowLocation(option) => option.tokenize(),
|
||||
EntryOption::MaxRedirect(option) => option.tokenize(),
|
||||
EntryOption::Variable(option) => option.tokenize(),
|
||||
EntryOption::Verbose(option) => option.tokenize(),
|
||||
EntryOption::VeryVerbose(option) => option.tokenize(),
|
||||
}
|
||||
@ -934,6 +935,50 @@ impl Tokenizable for MaxRedirectOption {
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for VariableOption {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
tokens.append(
|
||||
&mut self
|
||||
.line_terminators
|
||||
.iter()
|
||||
.flat_map(|e| e.tokenize())
|
||||
.collect(),
|
||||
);
|
||||
tokens.append(&mut self.space0.tokenize());
|
||||
tokens.push(Token::String("variable".to_string()));
|
||||
tokens.append(&mut self.space1.tokenize());
|
||||
tokens.push(Token::Colon(String::from(":")));
|
||||
tokens.append(&mut self.space2.tokenize());
|
||||
tokens.append(&mut self.value.tokenize());
|
||||
tokens.append(&mut self.line_terminator0.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for VariableDefinition {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![Token::String(self.name.clone())];
|
||||
tokens.append(&mut self.space0.tokenize());
|
||||
tokens.push(Token::Keyword("=".to_string()));
|
||||
tokens.append(&mut self.space1.tokenize());
|
||||
tokens.append(&mut self.value.tokenize());
|
||||
tokens
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for VariableValue {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
match self {
|
||||
VariableValue::Null { .. } => vec![Token::Keyword("null".to_string())],
|
||||
VariableValue::Bool(v) => vec![Token::Boolean(v.to_string())],
|
||||
VariableValue::Integer(v) => vec![Token::Number(v.to_string())],
|
||||
VariableValue::Float(v) => vec![Token::Number(v.to_string())],
|
||||
VariableValue::String(v) => v.tokenize(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for VerboseOption {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
|
Loading…
Reference in New Issue
Block a user