Support regex literal in query/subquery

This commit is contained in:
Fabrice Reix 2022-06-23 15:31:48 +02:00 committed by jcamiel
parent e549ac9d93
commit de79388be5
21 changed files with 206 additions and 92 deletions

View File

@ -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

View File

@ -2,6 +2,6 @@ error: Parsing literal
--> tests_error_parser/regex.hurl:5:7
|
5 | regex ?? not exists
| ^ expecting '"'
| ^ expecting '" or /'
|

View File

@ -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

View File

@ -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>

View File

@ -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"

View File

@ -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"}}]}}]}

View File

@ -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>

View File

@ -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]

View File

@ -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"}}]}}]}

View File

@ -162,7 +162,7 @@ pub enum RunnerError {
QueryInvalidJson,
NoQueryResult,
SubqueryInvalidInput,
SubqueryInvalidInput(String),
// Predicate
PredicateType,

View File

@ -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)

View File

@ -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,
}

View File

@ -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())
);
}
}

View File

@ -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 {},
}

View File

@ -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>")

View File

@ -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,
}
);
}

View File

@ -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);

View File

@ -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)
}
})
}
}
);

View File

@ -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![];

View File

@ -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")));

View File

@ -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 {},
}