use crate::ast::{CertificateAttributeName, Query, QueryValue, RegexValue, SourceInfo};
use crate::combinator::{ParseError as ParseErrorTrait, choice};
use crate::parser::cookiepath::cookiepath;
use crate::parser::primitives::{literal, one_or_more_spaces, regex, try_literal};
use crate::parser::string::{quoted_oneline_string, quoted_template};
use crate::parser::{ParseError, ParseErrorKind, ParseResult};
use crate::reader::{Pos, Reader};
pub fn query(reader: &mut Reader) -> ParseResult<Query> {
let start = reader.cursor();
let value = query_value(reader)?;
let end = reader.cursor();
Ok(Query {
source_info: SourceInfo::new(start.pos, end.pos),
value,
})
}
fn query_value(reader: &mut Reader) -> ParseResult<QueryValue> {
choice(
&[
status_query,
version_query,
url_query,
header_query,
cookie_query,
body_query,
xpath_query,
jsonpath_query,
regex_query,
variable_query,
duration_query,
bytes_query,
rawbytes_query,
sha256_query,
md5_query,
certificate_query,
ip_query,
redirects_query,
],
reader,
)
}
fn status_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("status", reader)?;
Ok(QueryValue::Status)
}
fn version_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("version", reader)?;
Ok(QueryValue::Version)
}
fn url_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("url", reader)?;
Ok(QueryValue::Url)
}
fn header_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("header", reader)?;
let space0 = one_or_more_spaces(reader)?;
let name = quoted_template(reader).map_err(|e| e.to_non_recoverable())?;
Ok(QueryValue::Header { space0, name })
}
fn cookie_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("cookie", reader)?;
let space0 = one_or_more_spaces(reader)?;
let start = reader.cursor();
let s = quoted_oneline_string(reader)?;
let pos = Pos::new(start.pos.line, start.pos.column + 1);
let mut cookiepath_reader = Reader::with_pos(s.as_str(), pos);
let expr = cookiepath(&mut cookiepath_reader)?;
Ok(QueryValue::Cookie { space0, expr })
}
fn body_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("body", reader)?;
Ok(QueryValue::Body)
}
fn xpath_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("xpath", reader)?;
let space0 = one_or_more_spaces(reader)?;
let expr = quoted_template(reader).map_err(|e| e.to_non_recoverable())?;
Ok(QueryValue::Xpath { space0, expr })
}
fn jsonpath_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("jsonpath", reader)?;
let space0 = one_or_more_spaces(reader)?;
let expr = quoted_template(reader).map_err(|e| e.to_non_recoverable())?;
Ok(QueryValue::Jsonpath { space0, expr })
}
fn regex_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("regex", reader)?;
let space0 = one_or_more_spaces(reader)?;
let value = regex_value(reader)?;
Ok(QueryValue::Regex { space0, value })
}
pub fn regex_value(reader: &mut Reader) -> ParseResult<RegexValue> {
choice(
&[
|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| {
let kind = ParseErrorKind::Expecting {
value: "\" or /".to_string(),
};
ParseError::new(e.pos, false, kind)
})
}
fn variable_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("variable", reader)?;
let space0 = one_or_more_spaces(reader)?;
let name = quoted_template(reader).map_err(|e| e.to_non_recoverable())?;
Ok(QueryValue::Variable { space0, name })
}
fn duration_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("duration", reader)?;
Ok(QueryValue::Duration)
}
fn bytes_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("bytes", reader)?;
Ok(QueryValue::Bytes)
}
fn rawbytes_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("rawbytes", reader)?;
Ok(QueryValue::RawBytes)
}
fn sha256_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("sha256", reader)?;
Ok(QueryValue::Sha256)
}
fn md5_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("md5", reader)?;
Ok(QueryValue::Md5)
}
fn certificate_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("certificate", reader)?;
let space0 = one_or_more_spaces(reader)?;
let field = certificate_field(reader)?;
Ok(QueryValue::Certificate {
space0,
attribute_name: field,
})
}
fn ip_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("ip", reader)?;
Ok(QueryValue::Ip)
}
fn redirects_query(reader: &mut Reader) -> ParseResult<QueryValue> {
try_literal("redirects", reader)?;
Ok(QueryValue::Redirects)
}
fn certificate_field(reader: &mut Reader) -> ParseResult<CertificateAttributeName> {
literal("\"", reader)?;
if try_literal(r#"Subject""#, reader).is_ok() {
Ok(CertificateAttributeName::Subject)
} else if try_literal(r#"Issuer""#, reader).is_ok() {
Ok(CertificateAttributeName::Issuer)
} else if try_literal(r#"Start-Date""#, reader).is_ok() {
Ok(CertificateAttributeName::StartDate)
} else if try_literal(r#"Expire-Date""#, reader).is_ok() {
Ok(CertificateAttributeName::ExpireDate)
} else if try_literal(r#"Serial-Number""#, reader).is_ok() {
Ok(CertificateAttributeName::SerialNumber)
} else if try_literal(r#"Subject-Alt-Name""#, reader).is_ok() {
Ok(CertificateAttributeName::SubjectAltName)
} else if try_literal(r#"Value""#, reader).is_ok() {
Ok(CertificateAttributeName::Value)
} else {
let value =
"Field <Subject>, <Issuer>, <Start-Date>, <Expire-Date>, <Serial-Number>, <Subject-Alt-Name> or <Value>".to_string();
let kind = ParseErrorKind::Expecting { value };
let cur = reader.cursor();
Err(ParseError::new(cur.pos, false, kind))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{
CookieAttribute, CookieAttributeName, CookiePath, Filter, FilterValue, Template,
TemplateElement, Whitespace,
};
use crate::parser::filter::filters;
use crate::reader::{CharPos, Pos};
use crate::types::ToSource;
#[test]
fn test_query() {
let mut reader = Reader::new("status");
assert_eq!(
query(&mut reader).unwrap(),
Query {
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 7)),
value: QueryValue::Status,
}
);
}
#[test]
fn test_status_query() {
let mut reader = Reader::new("status");
assert_eq!(
query(&mut reader).unwrap(),
Query {
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 7)),
value: QueryValue::Status,
}
);
}
#[test]
fn test_header_query() {
let mut reader = Reader::new("header \"Foo\"");
assert_eq!(
header_query(&mut reader).unwrap(),
QueryValue::Header {
space0: Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(Pos::new(1, 7), Pos::new(1, 8)),
},
name: Template::new(
Some('"'),
vec![TemplateElement::String {
value: "Foo".to_string(),
source: "Foo".to_source(),
}],
SourceInfo::new(Pos::new(1, 8), Pos::new(1, 13))
)
}
);
}
#[test]
fn test_cookie_query() {
let mut reader = Reader::new("cookie \"Foo[Domain]\"");
assert_eq!(
cookie_query(&mut reader).unwrap(),
QueryValue::Cookie {
space0: Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(Pos::new(1, 7), Pos::new(1, 8)),
},
expr: CookiePath {
name: Template::new(
None,
vec![TemplateElement::String {
value: "Foo".to_string(),
source: "Foo".to_source(),
}],
SourceInfo::new(Pos::new(1, 9), Pos::new(1, 12))
),
attribute: Some(CookieAttribute {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 13), Pos::new(1, 13)),
},
name: CookieAttributeName::Domain("Domain".to_string()),
space1: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(1, 19), Pos::new(1, 19)),
},
}),
},
}
);
assert_eq!(reader.cursor().index, CharPos(20));
}
#[test]
fn test_xpath_query() {
let mut reader = Reader::new("xpath \"normalize-space(//head/title)\"");
assert_eq!(
xpath_query(&mut reader).unwrap(),
QueryValue::Xpath {
space0: Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 7)),
},
expr: Template::new(
Some('"'),
vec![TemplateElement::String {
value: "normalize-space(//head/title)".to_string(),
source: "normalize-space(//head/title)".to_source(),
}],
SourceInfo::new(Pos::new(1, 7), Pos::new(1, 38))
),
},
);
let mut reader = Reader::new(
"xpath \"normalize-space(//div[contains(concat(' ',normalize-space(@class),' '),' monthly-price ')])\"",
);
assert_eq!(xpath_query(&mut reader).unwrap(), QueryValue::Xpath {
space0: Whitespace { value: String::from(" "), source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 7)) },
expr: Template::new(
Some('"'),
vec![
TemplateElement::String {
value: "normalize-space(//div[contains(concat(' ',normalize-space(@class),' '),' monthly-price ')])".to_string(),
source: "normalize-space(//div[contains(concat(' ',normalize-space(@class),' '),' monthly-price ')])".to_source(),
}
],
SourceInfo::new(Pos::new(1, 7), Pos::new(1, 100))
)
});
}
#[test]
fn test_jsonpath_query() {
let mut reader = Reader::new("jsonpath \"$['statusCode']\"");
assert_eq!(
jsonpath_query(&mut reader).unwrap(),
QueryValue::Jsonpath {
space0: Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(Pos::new(1, 9), Pos::new(1, 10)),
},
expr: Template::new(
Some('"'),
vec![TemplateElement::String {
value: "$['statusCode']".to_string(),
source: "$['statusCode']".to_source(),
}],
SourceInfo::new(Pos::new(1, 10), Pos::new(1, 27))
)
}
);
let mut reader = Reader::new("jsonpath \"$.success\"");
assert_eq!(
jsonpath_query(&mut reader).unwrap(),
QueryValue::Jsonpath {
space0: Whitespace {
value: String::from(" "),
source_info: SourceInfo::new(Pos::new(1, 9), Pos::new(1, 10)),
},
expr: Template::new(
Some('"'),
vec![TemplateElement::String {
value: "$.success".to_string(),
source: "$.success".to_source(),
}],
SourceInfo::new(Pos::new(1, 10), Pos::new(1, 21))
)
}
);
}
#[test]
fn test_query_with_filters() {
let mut reader = Reader::new("body urlDecode ");
assert_eq!(
query(&mut reader).unwrap(),
Query {
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 5)),
value: QueryValue::Body,
}
);
assert_eq!(
filters(&mut reader).unwrap(),
vec![(
Whitespace {
value: " ".to_string(),
source_info: SourceInfo::new(Pos::new(1, 5), Pos::new(1, 6))
},
Filter {
source_info: SourceInfo::new(Pos::new(1, 6), Pos::new(1, 15)),
value: FilterValue::UrlDecode,
}
)]
);
assert_eq!(reader.cursor().index, CharPos(14));
}
#[test]
fn test_bytes_query() {
let mut reader = Reader::new("bytes");
assert_eq!(
query(&mut reader).unwrap(),
Query {
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 6)),
value: QueryValue::Bytes,
}
);
}
#[test]
fn test_rawbytes_query() {
let mut reader = Reader::new("rawbytes");
assert_eq!(
query(&mut reader).unwrap(),
Query {
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 9)),
value: QueryValue::RawBytes,
}
);
}
}