#[cfg(feature = "embedded")]
use crate::sxd_document as sxd_document_no_unsafe;
use std::fmt;
use sxd_document_no_unsafe::QName;
use crate::context;
use crate::expression::Error;
use crate::nodeset::{self, OrderedNodes};
pub trait NodeTest: fmt::Debug {
fn validate(&self, _context: &context::Evaluation<'_, '_>) -> Result<(), Error> {
Ok(())
}
fn test<'c, 'd>(
&self,
context: &context::Evaluation<'c, 'd>,
result: &mut OrderedNodes<'d>,
) -> Result<(), Error>;
}
impl<T: ?Sized> NodeTest for Box<T>
where
T: NodeTest,
{
fn validate(&self, context: &context::Evaluation<'_, '_>) -> Result<(), Error> {
(**self).validate(context)
}
fn test<'c, 'd>(
&self,
context: &context::Evaluation<'c, 'd>,
result: &mut OrderedNodes<'d>,
) -> Result<(), Error> {
(**self).test(context, result)
}
}
pub type SubNodeTest = Box<dyn NodeTest + 'static>;
#[derive(Debug, Clone, PartialEq)]
pub struct NameTest {
pub prefix: Option<String>,
pub local_part: String,
}
impl NameTest {
fn validate(&self, context: &context::Evaluation<'_, '_>) -> Result<(), Error> {
if let Some(prefix) = &self.prefix
&& context.namespace_for(prefix).is_none()
{
return Err(Error::UnknownNamespace {
prefix: prefix.clone(),
});
}
Ok(())
}
fn matches(
&self,
context: &context::Evaluation<'_, '_>,
node_name: QName<'_>,
) -> Result<bool, Error> {
let is_wildcard = self.local_part == "*";
let test_uri = self
.prefix
.as_ref()
.map(|prefix| {
context
.namespace_for(prefix)
.ok_or_else(|| Error::UnknownNamespace {
prefix: prefix.clone(),
})
})
.transpose()?;
Ok(match (is_wildcard, test_uri) {
(true, None) => true,
(true, Some(..)) => test_uri == node_name.namespace_uri(),
_ => test_uri == node_name.namespace_uri() && self.local_part == node_name.local_part(),
})
}
}
#[derive(Debug)]
pub struct Attribute {
name_test: NameTest,
}
impl Attribute {
pub fn new(name: NameTest) -> Attribute {
Attribute { name_test: name }
}
}
impl NodeTest for Attribute {
fn validate(&self, context: &context::Evaluation<'_, '_>) -> Result<(), Error> {
self.name_test.validate(context)
}
fn test<'c, 'd>(
&self,
context: &context::Evaluation<'c, 'd>,
result: &mut OrderedNodes<'d>,
) -> Result<(), Error> {
if let nodeset::Node::Attribute(ref a) = context.node
&& self
.name_test
.matches(context, sxd_document_no_unsafe::as_qname!(a.name()))?
{
result
.add_metered(context, context.node.clone())
.map_err(|source| Error::FunctionEvaluation { source })?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct Namespace {
name_test: NameTest,
}
impl Namespace {
pub fn new(name: NameTest) -> Namespace {
Namespace { name_test: name }
}
}
impl NodeTest for Namespace {
fn validate(&self, context: &context::Evaluation<'_, '_>) -> Result<(), Error> {
self.name_test.validate(context)
}
fn test<'c, 'd>(
&self,
context: &context::Evaluation<'c, 'd>,
result: &mut OrderedNodes<'d>,
) -> Result<(), Error> {
if let nodeset::Node::Namespace(ref ns) = context.node
&& self.name_test.matches(context, QName::new(ns.prefix()))?
{
result
.add_metered(context, context.node.clone())
.map_err(|source| Error::FunctionEvaluation { source })?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct Element {
name_test: NameTest,
}
impl Element {
pub fn new(name: NameTest) -> Element {
Element { name_test: name }
}
}
impl NodeTest for Element {
fn validate(&self, context: &context::Evaluation<'_, '_>) -> Result<(), Error> {
self.name_test.validate(context)
}
fn test<'c, 'd>(
&self,
context: &context::Evaluation<'c, 'd>,
result: &mut OrderedNodes<'d>,
) -> Result<(), Error> {
if let nodeset::Node::Element(ref e) = context.node
&& self
.name_test
.matches(context, sxd_document_no_unsafe::as_qname!(e.name()))?
{
result
.add_metered(context, context.node.clone())
.map_err(|source| Error::FunctionEvaluation { source })?;
}
Ok(())
}
}
#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct Node;
impl NodeTest for Node {
fn test<'c, 'd>(
&self,
context: &context::Evaluation<'c, 'd>,
result: &mut OrderedNodes<'d>,
) -> Result<(), Error> {
result
.add_metered(context, context.node.clone())
.map_err(|source| Error::FunctionEvaluation { source })?;
Ok(())
}
}
#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct Text;
impl NodeTest for Text {
fn test<'c, 'd>(
&self,
context: &context::Evaluation<'c, 'd>,
result: &mut OrderedNodes<'d>,
) -> Result<(), Error> {
if let nodeset::Node::Text(_) = context.node {
result
.add_metered(context, context.node.clone())
.map_err(|source| Error::FunctionEvaluation { source })?;
}
Ok(())
}
}
#[allow(missing_copy_implementations)]
#[derive(Debug)]
pub struct Comment;
impl NodeTest for Comment {
fn test<'c, 'd>(
&self,
context: &context::Evaluation<'c, 'd>,
result: &mut OrderedNodes<'d>,
) -> Result<(), Error> {
if let nodeset::Node::Comment(_) = context.node {
result
.add_metered(context, context.node.clone())
.map_err(|source| Error::FunctionEvaluation { source })?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct ProcessingInstruction {
target: Option<String>,
}
impl ProcessingInstruction {
pub fn new(target: Option<String>) -> ProcessingInstruction {
ProcessingInstruction { target }
}
}
impl NodeTest for ProcessingInstruction {
fn test<'c, 'd>(
&self,
context: &context::Evaluation<'c, 'd>,
result: &mut OrderedNodes<'d>,
) -> Result<(), Error> {
if let nodeset::Node::ProcessingInstruction(pi) = &context.node {
match self.target {
Some(ref name) if name == sxd_document_no_unsafe::as_str!(pi.target()) => result
.add_metered(context, context.node.clone())
.map_err(|source| Error::FunctionEvaluation { source })?,
Some(_) => {}
None => result
.add_metered(context, context.node.clone())
.map_err(|source| Error::FunctionEvaluation { source })?,
}
}
Ok(())
}
}
#[cfg(test)]
mod test {
use std::borrow::ToOwned;
use sxd_document_no_unsafe::dom::{self, Document};
use sxd_document_no_unsafe::{Package, QName};
use crate::context::{self, Context};
use crate::nodeset::OrderedNodes;
use super::*;
struct Setup<'d> {
doc: Document<'d>,
context: Context<'d>,
}
impl<'d> Setup<'d> {
fn new(package: &'d Package) -> Setup<'d> {
Setup {
doc: package.as_document(),
context: Context::without_core_functions(),
}
}
fn register_prefix(&mut self, prefix: &str, namespace_uri: &str) {
self.context.set_namespace(prefix, namespace_uri);
}
fn context_for_attribute<'n, N>(
&'d self,
name: N,
val: &str,
) -> (dom::Attribute<'d>, context::Evaluation<'d, 'd>)
where
N: Into<QName<'n>>,
{
let e = self.doc.create_element("element");
let a = e.set_attribute_value(name, val);
let c = context::Evaluation::new(&self.context, a.into());
(a, c)
}
fn context_for_ns_attribute(
&'d mut self,
prefix: &str,
nsuri: &str,
local: &str,
value: &str,
) -> (dom::Attribute<'d>, context::Evaluation<'d, 'd>) {
self.register_prefix(prefix, nsuri);
self.context_for_attribute((nsuri, local), value)
}
fn context_for_element<'n, N>(
&'d self,
name: N,
) -> (dom::Element<'d>, context::Evaluation<'d, 'd>)
where
N: Into<QName<'n>>,
{
let e = self.doc.create_element(name);
let c = context::Evaluation::new(&self.context, e.into());
(e, c)
}
fn context_for_ns_element(
&'d mut self,
prefix: &str,
nsuri: &str,
local: &str,
) -> (dom::Element<'d>, context::Evaluation<'d, 'd>) {
self.register_prefix(prefix, nsuri);
self.context_for_element((nsuri, local))
}
}
fn run_attribute<'d>(
context: &context::Evaluation<'d, 'd>,
prefix: Option<&str>,
local: &str,
) -> OrderedNodes<'d> {
let mut result = OrderedNodes::new();
let name = NameTest {
prefix: prefix.map(|p| p.to_owned()),
local_part: local.to_owned(),
};
let test = Attribute::new(name);
test.test(context, &mut result)
.expect("node test evaluates");
result
}
#[test]
fn attribute_test_selects_attributes_with_matching_names() {
let package = Package::new();
let setup = Setup::new(&package);
let (attribute, context) = setup.context_for_attribute("hello", "world");
let result = run_attribute(&context, None, "hello");
assert_eq!(ordered_nodes![attribute], result);
}
#[test]
fn attribute_test_does_not_select_other_names() {
let package = Package::new();
let setup = Setup::new(&package);
let (_, context) = setup.context_for_attribute("goodbye", "world");
let result = run_attribute(&context, None, "hello");
assert_eq!(ordered_nodes![], result);
}
#[test]
fn attribute_test_supports_a_wildcard_match() {
let package = Package::new();
let setup = Setup::new(&package);
let (attribute, context) = setup.context_for_attribute("whatever", "value");
let result = run_attribute(&context, None, "*");
assert_eq!(ordered_nodes![attribute], result);
}
#[test]
fn attribute_test_matches_on_namespace_and_name() {
let package = Package::new();
let mut setup = Setup::new(&package);
setup.register_prefix("another", "different-uri");
let (attribute, context) =
setup.context_for_ns_attribute("prefix", "namespace", "name", "value");
let result = run_attribute(&context, Some("prefix"), "name");
assert_eq!(ordered_nodes![attribute], result);
let result = run_attribute(&context, Some("prefix"), "wrong-name");
assert_eq!(ordered_nodes![], result);
let result = run_attribute(&context, Some("another"), "name");
assert_eq!(ordered_nodes![], result);
}
#[test]
fn attribute_test_matches_on_namespace_when_wildcard_with_prefix() {
let package = Package::new();
let mut setup = Setup::new(&package);
setup.register_prefix("another", "different-uri");
let (attribute, context) =
setup.context_for_ns_attribute("prefix", "namespace", "name", "value");
let result = run_attribute(&context, Some("prefix"), "*");
assert_eq!(ordered_nodes![attribute], result);
let result = run_attribute(&context, Some("another"), "*");
assert_eq!(ordered_nodes![], result);
}
#[test]
fn attribute_test_ignores_namespace_when_wildcard_without_prefix() {
let package = Package::new();
let mut setup = Setup::new(&package);
let (attribute, context) =
setup.context_for_ns_attribute("prefix", "namespace", "name", "value");
let result = run_attribute(&context, None, "*");
assert_eq!(ordered_nodes![attribute], result);
}
#[test]
fn attribute_test_does_not_match_when_attribute_has_namespace_but_without_prefix() {
let package = Package::new();
let mut setup = Setup::new(&package);
let (_, context) = setup.context_for_ns_attribute("prefix", "namespace", "name", "value");
let result = run_attribute(&context, None, "name");
assert_eq!(ordered_nodes![], result);
}
fn run_element<'d>(
context: &context::Evaluation<'d, 'd>,
prefix: Option<&str>,
local: &str,
) -> OrderedNodes<'d> {
let mut result = OrderedNodes::new();
let name = NameTest {
prefix: prefix.map(|p| p.to_owned()),
local_part: local.to_owned(),
};
let test = Element::new(name);
test.test(context, &mut result)
.expect("node test evaluates");
result
}
#[test]
fn element_test_selects_nodes_with_matching_names() {
let package = Package::new();
let setup = Setup::new(&package);
let (element, context) = setup.context_for_element("hello");
let result = run_element(&context, None, "hello");
assert_eq!(ordered_nodes![element], result);
}
#[test]
fn element_test_does_not_select_other_names() {
let package = Package::new();
let setup = Setup::new(&package);
let (_, context) = setup.context_for_element("goodbye");
let result = run_element(&context, None, "hello");
assert_eq!(ordered_nodes![], result);
}
#[test]
fn element_test_supports_a_wildcard_match() {
let package = Package::new();
let setup = Setup::new(&package);
let (element, context) = setup.context_for_element("hello");
let result = run_element(&context, None, "*");
assert_eq!(ordered_nodes![element], result);
}
#[test]
fn element_test_matches_on_namespace_and_name() {
let package = Package::new();
let mut setup = Setup::new(&package);
setup.register_prefix("another", "different-uri");
let (element, context) = setup.context_for_ns_element("prefix", "uri", "name");
let result = run_element(&context, Some("prefix"), "name");
assert_eq!(ordered_nodes![element], result);
let result = run_element(&context, Some("prefix"), "wrong-name");
assert_eq!(ordered_nodes![], result);
let result = run_element(&context, Some("another"), "name");
assert_eq!(ordered_nodes![], result);
}
#[test]
fn element_test_matches_on_namespace_when_wildcard_with_prefix() {
let package = Package::new();
let mut setup = Setup::new(&package);
setup.register_prefix("another", "different-uri");
let (element, context) = setup.context_for_ns_element("prefix", "uri", "name");
let result = run_element(&context, Some("prefix"), "*");
assert_eq!(ordered_nodes![element], result);
let result = run_element(&context, Some("another"), "*");
assert_eq!(ordered_nodes![], result);
}
#[test]
fn element_test_ignores_namespace_when_wildcard_without_prefix() {
let package = Package::new();
let mut setup = Setup::new(&package);
let (element, context) = setup.context_for_ns_element("prefix", "uri", "name");
let result = run_element(&context, None, "*");
assert_eq!(ordered_nodes![element], result);
}
#[test]
fn element_test_does_not_match_when_element_has_namespace_but_without_prefix() {
let package = Package::new();
let mut setup = Setup::new(&package);
let (_, context) = setup.context_for_ns_element("prefix", "uri", "name");
let result = run_element(&context, None, "name");
assert_eq!(ordered_nodes![], result);
}
}