Skip to main content

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