xrust/transform/
mod.rs

1/*! The transformation engine.
2
3A [Transform] performs processing, control flow, calculations, navigation, and construction to produce a [Sequence]. It starts with an initial context, the most important component of which is the current [Item]; this is often a [Node] that is the source document.
4
5All functions in the [Transform] operate via the [Node] trait. This makes the transformation engine independent of the syntax of the source, stylesheet, and result documents. Any [Node]s created by the transformation use the context's result document object.
6
7The following transformation implements the expression "1 + 1". The result is (hopefully) "2".
8
9```rust
10# use std::rc::Rc;
11# use xrust::xdmerror::{Error, ErrorKind};
12# use xrust::trees::smite::{RNode, Node as SmiteNode};
13use xrust::value::Value;
14use xrust::item::{Item, Node, Sequence, SequenceTrait};
15use xrust::transform::{Transform, ArithmeticOperand, ArithmeticOperator};
16use xrust::transform::context::{Context, StaticContext, StaticContextBuilder};
17
18let xform = Transform::Arithmetic(vec![
19        ArithmeticOperand::new(
20            ArithmeticOperator::Noop,
21            Transform::Literal(Item::<RNode>::Value(Rc::new(Value::from(1))))
22        ),
23        ArithmeticOperand::new(
24            ArithmeticOperator::Add,
25            Transform::Literal(Item::<RNode>::Value(Rc::new(Value::from(1))))
26        )
27    ]);
28let mut static_context = StaticContextBuilder::new()
29    .message(|_| Ok(()))
30    .fetcher(|_| Ok(String::new()))
31    .parser(|_| Err(Error::new(ErrorKind::NotImplemented, "not implemented")))
32    .build();
33let sequence = Context::new()
34    .dispatch(&mut static_context, &xform)
35    .expect("evaluation failed");
36assert_eq!(sequence.to_string(), "2")
37```
38*/
39
40pub(crate) mod booleans;
41pub mod callable;
42pub(crate) mod construct;
43pub mod context;
44pub(crate) mod controlflow;
45pub(crate) mod datetime;
46pub(crate) mod functions;
47pub(crate) mod grouping;
48mod keys;
49pub(crate) mod logic;
50pub(crate) mod misc;
51pub(crate) mod navigate;
52pub mod numbers;
53pub(crate) mod strings;
54pub mod template;
55pub(crate) mod variables;
56
57#[allow(unused_imports)]
58use crate::item::Sequence;
59use crate::item::{Item, Node, NodeType, SequenceTrait};
60use crate::namespace::NamespaceMap;
61use crate::output::OutputSpec;
62use crate::pattern::Pattern;
63use crate::qname::QualifiedName;
64use crate::transform::callable::ActualParameters;
65use crate::transform::context::{Context, ContextBuilder, StaticContext};
66use crate::transform::numbers::Numbering;
67use crate::value::Operator;
68#[allow(unused_imports)]
69use crate::value::Value;
70use crate::xdmerror::{Error, ErrorKind};
71use std::convert::TryFrom;
72use std::fmt;
73use std::fmt::{Debug, Formatter};
74use std::rc::Rc;
75use url::Url;
76
77/// Specifies how a [Sequence] is constructed.
78#[derive(Clone)]
79pub enum Transform<N: Node> {
80    /// Produces the root node of the tree containing the context item.
81    Root,
82    /// Produces a copy of the context item.
83    ContextItem,
84    /// Produces a copy of the current item (see XSLT 20.4.1).
85    CurrentItem,
86
87    /// A path in a tree. Each element of the outer vector is a step in the path.
88    /// The result of each step becomes the new context for the next step.
89    Compose(Vec<Transform<N>>),
90    /// A step in a path.
91    Step(NodeMatch),
92    ///
93    /// Filters the selected items.
94    /// Each item in the context is evaluated against the predicate.
95    /// If the resulting sequence has an effective boolean value of 'true' then it is kept,
96    /// otherwise it is discarded.
97    Filter(Box<Transform<N>>),
98
99    /// An empty sequence
100    Empty,
101    /// A literal, atomic value.
102    Literal(Item<N>),
103    /// A literal element. Consists of the element name and content.
104    LiteralElement(Rc<QualifiedName>, Box<Transform<N>>),
105    /// A constructed element. Consists of the name and content.
106    Element(Box<Transform<N>>, Box<Transform<N>>),
107    /// A literal text node. Consists of the value of the node. Second argument gives an output hint.
108    LiteralText(Box<Transform<N>>, OutputSpec),
109    /// A literal attribute. Consists of the attribute name and value.
110    /// NB. The value may be produced by an Attribute Value Template, so must be dynamic.
111    LiteralAttribute(Rc<QualifiedName>, Box<Transform<N>>),
112    /// A literal comment. Consists of the value.
113    LiteralComment(Box<Transform<N>>),
114    /// A literal processing instruction. Consists of the name and value.
115    LiteralProcessingInstruction(Box<Transform<N>>, Box<Transform<N>>),
116    /// Produce a [Sequence]. Each element in the vector becomes one, or more, item in the sequence.
117    SequenceItems(Vec<Transform<N>>),
118
119    /// A shallow copy of an item. Consists of the selector of the item to be copied,
120    /// and the content of the target.
121    Copy(Box<Transform<N>>, Box<Transform<N>>),
122    /// A deep copy of an item. That is, it copies an item including its descendants.
123    DeepCopy(Box<Transform<N>>),
124
125    /// Logical OR. Each element of the outer vector is an operand.
126    Or(Vec<Transform<N>>),
127    /// Logical AND. Each element of the outer vector is an operand.
128    And(Vec<Transform<N>>),
129
130    /// XPath general comparison.
131    /// Each item in the first sequence is compared against all items in the second sequence.
132    GeneralComparison(Operator, Box<Transform<N>>, Box<Transform<N>>),
133    /// XPath value comparison.
134    /// The first singleton sequence is compared against the second singleton sequence.
135    ValueComparison(Operator, Box<Transform<N>>, Box<Transform<N>>),
136
137    /// Concatenate string values
138    Concat(Vec<Transform<N>>),
139    /// Produce a range of integer values.
140    /// Consists of the start value and end value.
141    Range(Box<Transform<N>>, Box<Transform<N>>),
142    /// Perform arithmetic operations
143    Arithmetic(Vec<ArithmeticOperand<N>>),
144
145    /// A repeating transformation. Consists of variable declarations and the loop body.
146    Loop(Vec<(String, Transform<N>)>, Box<Transform<N>>),
147    /// A branching transformation. Consists of (test, body) clauses and an otherwise clause.
148    Switch(Vec<(Transform<N>, Transform<N>)>, Box<Transform<N>>),
149
150    /// Evaluate a transformation for each selected item, with possible grouping and sorting.
151    ForEach(
152        Option<Grouping<N>>,
153        Box<Transform<N>>,
154        Box<Transform<N>>,
155        Vec<(Order, Transform<N>)>,
156    ),
157    /// Find a template that matches an item and evaluate its body with the item as the context.
158    /// Consists of the selector for items to be matched, the mode, and sort keys.
159    ApplyTemplates(
160        Box<Transform<N>>,
161        Option<Rc<QualifiedName>>,
162        Vec<(Order, Transform<N>)>,
163    ),
164    /// Find templates at the next import level and evaluate its body.
165    ApplyImports,
166    NextMatch,
167
168    /// Set union
169    Union(Vec<Transform<N>>),
170
171    /// Evaluate a named template or function, with arguments.
172    /// Consists of the body of the template/function, the actual arguments (variable declarations), and in-scope namespace declarations.
173    Call(Box<Transform<N>>, Vec<Transform<N>>, Rc<NamespaceMap>),
174
175    /// Declare a variable in the current context.
176    /// Consists of the variable name, its value, a transformation to perform with the variable in scope, and in-scope namespace declarations.
177    VariableDeclaration(
178        String,
179        Box<Transform<N>>,
180        Box<Transform<N>>,
181        Rc<NamespaceMap>,
182    ),
183    /// Reference a variable.
184    /// The result is the value stored for that variable in the current context and current scope.
185    VariableReference(String, Rc<NamespaceMap>),
186
187    /// Set the value of an attribute. The context item must be an element-type node.
188    /// Consists of the name of the attribute and its value. The [Sequence] produced will be cast to a [Value].
189    SetAttribute(Rc<QualifiedName>, Box<Transform<N>>),
190
191    /// XPath functions
192    Position,
193    Last,
194    Count(Box<Transform<N>>),
195    LocalName(Option<Box<Transform<N>>>),
196    Name(Option<Box<Transform<N>>>),
197    String(Box<Transform<N>>),
198    StartsWith(Box<Transform<N>>, Box<Transform<N>>),
199    EndsWith(Box<Transform<N>>, Box<Transform<N>>),
200    Contains(Box<Transform<N>>, Box<Transform<N>>),
201    Substring(
202        Box<Transform<N>>,
203        Box<Transform<N>>,
204        Option<Box<Transform<N>>>,
205    ),
206    SubstringBefore(Box<Transform<N>>, Box<Transform<N>>),
207    SubstringAfter(Box<Transform<N>>, Box<Transform<N>>),
208    NormalizeSpace(Option<Box<Transform<N>>>),
209    Translate(Box<Transform<N>>, Box<Transform<N>>, Box<Transform<N>>),
210    GenerateId(Option<Box<Transform<N>>>),
211    Boolean(Box<Transform<N>>),
212    Not(Box<Transform<N>>),
213    True,
214    False,
215    Number(Box<Transform<N>>),
216    Sum(Box<Transform<N>>),
217    Avg(Box<Transform<N>>),
218    Min(Box<Transform<N>>),
219    Max(Box<Transform<N>>),
220    Floor(Box<Transform<N>>),
221    Ceiling(Box<Transform<N>>),
222    Round(Box<Transform<N>>, Option<Box<Transform<N>>>),
223    CurrentDateTime,
224    CurrentDate,
225    CurrentTime,
226    FormatDateTime(
227        Box<Transform<N>>,
228        Box<Transform<N>>,
229        Option<Box<Transform<N>>>,
230        Option<Box<Transform<N>>>,
231        Option<Box<Transform<N>>>,
232    ),
233    FormatDate(
234        Box<Transform<N>>,
235        Box<Transform<N>>,
236        Option<Box<Transform<N>>>,
237        Option<Box<Transform<N>>>,
238        Option<Box<Transform<N>>>,
239    ),
240    FormatTime(
241        Box<Transform<N>>,
242        Box<Transform<N>>,
243        Option<Box<Transform<N>>>,
244        Option<Box<Transform<N>>>,
245        Option<Box<Transform<N>>>,
246    ),
247    FormatNumber(
248        Box<Transform<N>>,
249        Box<Transform<N>>,
250        Option<Box<Transform<N>>>,
251    ),
252    /// Convert a number to a string.
253    /// This is one half of the functionality of xsl:number, as well as format-integer().
254    /// See XSLT 12.4.
255    /// First argument is the integer to be formatted.
256    /// Second argument is the format specification.
257    FormatInteger(Box<Transform<N>>, Box<Transform<N>>),
258    /// Generate a sequence of integers. This is one half of the functionality of xsl:number.
259    /// First argument is the start-at specification.
260    /// Second argument is the select expression.
261    /// Third argument is the level.
262    /// Fourth argument is the count pattern.
263    /// Fifth argument is the from pattern.
264    GenerateIntegers(Box<Transform<N>>, Box<Transform<N>>, Box<Numbering<N>>),
265    CurrentGroup,
266    CurrentGroupingKey,
267    /// Look up a key. The first argument is the key name, the second argument is the key value,
268    /// the third argument is the top of the tree for the resulting nodes,
269    /// the fourth argument is the in-scope namespaces.
270    Key(
271        Box<Transform<N>>,
272        Box<Transform<N>>,
273        Option<Box<Transform<N>>>,
274        Rc<NamespaceMap>,
275    ),
276    /// Get information about the processor
277    SystemProperty(Box<Transform<N>>, Rc<NamespaceMap>),
278    AvailableSystemProperties,
279    /// Read an external document
280    Document(Box<Transform<N>>, Option<Box<Transform<N>>>),
281
282    /// Invoke a callable component. Consists of a name, an actual argument list, and in-scope namespace declarations.
283    Invoke(Rc<QualifiedName>, ActualParameters<N>, Rc<NamespaceMap>),
284
285    /// Emit a message. Consists of a select expression, a terminate attribute, an error-code, and a body.
286    Message(
287        Box<Transform<N>>,
288        Option<Box<Transform<N>>>,
289        Box<Transform<N>>,
290        Box<Transform<N>>,
291    ),
292
293    /// For things that are not yet implemented, such as:
294    /// Union, IntersectExcept, InstanceOf, Treat, Castable, Cast, Arrow, Unary, SimpleMap, Is, Before, After.
295    NotImplemented(String),
296
297    /// Error condition.
298    Error(ErrorKind, String),
299}
300
301impl<N: Node> Debug for Transform<N> {
302    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
303        match self {
304            Transform::Root => write!(f, "root node"),
305            Transform::ContextItem => write!(f, "context item"),
306            Transform::CurrentItem => write!(f, "current item"),
307            Transform::SequenceItems(v) => write!(f, "Sequence of {} items", v.len()),
308            Transform::Compose(v) => {
309                write!(f, "Compose {} steps [", v.len()).expect("unable to format step");
310                v.iter().for_each(|s| {
311                    s.fmt(f).expect("unable to format step");
312                    write!(f, "; ").expect("unable to format step")
313                });
314                write!(f, "]")
315            }
316            Transform::Step(nm) => write!(f, "Step matching {}", nm),
317            Transform::Filter(_) => write!(f, "Filter"),
318            Transform::Empty => write!(f, "Empty"),
319            Transform::Literal(_) => write!(f, "literal value"),
320            Transform::LiteralElement(qn, _) => write!(f, "literal element named \"{}\"", qn),
321            Transform::Element(_, _) => write!(f, "constructed element"),
322            Transform::LiteralText(_, b) => write!(f, "literal text (disable escaping {:?})", b),
323            Transform::LiteralAttribute(qn, _) => write!(f, "literal attribute named \"{}\"", qn),
324            Transform::LiteralComment(_) => write!(f, "literal comment"),
325            Transform::LiteralProcessingInstruction(_, _) => {
326                write!(f, "literal processing-instruction")
327            }
328            Transform::Copy(_, _) => write!(f, "shallow copy"),
329            Transform::DeepCopy(_) => write!(f, "deep copy"),
330            Transform::GeneralComparison(o, v, u) => {
331                write!(f, "general comparison {} of {:?} and {:?}", o, v, u)
332            }
333            Transform::ValueComparison(o, v, u) => {
334                write!(f, "value comparison {} of {:?} and {:?}", o, v, u)
335            }
336            Transform::Concat(o) => write!(f, "Concatenate {} operands", o.len()),
337            Transform::Range(_, _) => write!(f, "range"),
338            Transform::Arithmetic(o) => write!(f, "Arithmetic {} operands", o.len()),
339            Transform::And(o) => write!(f, "AND {} operands", o.len()),
340            Transform::Or(o) => write!(f, "OR {} operands", o.len()),
341            Transform::Loop(_, _) => write!(f, "loop"),
342            Transform::Switch(c, _) => write!(f, "switch {} clauses", c.len()),
343            Transform::ForEach(_g, _, _, o) => write!(f, "for-each ({} sort keys)", o.len()),
344            Transform::Union(v) => {
345                write!(f, "union of {} operands", v.len()).ok();
346                v.iter().for_each(|o| {
347                    write!(f, "\noperand: {:?}", o).ok();
348                });
349                Ok(())
350            }
351            Transform::ApplyTemplates(_, m, o) => {
352                write!(f, "Apply templates (mode {:?}, {} sort keys)", m, o.len())
353            }
354            Transform::Call(_, a, _) => write!(f, "Call transform with {} arguments", a.len()),
355            Transform::ApplyImports => write!(f, "Apply imports"),
356            Transform::NextMatch => write!(f, "next-match"),
357            Transform::VariableDeclaration(n, _, _, _) => write!(f, "declare variable \"{}\"", n),
358            Transform::VariableReference(n, _) => write!(f, "reference variable \"{}\"", n),
359            Transform::SetAttribute(n, _) => write!(f, "set attribute named \"{}\"", n),
360            Transform::Position => write!(f, "position"),
361            Transform::Last => write!(f, "last"),
362            Transform::Count(_s) => write!(f, "count()"),
363            Transform::Name(_n) => write!(f, "name()"),
364            Transform::LocalName(_n) => write!(f, "local-name()"),
365            Transform::String(s) => write!(f, "string({:?})", s),
366            Transform::StartsWith(s, t) => write!(f, "starts-with({:?}, {:?})", s, t),
367            Transform::EndsWith(s, t) => write!(f, "ends-with({:?}, {:?})", s, t),
368            Transform::Contains(s, t) => write!(f, "contains({:?}, {:?})", s, t),
369            Transform::Substring(s, t, _l) => write!(f, "substring({:?}, {:?}, ...)", s, t),
370            Transform::SubstringBefore(s, t) => write!(f, "substring-before({:?}, {:?})", s, t),
371            Transform::SubstringAfter(s, t) => write!(f, "substring-after({:?}, {:?})", s, t),
372            Transform::NormalizeSpace(_s) => write!(f, "normalize-space()"),
373            Transform::Translate(s, t, u) => write!(f, "translate({:?}, {:?}, {:?})", s, t, u),
374            Transform::GenerateId(_) => write!(f, "generate-id()"),
375            Transform::Boolean(b) => write!(f, "boolean({:?})", b),
376            Transform::Not(b) => write!(f, "not({:?})", b),
377            Transform::True => write!(f, "true"),
378            Transform::False => write!(f, "false"),
379            Transform::Number(n) => write!(f, "number({:?})", n),
380            Transform::Sum(n) => write!(f, "sum({:?})", n),
381            Transform::Avg(n) => write!(f, "avg({:?})", n),
382            Transform::Min(n) => write!(f, "min({:?})", n),
383            Transform::Max(n) => write!(f, "max({:?})", n),
384            Transform::Floor(n) => write!(f, "floor({:?})", n),
385            Transform::Ceiling(n) => write!(f, "ceiling({:?})", n),
386            Transform::Round(n, _p) => write!(f, "round({:?},...)", n),
387            Transform::CurrentDateTime => write!(f, "current-date-time"),
388            Transform::CurrentDate => write!(f, "current-date"),
389            Transform::CurrentTime => write!(f, "current-time"),
390            Transform::FormatDateTime(p, q, _, _, _) => {
391                write!(f, "format-date-time({:?}, {:?}, ...)", p, q)
392            }
393            Transform::FormatDate(p, q, _, _, _) => write!(f, "format-date({:?}, {:?}, ...)", p, q),
394            Transform::FormatTime(p, q, _, _, _) => write!(f, "format-time({:?}, {:?}, ...)", p, q),
395            Transform::FormatNumber(v, p, _) => write!(f, "format-number({:?}, {:?})", v, p),
396            Transform::FormatInteger(i, s) => write!(f, "format-integer({:?}, {:?})", i, s),
397            Transform::GenerateIntegers(_start_at, _select, _n) => write!(f, "generate-integers"),
398            Transform::CurrentGroup => write!(f, "current-group"),
399            Transform::CurrentGroupingKey => write!(f, "current-grouping-key"),
400            Transform::Key(s, _, _, _) => write!(f, "key({:?}, ...)", s),
401            Transform::SystemProperty(p, _) => write!(f, "system-properties({:?})", p),
402            Transform::AvailableSystemProperties => write!(f, "available-system-properties"),
403            Transform::Document(uris, _) => write!(f, "document({:?})", uris),
404            Transform::Invoke(qn, _a, _) => write!(f, "invoke \"{}\"", qn),
405            Transform::Message(_, _, _, _) => write!(f, "message"),
406            Transform::NotImplemented(s) => write!(f, "Not implemented: \"{}\"", s),
407            Transform::Error(k, s) => write!(f, "Error: {} \"{}\"", k, s),
408        }
409    }
410}
411
412/// A convenience function to create a namespace mapping from a [Node].
413pub fn in_scope_namespaces<N: Node>(n: Option<N>) -> Rc<NamespaceMap> {
414    if let Some(nn) = n {
415        Rc::new(nn.namespace_iter().fold(NamespaceMap::new(), |mut hm, ns| {
416            hm.insert(Some(ns.name().localname()), ns.value());
417            hm
418        }))
419    } else {
420        Rc::new(NamespaceMap::new())
421    }
422}
423
424/// The sort order
425#[derive(Clone, PartialEq, Debug)]
426pub enum Order {
427    Ascending,
428    Descending,
429}
430
431/// Performing sorting of a [Sequence] using the given sort keys.
432pub(crate) fn do_sort<
433    N: Node,
434    F: FnMut(&str) -> Result<(), Error>,
435    G: FnMut(&str) -> Result<N, Error>,
436    H: FnMut(&Url) -> Result<String, Error>,
437>(
438    seq: &mut Sequence<N>,
439    o: &Vec<(Order, Transform<N>)>,
440    ctxt: &Context<N>,
441    stctxt: &mut StaticContext<N, F, G, H>,
442) -> Result<(), Error> {
443    // Optionally sort the select sequence
444    // TODO: multiple sort keys
445    if !o.is_empty() {
446        seq.sort_by_cached_key(|k| {
447            // TODO: Don't panic
448            let key_seq = ContextBuilder::from(ctxt)
449                .context(vec![k.clone()])
450                .build()
451                .dispatch(stctxt, &o[0].1)
452                .expect("unable to determine key value");
453            // Assume string data type for now
454            // TODO: support number data type
455            // TODO: support all data types
456            key_seq.to_string()
457        });
458        if o[0].0 == Order::Descending {
459            seq.reverse();
460        }
461    }
462    Ok(())
463}
464
465/// Determine how a collection is to be divided into groups.
466/// This value would normally be inside an Option.
467/// A None value for the option means that the collection is not to be grouped.
468#[derive(Clone, Debug)]
469pub enum Grouping<N: Node> {
470    By(Vec<Transform<N>>),
471    StartingWith(Box<Pattern<N>>),
472    EndingWith(Box<Pattern<N>>),
473    Adjacent(Vec<Transform<N>>),
474}
475
476impl<N: Node> Grouping<N> {
477    fn to_string(&self) -> String {
478        match self {
479            Grouping::By(_) => "group-by".to_string(),
480            Grouping::Adjacent(_) => "group-adjacent".to_string(),
481            Grouping::StartingWith(_) => "group-starting-with".to_string(),
482            Grouping::EndingWith(_) => "group-ending-with".to_string(),
483        }
484    }
485}
486
487impl<N: Node> fmt::Display for Grouping<N> {
488    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
489        write!(f, "{}", self.to_string())
490    }
491}
492
493#[derive(Clone, Debug)]
494pub struct NodeMatch {
495    pub axis: Axis,
496    pub nodetest: NodeTest,
497}
498
499impl NodeMatch {
500    pub fn new(axis: Axis, nodetest: NodeTest) -> Self {
501        NodeMatch { axis, nodetest }
502    }
503    pub fn matches_item<N: Node>(&self, i: &Item<N>) -> bool {
504        match i {
505            Item::Node(n) => self.matches(n),
506            _ => false,
507        }
508    }
509    pub fn matches<N: Node>(&self, n: &N) -> bool {
510        match &self.nodetest {
511            NodeTest::Name(t) => {
512                match n.node_type() {
513                    NodeType::Element | NodeType::Attribute => {
514                        // TODO: namespaces
515                        match &t.name {
516                            Some(a) => match a {
517                                WildcardOrName::Wildcard => true,
518                                WildcardOrName::Name(s) => *s == n.name().localname(),
519                            },
520                            None => false,
521                        }
522                    }
523                    _ => false,
524                }
525            }
526            NodeTest::Kind(k) => {
527                match k {
528                    KindTest::Document => matches!(n.node_type(), NodeType::Document),
529                    KindTest::Element => matches!(n.node_type(), NodeType::Element),
530                    KindTest::PI => matches!(n.node_type(), NodeType::ProcessingInstruction),
531                    KindTest::Comment => matches!(n.node_type(), NodeType::Comment),
532                    KindTest::Text => matches!(n.node_type(), NodeType::Text),
533                    //Note: This one is matching not NodeType::Document
534                    KindTest::Any => !matches!(n.node_type(), NodeType::Document),
535                    KindTest::Attribute
536                    | KindTest::SchemaElement
537                    | KindTest::SchemaAttribute
538                    | KindTest::Namespace => false, // TODO: not yet implemented
539                }
540            }
541        }
542    }
543}
544
545impl fmt::Display for NodeMatch {
546    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
547        write!(f, "node match {} {}", self.axis, self.nodetest)
548    }
549}
550
551#[derive(Clone, Debug)]
552pub enum NodeTest {
553    Kind(KindTest),
554    Name(NameTest),
555}
556
557impl NodeTest {
558    pub fn matches<N: Node>(&self, i: &Item<N>) -> bool {
559        match i {
560            Item::Node(_) => match self {
561                NodeTest::Kind(k) => k.matches(i),
562                NodeTest::Name(nm) => nm.matches(i),
563            },
564            _ => false,
565        }
566    }
567}
568
569impl fmt::Display for NodeTest {
570    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
571        match self {
572            NodeTest::Kind(k) => write!(f, "kind test {}", k),
573            NodeTest::Name(nm) => write!(f, "name test {}", nm),
574        }
575    }
576}
577
578impl TryFrom<&str> for NodeTest {
579    type Error = Error;
580
581    fn try_from(s: &str) -> Result<Self, Self::Error> {
582        // Import this from xpath.rs?
583        let tok: Vec<&str> = s.split(':').collect();
584        match tok.len() {
585            1 => {
586                // unprefixed
587                if tok[0] == "*" {
588                    Ok(NodeTest::Name(NameTest {
589                        name: Some(WildcardOrName::Wildcard),
590                        ns: None,
591                        prefix: None,
592                    }))
593                } else {
594                    Ok(NodeTest::Name(NameTest {
595                        name: Some(WildcardOrName::Name(Rc::new(Value::from(tok[0])))),
596                        ns: None,
597                        prefix: None,
598                    }))
599                }
600            }
601            2 => {
602                // prefixed
603                if tok[0] == "*" {
604                    if tok[1] == "*" {
605                        Ok(NodeTest::Name(NameTest {
606                            name: Some(WildcardOrName::Wildcard),
607                            ns: Some(WildcardOrName::Wildcard),
608                            prefix: None,
609                        }))
610                    } else {
611                        Ok(NodeTest::Name(NameTest {
612                            name: Some(WildcardOrName::Name(Rc::new(Value::from(tok[1])))),
613                            ns: Some(WildcardOrName::Wildcard),
614                            prefix: None,
615                        }))
616                    }
617                } else if tok[1] == "*" {
618                    Ok(NodeTest::Name(NameTest {
619                        name: Some(WildcardOrName::Wildcard),
620                        ns: None,
621                        prefix: Some(Rc::new(Value::from(tok[0]))),
622                    }))
623                } else {
624                    Ok(NodeTest::Name(NameTest {
625                        name: Some(WildcardOrName::Name(Rc::new(Value::from(tok[1])))),
626                        ns: None,
627                        prefix: Some(Rc::new(Value::from(tok[0]))),
628                    }))
629                }
630            }
631            _ => Result::Err(Error::new(
632                ErrorKind::TypeError,
633                "invalid NodeTest".to_string(),
634            )),
635        }
636    }
637}
638
639#[derive(Copy, Clone, Debug)]
640pub enum KindTest {
641    Document,
642    Element,
643    Attribute,
644    SchemaElement,
645    SchemaAttribute,
646    PI,
647    Comment,
648    Text,
649    Namespace,
650    Any,
651}
652
653impl fmt::Display for KindTest {
654    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
655        match self {
656            KindTest::Document => write!(f, "document"),
657            KindTest::Element => write!(f, "element"),
658            KindTest::Attribute => write!(f, "attribute"),
659            KindTest::SchemaElement => write!(f, "schema element"),
660            KindTest::SchemaAttribute => write!(f, "schema attribute"),
661            KindTest::PI => write!(f, "processing instruction"),
662            KindTest::Comment => write!(f, "comment"),
663            KindTest::Text => write!(f, "text"),
664            KindTest::Namespace => write!(f, "namespace"),
665            KindTest::Any => write!(f, "any node"),
666        }
667    }
668}
669
670impl KindTest {
671    /// Does an item match the Kind Test?
672    pub fn matches<N: Node>(&self, i: &Item<N>) -> bool {
673        match i {
674            Item::Node(n) => {
675                match (self, n.node_type()) {
676                    (KindTest::Document, NodeType::Document) => true,
677                    (KindTest::Document, _) => false,
678                    (KindTest::Element, NodeType::Element) => true,
679                    (KindTest::Element, _) => false,
680                    (KindTest::Attribute, NodeType::Attribute) => true,
681                    (KindTest::Attribute, _) => false,
682                    (KindTest::SchemaElement, _) => false, // not supported
683                    (KindTest::SchemaAttribute, _) => false, // not supported
684                    (KindTest::PI, NodeType::ProcessingInstruction) => true,
685                    (KindTest::PI, _) => false,
686                    (KindTest::Comment, NodeType::Comment) => true,
687                    (KindTest::Comment, _) => false,
688                    (KindTest::Text, NodeType::Text) => true,
689                    (KindTest::Text, _) => false,
690                    (KindTest::Namespace, _) => false, // not yet implemented
691                    (KindTest::Any, _) => true,
692                }
693            }
694            _ => false,
695        }
696    }
697    pub fn to_string(&self) -> &'static str {
698        match self {
699            KindTest::Document => "DocumentTest",
700            KindTest::Element => "ElementTest",
701            KindTest::Attribute => "AttributeTest",
702            KindTest::SchemaElement => "SchemaElementTest",
703            KindTest::SchemaAttribute => "SchemaAttributeTest",
704            KindTest::PI => "PITest",
705            KindTest::Comment => "CommentTest",
706            KindTest::Text => "TextTest",
707            KindTest::Namespace => "NamespaceNodeTest",
708            KindTest::Any => "AnyKindTest",
709        }
710    }
711}
712
713#[derive(Clone, Debug)]
714pub struct NameTest {
715    pub ns: Option<WildcardOrName>,
716    pub prefix: Option<Rc<Value>>,
717    pub name: Option<WildcardOrName>,
718}
719
720impl NameTest {
721    pub fn new(
722        ns: Option<WildcardOrName>,
723        prefix: Option<Rc<Value>>,
724        name: Option<WildcardOrName>,
725    ) -> Self {
726        NameTest { ns, prefix, name }
727    }
728    /// Does an Item match this name test? To match, the item must be a node, have a name,
729    /// have the namespace URI and local name be equal or a wildcard
730    pub fn matches<N: Node>(&self, i: &Item<N>) -> bool {
731        match i {
732            Item::Node(n) => {
733                match n.node_type() {
734                    NodeType::Element | NodeType::ProcessingInstruction | NodeType::Attribute => {
735                        // TODO: avoid converting the values into strings just for comparison
736                        // Value interning should fix this
737                        match (
738                            self.ns.as_ref(),
739                            self.name.as_ref(),
740                            n.name().namespace_uri(),
741                            n.name().localname_to_string().as_str(),
742                        ) {
743                            (None, None, _, _) => false,
744                            (None, Some(WildcardOrName::Wildcard), None, _) => true,
745                            (None, Some(WildcardOrName::Wildcard), Some(_), _) => false,
746                            (None, Some(WildcardOrName::Name(_)), None, "") => false,
747                            (None, Some(WildcardOrName::Name(wn)), None, qn) => {
748                                wn.to_string() == qn
749                            }
750                            (None, Some(WildcardOrName::Name(_)), Some(_), _) => false,
751                            (Some(_), None, _, _) => false, // A namespace URI without a local name doesn't make sense
752                            (
753                                Some(WildcardOrName::Wildcard),
754                                Some(WildcardOrName::Wildcard),
755                                _,
756                                _,
757                            ) => true,
758                            (
759                                Some(WildcardOrName::Wildcard),
760                                Some(WildcardOrName::Name(_)),
761                                _,
762                                "",
763                            ) => false,
764                            (
765                                Some(WildcardOrName::Wildcard),
766                                Some(WildcardOrName::Name(wn)),
767                                _,
768                                qn,
769                            ) => wn.to_string() == qn,
770                            (Some(WildcardOrName::Name(_)), Some(_), None, _) => false,
771                            (
772                                Some(WildcardOrName::Name(wnsuri)),
773                                Some(WildcardOrName::Name(wn)),
774                                Some(qnsuri),
775                                qn,
776                            ) => wnsuri.clone() == qnsuri && wn.to_string() == qn,
777                            _ => false, // maybe should panic?
778                        }
779                    }
780                    _ => false, // all other node types don't have names
781                }
782            }
783            _ => false, // other item types don't have names
784        }
785    }
786}
787
788impl fmt::Display for NameTest {
789    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
790        let result = if self.name.is_some() {
791            match self.name.as_ref().unwrap() {
792                WildcardOrName::Wildcard => "*".to_string(),
793                WildcardOrName::Name(n) => n.to_string(),
794            }
795        } else {
796            "--no name--".to_string()
797        };
798        f.write_str(result.as_str())
799    }
800}
801
802#[derive(Clone, Debug)]
803pub enum WildcardOrName {
804    Wildcard,
805    Name(Rc<Value>),
806}
807
808#[derive(Copy, Clone, Debug, PartialEq)]
809pub enum Axis {
810    Child,
811    Descendant,
812    DescendantOrSelf,
813    DescendantOrSelfOrRoot,
814    Attribute,
815    SelfAttribute, // a special axis, only for matching an attribute in a pattern match
816    SelfAxis,
817    SelfDocument,  // a special axis, only for matching the Document in a pattern match
818    SelfNamespace, // a special axis, only for matching the namespace in a pattern match
819    Following,
820    FollowingSibling,
821    Namespace,
822    Parent,
823    ParentDocument, // a special axis, only for matching in a pattern match. Matches the parent as well as the Document.
824    Ancestor,
825    AncestorOrSelf,
826    AncestorOrSelfOrRoot, // a special axis for matching in a pattern
827    Preceding,
828    PrecedingSibling,
829    Unknown,
830}
831
832impl From<&str> for Axis {
833    fn from(s: &str) -> Self {
834        match s {
835            "child" => Axis::Child,
836            "descendant" => Axis::Descendant,
837            "descendant-or-self" => Axis::DescendantOrSelf,
838            "attribute" => Axis::Attribute,
839            "self" => Axis::SelfAxis,
840            "following" => Axis::Following,
841            "following-sibling" => Axis::FollowingSibling,
842            "namespace" => Axis::Namespace,
843            "parent" => Axis::Parent,
844            "ancestor" => Axis::Ancestor,
845            "ancestor-or-self" => Axis::AncestorOrSelf,
846            "preceding" => Axis::Preceding,
847            "preceding-sibling" => Axis::PrecedingSibling,
848            _ => Axis::Unknown,
849        }
850    }
851}
852
853impl fmt::Display for Axis {
854    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
855        let result = match self {
856            Axis::Child => "child".to_string(),
857            Axis::Descendant => "descendant".to_string(),
858            Axis::DescendantOrSelf => "descendant-or-self".to_string(),
859            Axis::DescendantOrSelfOrRoot => "descendant-or-self-or-root".to_string(),
860            Axis::Attribute => "attribute".to_string(),
861            Axis::SelfAttribute => "self-attribute".to_string(),
862            Axis::SelfAxis => "self".to_string(),
863            Axis::SelfDocument => "self-document".to_string(),
864            Axis::Following => "following".to_string(),
865            Axis::FollowingSibling => "following-sibling".to_string(),
866            Axis::Namespace => "namespace".to_string(),
867            Axis::Parent => "parent".to_string(),
868            Axis::ParentDocument => "parent-document".to_string(),
869            Axis::Ancestor => "ancestor".to_string(),
870            Axis::AncestorOrSelf => "ancestor-or-self".to_string(),
871            Axis::Preceding => "preceding".to_string(),
872            Axis::PrecedingSibling => "preceding-sibling".to_string(),
873            _ => "unknown".to_string(),
874        };
875        f.write_str(result.as_str())
876    }
877}
878
879#[derive(Clone, Debug)]
880pub struct ArithmeticOperand<N: Node> {
881    pub op: ArithmeticOperator,
882    pub operand: Transform<N>,
883}
884
885impl<N: Node> ArithmeticOperand<N> {
886    pub fn new(op: ArithmeticOperator, operand: Transform<N>) -> Self {
887        ArithmeticOperand { op, operand }
888    }
889}
890
891#[derive(Copy, Clone, Debug, PartialEq)]
892pub enum ArithmeticOperator {
893    Noop,
894    Add,
895    Multiply,
896    Divide,
897    IntegerDivide,
898    Subtract,
899    Modulo,
900}
901
902impl fmt::Display for ArithmeticOperator {
903    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
904        match self {
905            ArithmeticOperator::Add => f.write_str("+"),
906            ArithmeticOperator::Multiply => f.write_str("*"),
907            ArithmeticOperator::Divide => f.write_str("div"),
908            ArithmeticOperator::IntegerDivide => f.write_str("idiv"),
909            ArithmeticOperator::Subtract => f.write_str("-"),
910            ArithmeticOperator::Modulo => f.write_str("mod"),
911            ArithmeticOperator::Noop => f.write_str("noop"),
912        }
913    }
914}
915
916impl From<&str> for ArithmeticOperator {
917    fn from(a: &str) -> Self {
918        match a {
919            "+" => ArithmeticOperator::Add,
920            "*" => ArithmeticOperator::Multiply,
921            "div" => ArithmeticOperator::Divide,
922            "idiv" => ArithmeticOperator::IntegerDivide,
923            "-" => ArithmeticOperator::Subtract,
924            "mod" => ArithmeticOperator::Modulo,
925            _ => ArithmeticOperator::Noop,
926        }
927    }
928}
929
930impl From<String> for ArithmeticOperator {
931    fn from(a: String) -> Self {
932        match a.as_str() {
933            "+" => ArithmeticOperator::Add,
934            "*" => ArithmeticOperator::Multiply,
935            "div" => ArithmeticOperator::Divide,
936            "idiv" => ArithmeticOperator::IntegerDivide,
937            "-" => ArithmeticOperator::Subtract,
938            "mod" => ArithmeticOperator::Modulo,
939            _ => ArithmeticOperator::Noop,
940        }
941    }
942}