use hurl_core::ast::{SourceInfo, Template};
use crate::runner::template::eval_template;
use crate::runner::xpath::{Document, Format, XPathError};
use crate::runner::{RunnerError, RunnerErrorKind, Value, VariableSet};
pub fn eval_xpath(
value: &Value,
expr: &Template,
variables: &VariableSet,
source_info: SourceInfo,
assert: bool,
) -> Result<Option<Value>, RunnerError> {
match value {
Value::String(xml) => {
let Ok(doc) = Document::parse(xml, Format::Html) else {
return Err(RunnerError::new(
source_info,
RunnerErrorKind::FilterInvalidInputValue(
"value is not a valid XML".to_string(),
),
false,
));
};
eval_xpath_doc(&doc, expr, variables)
}
v => {
let kind = RunnerErrorKind::FilterInvalidInputType {
actual: v.kind().to_string(),
expected: "string".to_string(),
};
Err(RunnerError::new(source_info, kind, assert))
}
}
}
pub fn eval_xpath_doc(
doc: &Document,
expr: &Template,
variables: &VariableSet,
) -> Result<Option<Value>, RunnerError> {
let expr_str = eval_template(expr, variables)?;
let result = doc.eval_xpath(&expr_str);
match result {
Ok(value) => Ok(Some(value)),
Err(XPathError::Eval) => Err(RunnerError::new(
expr.source_info,
RunnerErrorKind::InvalidXPathEval,
false,
)),
Err(XPathError::Unsupported) => {
panic!("Unsupported xpath {expr}"); }
}
}
#[cfg(test)]
mod tests {
use hurl_core::ast::{Filter, FilterValue, SourceInfo, Template, TemplateElement, Whitespace};
use hurl_core::reader::Pos;
use hurl_core::types::ToSource;
use super::*;
use crate::runner::VariableSet;
use crate::runner::filter::eval::eval_filter;
fn new_xpath_filter(expr: &str) -> Filter {
Filter {
source_info: SourceInfo::new(Pos::new(1, 1), Pos::new(1, 1)),
value: FilterValue::XPath {
space0: Whitespace {
value: String::new(),
source_info: SourceInfo::new(Pos::new(6, 1), Pos::new(7, 1)),
},
expr: Template::new(
None,
vec![TemplateElement::String {
value: expr.to_string(),
source: expr.to_source(),
}],
SourceInfo::new(Pos::new(7, 1), Pos::new(7 + expr.len(), 1)),
),
},
}
}
#[test]
fn eval_filter_xpath_doc_ok() {
let variables = VariableSet::new();
let html = "<html><body>你好世界</body></html>";
let filter = new_xpath_filter("string(//body/text())");
let ret = eval_filter(&filter, &Value::String(html.to_string()), &variables, false);
assert_eq!(ret.unwrap().unwrap(), Value::String("你好世界".to_string()));
}
#[test]
fn eval_filter_xpath_doc_ko_invalid_xpath() {
let variables = VariableSet::new();
let html = "<html><body>你好世界</body></html>";
let filter = new_xpath_filter("str(//body/text())");
let ret = eval_filter(&filter, &Value::String(html.to_string()), &variables, false);
assert_eq!(ret.unwrap_err().kind, RunnerErrorKind::InvalidXPathEval);
}
#[test]
fn eval_filter_xpath_doc_ko_invalid_xml() {
let variables = VariableSet::new();
let html = "";
let filter = new_xpath_filter("string(//body/text())");
let ret = eval_filter(&filter, &Value::String(html.to_string()), &variables, false);
assert_eq!(
ret.unwrap_err().kind,
RunnerErrorKind::FilterInvalidInputValue("value is not a valid XML".to_string())
);
}
#[test]
fn eval_filter_xpath_doc_ko_invalid_input() {
let variables = VariableSet::new();
let filter = new_xpath_filter("string(//body/text())");
let ret = eval_filter(
&filter,
&Value::Bytes(vec![0xc4, 0xe3, 0xba, 0xc3, 0xca, 0xc0, 0xbd, 0xe7]),
&variables,
false,
);
assert_eq!(
ret.unwrap_err().kind,
RunnerErrorKind::FilterInvalidInputType {
actual: "bytes".to_string(),
expected: "string".to_string()
}
);
}
}