mirror of
https://github.com/Orange-OpenSource/hurl.git
synced 2024-09-11 05:35:53 +03:00
Support regex literal in query/subquery
This commit is contained in:
parent
e549ac9d93
commit
de79388be5
@ -154,12 +154,11 @@ bytes-query: "bytes"
|
||||
|
||||
subquery: regex-subquery | count-subquery
|
||||
|
||||
regex-subquery: "regex" sp quoted-string
|
||||
regex-subquery: "regex" sp (quoted-string | regex)
|
||||
|
||||
count-subquery: "count"
|
||||
|
||||
|
||||
|
||||
# Predicates
|
||||
|
||||
predicate: ("not" sp )? predicate-func
|
||||
|
@ -2,6 +2,6 @@ error: Parsing literal
|
||||
--> tests_error_parser/regex.hurl:5:7
|
||||
|
|
||||
5 | regex ?? not exists
|
||||
| ^ expecting '"'
|
||||
| ^ expecting '" or /'
|
||||
|
|
||||
|
||||
|
@ -74,7 +74,7 @@ error: Subquery error
|
||||
--> tests_failed/predicate.hurl:14:22
|
||||
|
|
||||
14 | jsonpath "$.message" count == 1
|
||||
| ^^^^^ Type from query result and subquery do not match
|
||||
| ^^^^^ Type <string> from query result and subquery do not match
|
||||
|
|
||||
|
||||
error: Assert Failure
|
||||
|
@ -3,5 +3,6 @@
|
||||
<span class="line"><span class="version">HTTP/1.0</span> <span class="number">200</span></span>
|
||||
<span class="line section-header">[Asserts]</span>
|
||||
<span class="line"><span class="query-type">regex</span> <span class="string">"Hello ([0-9]+)!"</span> <span class="not">not</span> <span class="predicate-type">exists</span></span>
|
||||
<span class="line"><span class="query-type">regex</span> <span class="regex">/Hello ([0-9]+)!/</span> <span class="not">not</span> <span class="predicate-type">exists</span></span>
|
||||
<span class="line"><span class="query-type">regex</span> <span class="string">"Hello ([a-zA-Z]+)!"</span> <span class="predicate-type">==</span> <span class="string">"World"</span></span>
|
||||
</span></span></code></pre>
|
||||
<span class="line"><span class="query-type">regex</span> <span class="regex">/Hello ([a-zA-Z]+)!/</span> <span class="predicate-type">==</span> <span class="string">"World"</span></span></span></span></code></pre>
|
@ -3,4 +3,6 @@ GET http://localhost:8000/assert-regex
|
||||
HTTP/1.0 200
|
||||
[Asserts]
|
||||
regex "Hello ([0-9]+)!" not exists
|
||||
regex /Hello ([0-9]+)!/ not exists
|
||||
regex "Hello ([a-zA-Z]+)!" == "World"
|
||||
regex /Hello ([a-zA-Z]+)!/ == "World"
|
@ -1 +1 @@
|
||||
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/assert-regex"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"regex","expr":"Hello ([0-9]+)!"},"predicate":{"not":true,"type":"exist"}},{"query":{"type":"regex","expr":"Hello ([a-zA-Z]+)!"},"predicate":{"type":"equal","value":"World"}}]}}]}
|
||||
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/assert-regex"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"regex","expr":"Hello ([0-9]+)!"},"predicate":{"not":true,"type":"exist"}},{"query":{"type":"regex","expr":{"type":"regex","value":"Hello ([0-9]+)!"}},"predicate":{"not":true,"type":"exist"}},{"query":{"type":"regex","expr":"Hello ([a-zA-Z]+)!"},"predicate":{"type":"equal","value":"World"}},{"query":{"type":"regex","expr":{"type":"regex","value":"Hello ([a-zA-Z]+)!"}},"predicate":{"type":"equal","value":"World"}}]}}]}
|
@ -4,9 +4,12 @@
|
||||
<span class="line section-header">[Captures]</span>
|
||||
<span class="line"><span class="name">param1</span><span>:</span> <span class="query-type">header</span> <span class="string">"header1"</span></span>
|
||||
<span class="line"><span class="name">param2</span><span>:</span> <span class="query-type">header</span> <span class="string">"header2"</span> <span class="subquery-type">regex</span> <span class="string">"Hello (.*)!"</span></span>
|
||||
<span class="line"><span class="name">param3</span><span>:</span> <span class="query-type">header</span> <span class="string">"header2"</span> <span class="subquery-type">regex</span> <span class="regex">/Hello (.*)!/</span></span>
|
||||
<span class="line"></span>
|
||||
<span class="line section-header">[Asserts]</span>
|
||||
<span class="line"><span class="query-type">variable</span> <span class="string">"param1"</span> <span class="predicate-type">==</span> <span class="string">"value1"</span></span>
|
||||
<span class="line"><span class="query-type">variable</span> <span class="string">"param2"</span> <span class="predicate-type">==</span> <span class="string">"Bob"</span></span>
|
||||
<span class="line"><span class="query-type">variable</span> <span class="string">"param3"</span> <span class="predicate-type">==</span> <span class="string">"Bob"</span></span>
|
||||
</span></span><span class="hurl-entry"><span class="request"><span class="line"></span>
|
||||
<span class="line"><span class="method">GET</span> <span class="url">http://localhost:8000/captures-check</span></span>
|
||||
<span class="line section-header">[QueryStringParams]</span>
|
||||
|
@ -4,9 +4,12 @@ HTTP/1.0 200
|
||||
[Captures]
|
||||
param1: header "header1"
|
||||
param2: header "header2" regex "Hello (.*)!"
|
||||
param3: header "header2" regex /Hello (.*)!/
|
||||
|
||||
[Asserts]
|
||||
variable "param1" == "value1"
|
||||
variable "param2" == "Bob"
|
||||
variable "param3" == "Bob"
|
||||
|
||||
GET http://localhost:8000/captures-check
|
||||
[QueryStringParams]
|
||||
|
@ -1 +1 @@
|
||||
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/captures"},"response":{"version":"HTTP/1.0","status":200,"captures":[{"name":"param1","query":{"type":"header","name":"header1"}},{"name":"param2","query":{"type":"header","name":"header2","subquery":{"type":"regex","expr":"Hello (.*)!"}}}],"asserts":[{"query":{"type":"variable","name":"param1"},"predicate":{"type":"equal","value":"value1"}},{"query":{"type":"variable","name":"param2"},"predicate":{"type":"equal","value":"Bob"}}]}},{"request":{"method":"GET","url":"http://localhost:8000/captures-check","query_string_params":[{"name":"param1","value":"{{param1}}"},{"name":"param2","value":"{{param2}}"}]},"response":{"version":"HTTP/1.0","status":200}},{"request":{"method":"GET","url":"http://localhost:8000/captures-json"},"response":{"version":"HTTP/1.0","status":200,"captures":[{"name":"an_object","query":{"type":"jsonpath","expr":"$['an_object']"}},{"name":"a_list","query":{"type":"jsonpath","expr":"$['a_list']"}},{"name":"a_null","query":{"type":"jsonpath","expr":"$['a_null']"}},{"name":"an_integer","query":{"type":"jsonpath","expr":"$['an_integer']"}},{"name":"a_float","query":{"type":"jsonpath","expr":"$['a_float']"}},{"name":"a_bool","query":{"type":"jsonpath","expr":"$['a_bool']"}},{"name":"a_string","query":{"type":"jsonpath","expr":"$['a_string']"}},{"name":"all","query":{"type":"jsonpath","expr":"$"}}],"asserts":[{"query":{"type":"variable","name":"a_null"},"predicate":{"type":"exist"}},{"query":{"type":"variable","name":"undefined"},"predicate":{"not":true,"type":"exist"}},{"query":{"type":"variable","name":"a_null"},"predicate":{"type":"equal","value":"a_null"}},{"query":{"type":"variable","name":"an_integer"},"predicate":{"type":"equal","value":"an_integer"}},{"query":{"type":"variable","name":"a_float"},"predicate":{"type":"equal","value":"a_float"}},{"query":{"type":"variable","name":"a_bool"},"predicate":{"type":"equal","value":"a_bool"}},{"query":{"type":"variable","name":"a_string"},"predicate":{"type":"equal","value":"a_string"}},{"query":{"type":"variable","name":"a_list"},"predicate":{"type":"equal","value":"a_list"}}]}}]}
|
||||
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/captures"},"response":{"version":"HTTP/1.0","status":200,"captures":[{"name":"param1","query":{"type":"header","name":"header1"}},{"name":"param2","query":{"type":"header","name":"header2","subquery":{"type":"regex","expr":"Hello (.*)!"}}},{"name":"param3","query":{"type":"header","name":"header2","subquery":{"type":"regex","expr":{"type":"regex","value":"Hello (.*)!"}}}}],"asserts":[{"query":{"type":"variable","name":"param1"},"predicate":{"type":"equal","value":"value1"}},{"query":{"type":"variable","name":"param2"},"predicate":{"type":"equal","value":"Bob"}},{"query":{"type":"variable","name":"param3"},"predicate":{"type":"equal","value":"Bob"}}]}},{"request":{"method":"GET","url":"http://localhost:8000/captures-check","query_string_params":[{"name":"param1","value":"{{param1}}"},{"name":"param2","value":"{{param2}}"}]},"response":{"version":"HTTP/1.0","status":200}},{"request":{"method":"GET","url":"http://localhost:8000/captures-json"},"response":{"version":"HTTP/1.0","status":200,"captures":[{"name":"an_object","query":{"type":"jsonpath","expr":"$['an_object']"}},{"name":"a_list","query":{"type":"jsonpath","expr":"$['a_list']"}},{"name":"a_null","query":{"type":"jsonpath","expr":"$['a_null']"}},{"name":"an_integer","query":{"type":"jsonpath","expr":"$['an_integer']"}},{"name":"a_float","query":{"type":"jsonpath","expr":"$['a_float']"}},{"name":"a_bool","query":{"type":"jsonpath","expr":"$['a_bool']"}},{"name":"a_string","query":{"type":"jsonpath","expr":"$['a_string']"}},{"name":"all","query":{"type":"jsonpath","expr":"$"}}],"asserts":[{"query":{"type":"variable","name":"a_null"},"predicate":{"type":"exist"}},{"query":{"type":"variable","name":"undefined"},"predicate":{"not":true,"type":"exist"}},{"query":{"type":"variable","name":"a_null"},"predicate":{"type":"equal","value":"a_null"}},{"query":{"type":"variable","name":"an_integer"},"predicate":{"type":"equal","value":"an_integer"}},{"query":{"type":"variable","name":"a_float"},"predicate":{"type":"equal","value":"a_float"}},{"query":{"type":"variable","name":"a_bool"},"predicate":{"type":"equal","value":"a_bool"}},{"query":{"type":"variable","name":"a_string"},"predicate":{"type":"equal","value":"a_string"}},{"query":{"type":"variable","name":"a_list"},"predicate":{"type":"equal","value":"a_list"}}]}}]}
|
@ -162,7 +162,7 @@ pub enum RunnerError {
|
||||
QueryInvalidJson,
|
||||
NoQueryResult,
|
||||
|
||||
SubqueryInvalidInput,
|
||||
SubqueryInvalidInput(String),
|
||||
|
||||
// Predicate
|
||||
PredicateType,
|
||||
|
@ -98,8 +98,8 @@ impl Error for runner::Error {
|
||||
RunnerError::PredicateType { .. } => {
|
||||
"predicate type inconsistent with value return by query".to_string()
|
||||
}
|
||||
RunnerError::SubqueryInvalidInput => {
|
||||
"Type from query result and subquery do not match".to_string()
|
||||
RunnerError::SubqueryInvalidInput(t) => {
|
||||
format!("Type <{}> from query result and subquery do not match", t)
|
||||
}
|
||||
RunnerError::InvalidDecoding { charset } => {
|
||||
format!("The body can not be decoded with charset '{}'", charset)
|
||||
|
@ -43,7 +43,7 @@ pub fn eval_query(
|
||||
} else {
|
||||
Err(Error {
|
||||
source_info: subquery.source_info,
|
||||
inner: RunnerError::SubqueryInvalidInput,
|
||||
inner: RunnerError::SubqueryInvalidInput("none".to_string()),
|
||||
assert: false,
|
||||
})
|
||||
}
|
||||
@ -184,9 +184,7 @@ pub fn eval_query_value(
|
||||
Ok(Some(Value::from_json(&serde_json::Value::Array(results))))
|
||||
}
|
||||
}
|
||||
QueryValue::Regex { expr, .. } => {
|
||||
let value = eval_template(&expr, variables)?;
|
||||
let source_info = expr.source_info;
|
||||
QueryValue::Regex { value, .. } => {
|
||||
let s = match http_response.text() {
|
||||
Err(inner) => {
|
||||
return Err(Error {
|
||||
@ -197,19 +195,29 @@ pub fn eval_query_value(
|
||||
}
|
||||
Ok(v) => v,
|
||||
};
|
||||
match Regex::new(value.as_str()) {
|
||||
Ok(re) => match re.captures(s.as_str()) {
|
||||
Some(captures) => match captures.get(1) {
|
||||
Some(v) => Ok(Some(Value::String(v.as_str().to_string()))),
|
||||
None => Ok(None),
|
||||
},
|
||||
let re = match value {
|
||||
RegexValue::Template(t) => {
|
||||
let value = eval_template(&t, variables)?;
|
||||
match Regex::new(value.as_str()) {
|
||||
Ok(re) => re,
|
||||
Err(_) => {
|
||||
let source_info = t.source_info;
|
||||
return Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::InvalidRegex(),
|
||||
assert: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
RegexValue::Regex(re) => re.inner,
|
||||
};
|
||||
match re.captures(s.as_str()) {
|
||||
Some(captures) => match captures.get(1) {
|
||||
Some(v) => Ok(Some(Value::String(v.as_str().to_string()))),
|
||||
None => Ok(None),
|
||||
},
|
||||
Err(_) => Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::InvalidRegex(),
|
||||
assert: false,
|
||||
}),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
QueryValue::Variable { name, .. } => {
|
||||
@ -525,14 +533,14 @@ pub mod tests {
|
||||
value: String::from(""),
|
||||
source_info: SourceInfo::init(1, 6, 1, 7),
|
||||
},
|
||||
expr: Template {
|
||||
value: RegexValue::Template(Template {
|
||||
quotes: true,
|
||||
elements: vec![TemplateElement::String {
|
||||
value: "Hello ([a-zA-Z]+)!".to_string(),
|
||||
encoded: "Hello ([a-zA-Z]+)!".to_string(),
|
||||
}],
|
||||
source_info: SourceInfo::init(1, 7, 1, 26),
|
||||
},
|
||||
}),
|
||||
},
|
||||
subquery: None,
|
||||
}
|
||||
@ -547,14 +555,14 @@ pub mod tests {
|
||||
value: String::from(""),
|
||||
source_info: SourceInfo::init(1, 6, 1, 7),
|
||||
},
|
||||
expr: Template {
|
||||
value: RegexValue::Template(Template {
|
||||
quotes: true,
|
||||
elements: vec![TemplateElement::String {
|
||||
value: "???".to_string(),
|
||||
encoded: "???".to_string(),
|
||||
}],
|
||||
source_info: SourceInfo::init(1, 7, 1, 10),
|
||||
},
|
||||
}),
|
||||
},
|
||||
subquery: None,
|
||||
}
|
||||
|
@ -29,38 +29,47 @@ pub fn eval_subquery(
|
||||
variables: &HashMap<String, Value>,
|
||||
) -> Result<Option<Value>, Error> {
|
||||
match subquery.value {
|
||||
SubqueryValue::Regex { expr, .. } => {
|
||||
eval_regex(value, expr, variables, subquery.source_info)
|
||||
}
|
||||
SubqueryValue::Regex {
|
||||
value: regex_value, ..
|
||||
} => eval_regex(value, regex_value, variables, subquery.source_info),
|
||||
SubqueryValue::Count {} => eval_count(value, subquery.source_info),
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_regex(
|
||||
value: Value,
|
||||
expr: Template,
|
||||
regex_value: RegexValue,
|
||||
variables: &HashMap<String, Value>,
|
||||
source_info: SourceInfo,
|
||||
) -> Result<Option<Value>, Error> {
|
||||
let templ = eval_template(&expr, variables)?;
|
||||
let re = match regex_value {
|
||||
RegexValue::Template(t) => {
|
||||
let value = eval_template(&t, variables)?;
|
||||
match Regex::new(value.as_str()) {
|
||||
Ok(re) => re,
|
||||
Err(_) => {
|
||||
return Err(Error {
|
||||
source_info: t.source_info,
|
||||
inner: RunnerError::InvalidRegex(),
|
||||
assert: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
RegexValue::Regex(re) => re.inner,
|
||||
};
|
||||
|
||||
match value {
|
||||
Value::String(s) => match Regex::new(templ.as_str()) {
|
||||
Ok(re) => match re.captures(s.as_str()) {
|
||||
Some(captures) => match captures.get(1) {
|
||||
Some(v) => Ok(Some(Value::String(v.as_str().to_string()))),
|
||||
None => Ok(None),
|
||||
},
|
||||
Value::String(s) => match re.captures(s.as_str()) {
|
||||
Some(captures) => match captures.get(1) {
|
||||
Some(v) => Ok(Some(Value::String(v.as_str().to_string()))),
|
||||
None => Ok(None),
|
||||
},
|
||||
Err(_) => Err(Error {
|
||||
source_info: expr.source_info,
|
||||
inner: RunnerError::InvalidRegex(),
|
||||
assert: false,
|
||||
}),
|
||||
None => Ok(None),
|
||||
},
|
||||
_ => Err(Error {
|
||||
v => Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::SubqueryInvalidInput,
|
||||
inner: RunnerError::SubqueryInvalidInput(v._type()),
|
||||
assert: false,
|
||||
}),
|
||||
}
|
||||
@ -71,9 +80,9 @@ fn eval_count(value: Value, source_info: SourceInfo) -> Result<Option<Value>, Er
|
||||
Value::List(values) => Ok(Some(Value::Integer(values.len() as i64))),
|
||||
Value::Bytes(values) => Ok(Some(Value::Integer(values.len() as i64))),
|
||||
Value::Nodeset(size) => Ok(Some(Value::Integer(size as i64))),
|
||||
_ => Err(Error {
|
||||
v => Err(Error {
|
||||
source_info,
|
||||
inner: RunnerError::SubqueryInvalidInput,
|
||||
inner: RunnerError::SubqueryInvalidInput(v._type()),
|
||||
assert: false,
|
||||
}),
|
||||
}
|
||||
@ -96,21 +105,21 @@ pub mod tests {
|
||||
source_info: SourceInfo::init(1, 1, 1, 20),
|
||||
value: SubqueryValue::Regex {
|
||||
space0: whitespace,
|
||||
expr: Template {
|
||||
value: RegexValue::Template(Template {
|
||||
quotes: false,
|
||||
elements: vec![TemplateElement::String {
|
||||
value: "Hello (.*)!".to_string(),
|
||||
encoded: "Hello (.*)!".to_string(),
|
||||
}],
|
||||
source_info: SourceInfo::init(1, 7, 1, 20),
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
assert_eq!(
|
||||
eval_subquery(
|
||||
subquery.clone(),
|
||||
Value::String("Hello Bob!".to_string()),
|
||||
&variables
|
||||
&variables,
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
@ -121,7 +130,10 @@ pub mod tests {
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.source_info, SourceInfo::init(1, 1, 1, 20));
|
||||
assert_eq!(error.inner, RunnerError::SubqueryInvalidInput);
|
||||
assert_eq!(
|
||||
error.inner,
|
||||
RunnerError::SubqueryInvalidInput("boolean".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -135,14 +147,14 @@ pub mod tests {
|
||||
source_info: SourceInfo::init(1, 1, 1, 20),
|
||||
value: SubqueryValue::Regex {
|
||||
space0: whitespace,
|
||||
expr: Template {
|
||||
value: RegexValue::Template(Template {
|
||||
quotes: false,
|
||||
elements: vec![TemplateElement::String {
|
||||
value: "???".to_string(),
|
||||
encoded: "???".to_string(),
|
||||
}],
|
||||
source_info: SourceInfo::init(1, 7, 1, 20),
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
let error = eval_subquery(
|
||||
@ -169,9 +181,9 @@ pub mod tests {
|
||||
Value::List(vec![
|
||||
Value::Integer(1),
|
||||
Value::Integer(2),
|
||||
Value::Integer(3)
|
||||
Value::Integer(3),
|
||||
]),
|
||||
&variables
|
||||
&variables,
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
@ -182,6 +194,9 @@ pub mod tests {
|
||||
.err()
|
||||
.unwrap();
|
||||
assert_eq!(error.source_info, SourceInfo::init(1, 1, 1, 20));
|
||||
assert_eq!(error.inner, RunnerError::SubqueryInvalidInput);
|
||||
assert_eq!(
|
||||
error.inner,
|
||||
RunnerError::SubqueryInvalidInput("boolean".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ pub enum QueryValue {
|
||||
},
|
||||
Regex {
|
||||
space0: Whitespace,
|
||||
expr: Template,
|
||||
value: RegexValue,
|
||||
},
|
||||
Variable {
|
||||
space0: Whitespace,
|
||||
@ -334,6 +334,12 @@ pub enum QueryValue {
|
||||
Md5 {},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum RegexValue {
|
||||
Template(Template),
|
||||
Regex(Regex),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CookiePath {
|
||||
pub name: Template,
|
||||
@ -382,7 +388,10 @@ pub struct Subquery {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum SubqueryValue {
|
||||
Regex { space0: Whitespace, expr: Template },
|
||||
Regex {
|
||||
space0: Whitespace,
|
||||
value: RegexValue,
|
||||
},
|
||||
Count {},
|
||||
}
|
||||
|
||||
|
@ -365,12 +365,10 @@ impl Htmlable for QueryValue {
|
||||
format!("<span class=\"string\">\"{}\"</span>", expr.to_html()).as_str(),
|
||||
);
|
||||
}
|
||||
QueryValue::Regex { space0, expr } => {
|
||||
QueryValue::Regex { space0, value } => {
|
||||
buffer.push_str("<span class=\"query-type\">regex</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(
|
||||
format!("<span class=\"string\">\"{}\"</span>", expr.to_html()).as_str(),
|
||||
);
|
||||
buffer.push_str(value.to_html().as_str());
|
||||
}
|
||||
QueryValue::Variable { space0, name } => {
|
||||
buffer.push_str("<span class=\"query-type\">variable</span>");
|
||||
@ -395,17 +393,28 @@ impl Htmlable for QueryValue {
|
||||
buffer
|
||||
}
|
||||
}
|
||||
impl Htmlable for RegexValue {
|
||||
fn to_html(&self) -> String {
|
||||
match self {
|
||||
RegexValue::Template(template) => {
|
||||
format!("<span class=\"string\">\"{}\"</span>", template.to_html())
|
||||
}
|
||||
RegexValue::Regex(regex) => regex.to_html(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Htmlable for Subquery {
|
||||
fn to_html(&self) -> String {
|
||||
let mut buffer = String::from("");
|
||||
match self.value.clone() {
|
||||
SubqueryValue::Regex { expr, space0 } => {
|
||||
SubqueryValue::Regex { value, space0 } => {
|
||||
buffer.push_str("<span class=\"subquery-type\">regex</span>");
|
||||
buffer.push_str(space0.to_html().as_str());
|
||||
buffer.push_str(
|
||||
format!("<span class=\"string\">\"{}\"</span>", expr.to_html()).as_str(),
|
||||
);
|
||||
// buffer.push_str(
|
||||
// format!("<span class=\"string\">\"{}\"</span>", value.to_html()).as_str(),
|
||||
// );
|
||||
buffer.push_str(value.to_html().as_str());
|
||||
}
|
||||
SubqueryValue::Count {} => {
|
||||
buffer.push_str("<span class=\"subquery-type\">count</span>")
|
||||
|
@ -16,6 +16,7 @@
|
||||
*
|
||||
*/
|
||||
use crate::ast::*;
|
||||
use crate::parser::{Error, ParseError};
|
||||
|
||||
use super::combinators::*;
|
||||
use super::cookiepath::cookiepath;
|
||||
@ -141,8 +142,31 @@ fn jsonpath_query(reader: &mut Reader) -> ParseResult<'static, QueryValue> {
|
||||
fn regex_query(reader: &mut Reader) -> ParseResult<'static, QueryValue> {
|
||||
try_literal("regex", reader)?;
|
||||
let space0 = one_or_more_spaces(reader)?;
|
||||
let expr = quoted_template(reader).map_err(|e| e.non_recoverable())?;
|
||||
Ok(QueryValue::Regex { space0, expr })
|
||||
let value = regex_value(reader)?;
|
||||
Ok(QueryValue::Regex { space0, value })
|
||||
}
|
||||
|
||||
pub fn regex_value(reader: &mut Reader) -> ParseResult<'static, RegexValue> {
|
||||
choice(
|
||||
vec![
|
||||
|p1| match quoted_template(p1) {
|
||||
Ok(value) => Ok(RegexValue::Template(value)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
|p1| match regex(p1) {
|
||||
Ok(value) => Ok(RegexValue::Regex(value)),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
],
|
||||
reader,
|
||||
)
|
||||
.map_err(|e| Error {
|
||||
pos: e.pos,
|
||||
recoverable: false,
|
||||
inner: ParseError::Expecting {
|
||||
value: "\" or /".to_string(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn variable_query(reader: &mut Reader) -> ParseResult<'static, QueryValue> {
|
||||
@ -184,7 +208,7 @@ mod tests {
|
||||
Query {
|
||||
source_info: SourceInfo::init(1, 1, 1, 7),
|
||||
value: QueryValue::Status {},
|
||||
subquery: None
|
||||
subquery: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -197,7 +221,7 @@ mod tests {
|
||||
Query {
|
||||
source_info: SourceInfo::init(1, 1, 1, 7),
|
||||
value: QueryValue::Status {},
|
||||
subquery: None
|
||||
subquery: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -651,14 +651,14 @@ mod tests {
|
||||
value: " ".to_string(),
|
||||
source_info: SourceInfo::init(1, 31, 1, 32),
|
||||
},
|
||||
expr: Template {
|
||||
value: RegexValue::Template(Template {
|
||||
quotes: true,
|
||||
elements: vec![TemplateElement::String {
|
||||
value: "token=(.*)".to_string(),
|
||||
encoded: "token=(.*)".to_string(),
|
||||
}],
|
||||
source_info: SourceInfo::init(1, 32, 1, 44),
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
)),
|
||||
@ -681,7 +681,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
error.inner,
|
||||
ParseError::Expecting {
|
||||
value: "\"".to_string()
|
||||
value: "\" or /".to_string()
|
||||
}
|
||||
);
|
||||
assert!(!error.recoverable);
|
||||
|
@ -20,8 +20,8 @@ use crate::ast::*;
|
||||
use super::combinators::*;
|
||||
use super::primitives::*;
|
||||
use super::reader::Reader;
|
||||
use super::string::*;
|
||||
use super::ParseResult;
|
||||
use crate::parser::query::regex_value;
|
||||
use crate::parser::{Error, ParseError};
|
||||
|
||||
pub fn subquery(reader: &mut Reader) -> ParseResult<'static, Subquery> {
|
||||
@ -41,8 +41,8 @@ fn subquery_value(reader: &mut Reader) -> ParseResult<'static, SubqueryValue> {
|
||||
fn regex_subquery(reader: &mut Reader) -> ParseResult<'static, SubqueryValue> {
|
||||
try_literal("regex", reader)?;
|
||||
let space0 = one_or_more_spaces(reader)?;
|
||||
let expr = quoted_template(reader).map_err(|e| e.non_recoverable())?;
|
||||
Ok(SubqueryValue::Regex { space0, expr })
|
||||
let value = regex_value(reader)?;
|
||||
Ok(SubqueryValue::Regex { space0, value })
|
||||
}
|
||||
|
||||
fn count_subquery(reader: &mut Reader) -> ParseResult<'static, SubqueryValue> {
|
||||
@ -77,14 +77,14 @@ mod tests {
|
||||
value: " ".to_string(),
|
||||
source_info: SourceInfo::init(1, 6, 1, 7)
|
||||
},
|
||||
expr: Template {
|
||||
value: RegexValue::Template(Template {
|
||||
quotes: true,
|
||||
elements: vec![TemplateElement::String {
|
||||
value: "Hello (.*)!".to_string(),
|
||||
encoded: "Hello (.*)!".to_string()
|
||||
}],
|
||||
source_info: SourceInfo::init(1, 7, 1, 20)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -304,9 +304,9 @@ fn query_value_attributes(query_value: &QueryValue) -> Vec<(String, JValue)> {
|
||||
attributes.push(("type".to_string(), JValue::String("xpath".to_string())));
|
||||
attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
|
||||
}
|
||||
QueryValue::Regex { expr, .. } => {
|
||||
QueryValue::Regex { value, .. } => {
|
||||
attributes.push(("type".to_string(), JValue::String("regex".to_string())));
|
||||
attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
|
||||
attributes.push(("expr".to_string(), value.to_json()));
|
||||
}
|
||||
QueryValue::Variable { name, .. } => {
|
||||
attributes.push(("type".to_string(), JValue::String("variable".to_string())));
|
||||
@ -338,9 +338,9 @@ impl ToJson for SubqueryValue {
|
||||
fn to_json(&self) -> JValue {
|
||||
let mut attributes = vec![];
|
||||
match self {
|
||||
SubqueryValue::Regex { expr, .. } => {
|
||||
SubqueryValue::Regex { value, .. } => {
|
||||
attributes.push(("type".to_string(), JValue::String("regex".to_string())));
|
||||
attributes.push(("expr".to_string(), JValue::String(expr.to_string())));
|
||||
attributes.push(("expr".to_string(), value.to_json()));
|
||||
}
|
||||
SubqueryValue::Count { .. } => {
|
||||
attributes.push(("type".to_string(), JValue::String("count".to_string())));
|
||||
@ -350,6 +350,25 @@ impl ToJson for SubqueryValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for RegexValue {
|
||||
fn to_json(&self) -> JValue {
|
||||
match self {
|
||||
RegexValue::Template(template) => JValue::String(template.to_string()),
|
||||
RegexValue::Regex(regex) => regex.to_json(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for Regex {
|
||||
fn to_json(&self) -> JValue {
|
||||
let attributes = vec![
|
||||
("type".to_string(), JValue::String("regex".to_string())),
|
||||
("value".to_string(), JValue::String(self.to_string())),
|
||||
];
|
||||
JValue::Object(attributes)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for Predicate {
|
||||
fn to_json(&self) -> JValue {
|
||||
let mut attributes = vec![];
|
||||
|
@ -471,10 +471,10 @@ impl Tokenizable for QueryValue {
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, expr.tokenize());
|
||||
}
|
||||
QueryValue::Regex { space0, expr } => {
|
||||
QueryValue::Regex { space0, value } => {
|
||||
tokens.push(Token::QueryType(String::from("regex")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, expr.tokenize());
|
||||
add_tokens(&mut tokens, value.tokenize());
|
||||
}
|
||||
QueryValue::Variable { space0, name } => {
|
||||
tokens.push(Token::QueryType(String::from("variable")));
|
||||
@ -490,6 +490,15 @@ impl Tokenizable for QueryValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for RegexValue {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
match self {
|
||||
RegexValue::Template(template) => template.tokenize(),
|
||||
RegexValue::Regex(regex) => regex.tokenize(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tokenizable for CookiePath {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
@ -516,10 +525,10 @@ impl Tokenizable for Subquery {
|
||||
fn tokenize(&self) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = vec![];
|
||||
match self.value.clone() {
|
||||
SubqueryValue::Regex { space0, expr } => {
|
||||
SubqueryValue::Regex { space0, value } => {
|
||||
tokens.push(Token::QueryType(String::from("regex")));
|
||||
add_tokens(&mut tokens, space0.tokenize());
|
||||
add_tokens(&mut tokens, expr.tokenize());
|
||||
add_tokens(&mut tokens, value.tokenize());
|
||||
}
|
||||
SubqueryValue::Count { .. } => {
|
||||
tokens.push(Token::QueryType(String::from("count")));
|
||||
|
@ -291,8 +291,8 @@ impl Lintable<QueryValue> for QueryValue {
|
||||
expr: expr.clone(),
|
||||
space0: one_whitespace(),
|
||||
},
|
||||
QueryValue::Regex { expr, .. } => QueryValue::Regex {
|
||||
expr: expr.clone(),
|
||||
QueryValue::Regex { value, .. } => QueryValue::Regex {
|
||||
value: value.lint(),
|
||||
space0: one_whitespace(),
|
||||
},
|
||||
QueryValue::Variable { name, .. } => QueryValue::Variable {
|
||||
@ -320,6 +320,19 @@ impl Lintable<Subquery> for Subquery {
|
||||
}
|
||||
}
|
||||
|
||||
impl Lintable<RegexValue> for RegexValue {
|
||||
fn errors(&self) -> Vec<Error> {
|
||||
let errors = vec![];
|
||||
errors
|
||||
}
|
||||
|
||||
fn lint(&self) -> RegexValue {
|
||||
match self {
|
||||
RegexValue::Template(template) => RegexValue::Template(template.lint()),
|
||||
RegexValue::Regex(regex) => RegexValue::Regex(regex.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Lintable<SubqueryValue> for SubqueryValue {
|
||||
fn errors(&self) -> Vec<Error> {
|
||||
let errors = vec![];
|
||||
@ -328,9 +341,9 @@ impl Lintable<SubqueryValue> for SubqueryValue {
|
||||
|
||||
fn lint(&self) -> SubqueryValue {
|
||||
match self {
|
||||
SubqueryValue::Regex { expr, .. } => SubqueryValue::Regex {
|
||||
SubqueryValue::Regex { value, .. } => SubqueryValue::Regex {
|
||||
space0: one_whitespace(),
|
||||
expr: expr.clone(),
|
||||
value: value.lint(),
|
||||
},
|
||||
SubqueryValue::Count {} => SubqueryValue::Count {},
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user