use std::convert::TryFrom;
use std::fmt;
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
use url::Url;
use crate::item::{Item, Node, NodeType, Sequence, SequenceTrait};
use crate::parser::combinators::whitespace::xpwhitespace;
use crate::parser::xpath::literals::literal;
use crate::parser::xpath::nodetests::{nodetest, qualname_test};
use crate::parser::xpath::predicates::predicate_list;
use crate::parser::xpath::variables::variable_reference;
use crate::transform::context::{Context, ContextBuilder, StaticContext};
use crate::transform::{Axis, KindTest, NameTest, NodeTest, Transform, WildcardOrName};
use crate::value::Value;
use crate::xdmerror::{Error, ErrorKind};
use crate::parser::combinators::alt::{alt2, alt4, alt6};
use crate::parser::combinators::list::{separated_list0, separated_list1};
use crate::parser::combinators::many::many0;
use crate::parser::combinators::map::map;
use crate::parser::combinators::opt::opt;
use crate::parser::combinators::pair::pair;
use crate::parser::combinators::tag::tag;
use crate::parser::combinators::tuple::{tuple2, tuple3};
use crate::parser::{ParseError, ParseInput, ParserState};
#[derive(Clone)]
pub enum Pattern<N: Node> {
Predicate(Transform<N>),
Selection(Path),
Error(Error),
}
impl<N: Node> Pattern<N> {
pub fn is_err(&self) -> bool {
if let Pattern::Selection(s) = self {
s.is_err()
} else {
matches!(self, Pattern::Error(_))
}
}
pub fn get_err(&self) -> Option<Error> {
if let Pattern::Selection(s) = self {
s.get_err()
} else if let Pattern::Error(e) = self {
Some(e.clone())
} else {
None
}
}
pub fn matches<
F: FnMut(&str) -> Result<(), Error>,
G: FnMut(&str) -> Result<N, Error>,
H: FnMut(&Url) -> Result<String, Error>,
>(
&self,
ctxt: &Context<N>,
stctxt: &mut StaticContext<N, F, G, H>,
i: &Item<N>,
) -> bool {
match self {
Pattern::Predicate(t) => ContextBuilder::from(ctxt)
.context(vec![i.clone()])
.build()
.dispatch(stctxt, t)
.unwrap_or(vec![Item::Value(Rc::new(Value::from(false)))])
.to_bool(),
Pattern::Selection(p) => path_match(p, i),
_ => false, }
}
pub fn terminal_node_test(&self) -> (Axis, Axis, NodeTest) {
if let Pattern::Selection(sel) = self {
branch_terminal_node_test(sel)
} else {
(
Axis::SelfDocument,
Axis::SelfDocument,
NodeTest::Kind(KindTest::Document),
)
}
}
}
fn branch_terminal_node_test(b: &Branch) -> (Axis, Axis, NodeTest) {
match b {
Branch::SingleStep(t) => (t.terminal, t.non_terminal, t.nt.clone()),
Branch::RelPath(r) => branch_terminal_node_test(&r[0]),
Branch::Union(u) => branch_terminal_node_test(&u[0]), Branch::Error(_) => (
Axis::SelfDocument,
Axis::SelfDocument,
NodeTest::Kind(KindTest::Document),
),
}
}
fn path_match<N: Node>(p: &Path, i: &Item<N>) -> bool {
!branch_match(p, vec![i.clone()]).is_empty()
}
fn branch_match<N: Node>(p: &Path, s: Sequence<N>) -> Sequence<N> {
match p {
Branch::SingleStep(t) => s
.iter()
.filter(|i| is_match(&t.terminal, &t.nt, i))
.flat_map(|i| find_seq(&t.non_terminal, i))
.collect(),
Branch::RelPath(r) => {
r.iter().fold(s, |ctxt, b| {
let new_ctxt = ctxt
.iter()
.cloned()
.flat_map(|i| branch_match(b, vec![i]))
.collect();
new_ctxt
})
}
Branch::Union(u) => {
u.iter()
.flat_map(|b| {
s.iter()
.cloned()
.flat_map(|i| branch_match(b, vec![i]))
.collect::<Sequence<N>>()
})
.collect()
}
Branch::Error(_) => vec![],
}
}
fn find_seq<N: Node>(a: &Axis, i: &Item<N>) -> Sequence<N> {
match a {
Axis::SelfDocument => match i {
Item::Node(n) => {
if n.node_type() == NodeType::Document {
vec![i.clone()]
} else {
vec![]
}
}
_ => vec![],
},
Axis::SelfAxis => vec![i.clone()],
Axis::Parent => match i {
Item::Node(n) => n.parent().map_or(vec![], |p| vec![Item::Node(p)]),
_ => vec![],
},
_ => vec![], }
}
fn is_match<N: Node>(a: &Axis, nt: &NodeTest, i: &Item<N>) -> bool {
match a {
Axis::SelfDocument => {
match i {
Item::Node(n) => {
if n.node_type() == NodeType::Document {
nt.matches(i)
} else {
false
}
}
_ => false,
}
}
Axis::SelfAxis => {
nt.matches(i)
}
Axis::Parent => {
match i {
Item::Node(n) => n.parent().map_or(false, |p| nt.matches(&Item::Node(p))),
_ => false,
}
}
_ => false, }
}
impl<N: Node> Debug for Pattern<N> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Pattern::Predicate(t) => write!(f, "Pattern::Predicate t==\"{:?}\"", t),
Pattern::Selection(p) => write!(f, "Pattern::Selection path=\"{:?}\"", p),
Pattern::Error(e) => write!(f, "Pattern::Error error=\"{:?}\"", e),
}
}
}
pub type Path = Branch;
#[derive(Clone, Debug)]
pub enum Branch {
SingleStep(Step),
RelPath(Vec<Branch>),
Union(Vec<Branch>),
Error(Error),
}
impl Branch {
pub fn terminal_node_test(&self) -> (Axis, Axis, NodeTest) {
branch_terminal_node_test(self)
}
pub fn is_err(&self) -> bool {
match self {
Branch::Error(_) => true,
Branch::SingleStep(_) => false,
Branch::RelPath(r) => r.iter().any(|f| f.is_err()),
Branch::Union(u) => u.iter().any(|f| f.is_err()),
}
}
pub fn get_err(&self) -> Option<Error> {
match self {
Branch::Error(e) => Some(e.clone()),
Branch::SingleStep(_) => None,
Branch::RelPath(r) => r.iter().fold(None, |v, f| v.or_else(|| f.get_err())),
Branch::Union(u) => u.iter().fold(None, |v, f| v.or_else(|| f.get_err())),
}
}
}
#[derive(Clone, Debug)]
pub struct Step {
terminal: Axis,
non_terminal: Axis,
nt: NodeTest,
}
impl Step {
pub fn new(terminal: Axis, non_terminal: Axis, nt: NodeTest) -> Self {
Step {
terminal,
non_terminal,
nt,
}
}
pub fn get_ref(&self) -> (&Axis, &Axis, &NodeTest) {
(&self.terminal, &self.non_terminal, &self.nt)
}
}
impl<N: Node> TryFrom<&str> for Pattern<N> {
type Error = Error;
fn try_from(e: &str) -> Result<Self, <crate::pattern::Pattern<N> as TryFrom<&str>>::Error> {
if e.is_empty() {
Err(Error::new(
ErrorKind::TypeError,
String::from("empty string is not allowed as an XPath pattern"),
))
} else {
let state = ParserState::new(None, None, None);
match pattern::<N>((e, state)) {
Ok(((rem, _), f)) => {
if rem.is_empty() {
Ok(f)
} else {
Err(Error::new(
ErrorKind::Unknown,
format!("extra characters found: \"{:?}\"", rem),
))
}
}
Err(err) => Err(Error::new(ErrorKind::Unknown, format!("{:?}", err))),
}
}
}
}
impl<N: Node> TryFrom<(&str, N)> for Pattern<N> {
type Error = Error;
fn try_from(
e: (&str, N),
) -> Result<Self, <crate::pattern::Pattern<N> as TryFrom<&str>>::Error> {
if e.0.is_empty() {
Err(Error::new(
ErrorKind::TypeError,
String::from("empty string is not allowed as an XPath pattern"),
))
} else {
let state = ParserState::new(None, Some(e.1), None);
match pattern::<N>((e.0, state)) {
Ok(((rem, _), f)) => {
if rem.is_empty() {
Ok(f)
} else {
Err(Error::new(
ErrorKind::Unknown,
format!("extra characters found: \"{:?}\"", rem),
))
}
}
Err(err) => Err(Error::new(ErrorKind::Unknown, format!("{:?}", err))),
}
}
}
}
impl<'a, N: Node> TryFrom<String> for Pattern<N> {
type Error = Error;
fn try_from(e: String) -> Result<Self, <Pattern<N> as TryFrom<&'a str>>::Error> {
Pattern::try_from(e.as_str())
}
}
impl<'a, N: Node> TryFrom<(String, N)> for Pattern<N> {
type Error = Error;
fn try_from(e: (String, N)) -> Result<Self, <Pattern<N> as TryFrom<(&'a str, N)>>::Error> {
Pattern::try_from((e.0.as_str(), e.1))
}
}
fn pattern<N: Node>(input: ParseInput<N>) -> Result<(ParseInput<N>, Pattern<N>), ParseError> {
alt2(predicate_pattern::<N>(), union_expr_pattern())(input)
}
fn predicate_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Pattern<N>), ParseError> + 'a> {
Box::new(map(
pair(
map(tuple3(xpwhitespace(), tag("."), xpwhitespace()), |_| ()),
predicate_list::<N>(),
),
|(_, p)| Pattern::Predicate(p),
))
}
fn union_expr_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Pattern<N>), ParseError> + 'a> {
Box::new(map(
separated_list1(
map(
tuple3(xpwhitespace(), alt2(tag("union"), tag("|")), xpwhitespace()),
|_| (),
),
intersect_except_expr_pattern::<N>(),
),
|v| {
Pattern::Selection(Branch::Union(v))
},
))
}
fn union_expr_wrapper<N: Node>(
b: bool,
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Pattern<N>), ParseError>> {
Box::new(move |input| {
if b {
union_expr_pattern::<N>()(input)
} else {
noop()(input)
}
})
}
fn noop<N: Node>() -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Pattern<N>), ParseError>>
{
Box::new(move |_| Err(ParseError::Combinator))
}
fn intersect_except_expr_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(
separated_list1(
map(
tuple3(
xpwhitespace(),
alt2(tag("intersect"), tag("except")),
xpwhitespace(),
),
|_| (),
),
path_expr_pattern::<N>(),
),
|mut v| {
if v.len() == 1 {
v.pop().unwrap()
} else {
Branch::Error(Error::new(
ErrorKind::NotImplemented,
String::from("intersect or except in a pattern has not been implemented"),
))
}
},
))
}
fn path_expr_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(alt4(
rooted_path_pattern::<N>(),
absolutedescendant_expr_pattern(),
absolutepath_expr_pattern(),
relativepath_expr_pattern::<N>(),
))
}
fn rooted_path_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(
tuple3(
alt2(variable_reference_pattern::<N>(), function_call_pattern()),
predicate_list::<N>(),
alt2(
absolutedescendant_expr_pattern::<N>(),
absolutepath_expr_pattern(),
),
),
|(_a, _b, _c)| {
Branch::Error(Error::new(
ErrorKind::NotImplemented,
String::from("rooted path in a pattern has not been implemented"),
))
},
))
}
fn variable_reference_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(variable_reference::<N>(), |_| {
Branch::Error(Error::new(
ErrorKind::NotImplemented,
"variable reference not yet supported",
))
}))
}
fn absolutedescendant_expr_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(
pair(tag("//"), relativepath_expr_pattern::<N>()),
|(_, _r)| {
Branch::Error(Error::new(
ErrorKind::NotImplemented,
String::from("absolute descendant path in a pattern has not been implemented"),
))
},
))
}
fn absolutepath_expr_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(
pair(
map(tag("/"), |_| "/"),
opt(relativepath_expr_pattern::<N>()),
),
|(d, r)| match (d, r.clone()) {
("/", None) => {
Branch::SingleStep(Step::new(
Axis::SelfDocument,
Axis::SelfDocument,
NodeTest::Kind(KindTest::Document),
))
}
("/", Some(Branch::SingleStep(s))) => Branch::RelPath(vec![
Branch::SingleStep(s),
Branch::SingleStep(Step::new(
Axis::SelfDocument,
Axis::SelfDocument,
NodeTest::Kind(KindTest::Document),
)),
]),
("/", Some(Branch::RelPath(mut a))) => {
a.push(Branch::SingleStep(Step::new(
Axis::SelfDocument,
Axis::SelfDocument,
NodeTest::Kind(KindTest::Document),
)));
Branch::RelPath(a)
}
("/", Some(Branch::Union(u))) => Branch::RelPath(vec![
Branch::Union(u),
Branch::SingleStep(Step::new(
Axis::SelfDocument,
Axis::SelfDocument,
NodeTest::Kind(KindTest::Document),
)),
]),
_ => Branch::Error(Error::new(
ErrorKind::Unknown,
String::from("unable to parse pattern"),
)),
},
))
}
fn relativepath_expr_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(
pair(
step_expr_pattern::<N>(),
many0(tuple2(
alt2(
map(tuple3(xpwhitespace(), tag("//"), xpwhitespace()), |_| "//"),
map(tuple3(xpwhitespace(), tag("/"), xpwhitespace()), |_| "/"),
),
step_expr_pattern::<N>(),
)),
),
|(a, b)| {
if b.is_empty() {
a
} else {
let mut result = vec![a];
for (_c, d) in b {
result.insert(0, d);
}
Branch::RelPath(result)
}
},
))
}
fn step_expr_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(alt2(postfix_expr_pattern::<N>(), axis_step_pattern::<N>()))
}
fn postfix_expr_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(
tuple2(paren_expr_pattern(), predicate_list::<N>()),
|(p, _)| p,
))
}
fn paren_expr_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(
tuple3(
tuple3(xpwhitespace(), tag("("), xpwhitespace()),
union_expr_wrapper(true),
tuple3(xpwhitespace(), tag(")"), xpwhitespace()),
),
|(_, u, _)| {
if let Pattern::Selection(sel) = u {
sel
} else {
Branch::Error(Error::new(
ErrorKind::TypeError,
"expression must be a selection",
))
}
},
))
}
fn axis_step_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(
tuple2(forward_step_pattern(), predicate_list::<N>()),
|(f, _p)| f, ))
}
fn forward_step_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(
alt2(
tuple2(forward_axis_pattern(), nodetest()),
abbrev_forward_step(),
),
|((a, c), nt)| Branch::SingleStep(Step::new(a, c, nt)),
))
}
fn abbrev_forward_step<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, ((Axis, Axis), NodeTest)), ParseError> + 'a>
{
Box::new(map(tuple2(opt(tag("@")), nodetest()), |(a, nt)| {
a.map_or_else(
|| {
((Axis::SelfAxis, Axis::Parent), nt.clone())
},
|_| {
((Axis::SelfAttribute, Axis::Parent), nt.clone())
},
)
}))
}
fn forward_axis_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, (Axis, Axis)), ParseError> + 'a> {
Box::new(map(
tuple2(
alt6(
map(tag("child"), |_| (Axis::SelfAxis, Axis::Parent)),
map(tag("descendant"), |_| (Axis::SelfAxis, Axis::Ancestor)),
map(tag("attribute"), |_| (Axis::SelfAttribute, Axis::Parent)),
map(tag("self"), |_| (Axis::SelfAxis, Axis::SelfAxis)),
map(tag("descendant-or-self"), |_| {
(Axis::SelfAxis, Axis::Ancestor)
}),
map(tag("namespace"), |_| (Axis::SelfNamespace, Axis::Parent)),
),
tag("::"),
),
|(a, _)| a,
))
}
fn function_call_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(
tuple2(outer_function_name(), argument_list_pattern::<N>()),
|(_n, _a)| {
Branch::Error(Error::new(
ErrorKind::NotImplemented,
String::from("function call in a pattern has not been implemented"),
))
},
))
}
fn argument_list_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(
tuple3(
map(tuple3(xpwhitespace(), tag("("), xpwhitespace()), |_| ()),
separated_list0(
map(tuple3(xpwhitespace(), tag(","), xpwhitespace()), |_| ()),
argument_pattern::<N>(),
),
map(tuple3(xpwhitespace(), tag(")"), xpwhitespace()), |_| ()),
),
|(_, _a, _)| {
Branch::Error(Error::new(
ErrorKind::NotImplemented,
String::from("argument list in a pattern has not been implemented"),
))
},
))
}
fn argument_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(alt2(
variable_reference_pattern::<N>(),
literal_pattern::<N>(),
))
}
fn literal_pattern<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, Path), ParseError> + 'a> {
Box::new(map(literal::<N>(), |_| {
Branch::Error(Error::new(ErrorKind::NotImplemented, "not yet implemented"))
}))
}
fn outer_function_name<'a, N: Node + 'a>(
) -> Box<dyn Fn(ParseInput<N>) -> Result<(ParseInput<N>, NodeTest), ParseError> + 'a> {
Box::new(alt6(
map(tag("doc"), |_| {
NodeTest::Name(NameTest {
ns: None,
prefix: None,
name: Some(WildcardOrName::Name(Rc::new(Value::from("doc")))),
})
}),
map(tag("id"), |_| {
NodeTest::Name(NameTest {
ns: None,
prefix: None,
name: Some(WildcardOrName::Name(Rc::new(Value::from("id")))),
})
}),
map(tag("element-with-id"), |_| {
NodeTest::Name(NameTest {
ns: None,
prefix: None,
name: Some(WildcardOrName::Name(Rc::new(Value::from(
"element-with-id",
)))),
})
}),
map(tag("key"), |_| {
NodeTest::Name(NameTest {
ns: None,
prefix: None,
name: Some(WildcardOrName::Name(Rc::new(Value::from("key")))),
})
}),
map(tag("root"), |_| {
NodeTest::Name(NameTest {
ns: None,
prefix: None,
name: Some(WildcardOrName::Name(Rc::new(Value::from("root")))),
})
}),
map(qualname_test(), |q| q),
))
}