Skip to main content

xrust/transform/
context.rs

1/*! Context for a transformation
2
3A dynamic and static context for a transformation. These are both necessary to give the transformation all the data it needs to performs its functions.
4
5The dynamic [Context] stores data that changes. It is frequently cloned to create a new context. A [ContextBuilder] can be used to create the dynamic context incrementally.
6
7The [StaticContext] stores immutable data and is not cloneable. A [StaticContextBuilder] can be used to create the static context incrementally.
8
9A [Context] is used to evaluate a [Transform]. The evaluate method matches the current item to a template and then evaluates the body of that template. The dispatch method directly evaluates a given [Transform].
10
11 */
12
13use stacksafe::stacksafe;
14
15use crate::item::{Node, Sequence};
16use crate::output::OutputDefinition;
17#[allow(unused_imports)]
18use crate::pattern::Pattern;
19use crate::security::{Policy, SecurityResult};
20use crate::transform::booleans::*;
21use crate::transform::callable::{ActualParameters, Callable, invoke};
22use crate::transform::construct::*;
23use crate::transform::controlflow::*;
24use crate::transform::datetime::*;
25use crate::transform::functions::*;
26use crate::transform::grouping::*;
27use crate::transform::keys::{key, populate_key_values};
28use crate::transform::logic::*;
29use crate::transform::misc::*;
30use crate::transform::navigate::*;
31use crate::transform::numbers::*;
32use crate::transform::strings::*;
33use crate::transform::template::{Template, apply_imports, apply_templates, next_match};
34use crate::transform::variables::{declare_variable, reference_variable};
35use crate::transform::{MAXDEPTH, Transform};
36use crate::xdmerror::Error;
37use crate::{ErrorKind, Item, SequenceTrait, Value};
38use qualname::{NamespaceMap, NamespaceUri, NcName, QName};
39use std::cmp::Ordering;
40use std::collections::HashMap;
41use std::rc::Rc;
42use std::sync::LazyLock;
43use url::Url;
44
45/// The transformation context. This is the dynamic context.
46/// The static parts of the context are in a separate structure.
47/// Contexts are immutable, but frequently are cloned to provide a new context.
48/// Although it is optional, it would be very unusual not to set a result document in a context since nodes cannot be created in the result without one.
49/// ## Security
50/// Certain security features are provided, as detailed below. These may be set by a security policy, see [χrust Security](https://gitlab.gnome.org/balls/xrust-sec)
51/// ### Evaluation Depth
52/// The depth of evaluation may be limited to prevent an infinite loop.
53/// Feature URI:
54///```xslt
55/// Q{http://gitlab.gnome.org/World/Rust/markup-rs/xrust/transform}maximum-depth
56///```
57/// Security Result: NotPermitted - Use default value; Permitted - no limit; Value - number is maximum evaluation depth.
58#[derive(Clone, Debug)]
59pub struct Context<N: Node> {
60    pub(crate) context: Sequence<N>,          // The (outer) context
61    pub(crate) i: usize,                      // The index to the item that is the context item
62    pub(crate) context_item: Option<Item<N>>, // The context item, which is usually context[i]. Sometimes the inner focus is different to the context, e.g. when evaluating predicates
63    // Context size for the last() function. When None it is derived from
64    // context.len(); it is set explicitly while filtering by a predicate, where
65    // the context sequence is a single focus node but last() must report the
66    // size of the node-list being filtered.
67    pub(crate) last: Option<usize>,
68    pub(crate) current: Sequence<N>,
69    pub(crate) current_item: Option<Item<N>>, // The "current" XPath item, which is really the context item for the invoking context. See XSLT 20.4.1.
70    pub(crate) depth: usize,                  // Depth of evaluation
71    pub(crate) max_depth: Option<usize>, // Maximum allowed evaluation depth. None means no limit.
72    pub(crate) rd: Option<N>,            // Result document
73    // There is no distinction between built-in and user-defined templates
74    // Built-in templates have no priority and no document order
75    pub(crate) templates: Vec<Rc<Template<N>>>,
76    pub(crate) current_templates: Vec<Rc<Template<N>>>,
77    // Named templates and functions
78    pub(crate) callables: HashMap<QName, Callable<N>>,
79    // Variables, with scoping
80    pub(crate) vars: HashMap<String, Vec<Sequence<N>>>,
81    // Stylesheet variables, to be evaluated before template processing.
82    // (name, value) pairs
83    pub(crate) pre_vars: Vec<(String, Transform<N>)>,
84    // Grouping
85    pub(crate) current_grouping_key: Option<Rc<Value>>,
86    pub(crate) current_group: Sequence<N>,
87    // Keys
88    // The declaration of a key. Keys are named, and each key can have multiple definitions.
89    // Each definition is the pattern that matches nodes and the expression that computes the key value.
90    pub(crate) keys: HashMap<String, Vec<(Pattern<N>, Transform<N>)>>,
91    // The calculated values of keys.
92    pub(crate) key_values: HashMap<String, HashMap<String, Vec<N>>>,
93    // Output control
94    pub(crate) od: OutputDefinition,
95    pub(crate) base_url: Option<Url>,
96    // Namespace resolution. If any transforms contain a QName that needs to be resolved to an EQName,
97    // then these URI -> prefix mappings may be used. These are usually derived from the stylesheet document.
98    // The search order is: Namespace declarations in the source document; namespace declarations in the result document; this NamespaceMap.
99    pub(crate) namespaces: Option<Rc<NamespaceMap>>,
100    // The security policy in force
101    policy: Option<Rc<Policy<N>>>,
102}
103impl<N: Node> Default for Context<N> {
104    fn default() -> Self {
105        Self::new()
106    }
107}
108
109impl<N: Node> Context<N> {
110    pub fn new() -> Self {
111        Context {
112            context: Sequence::new(),
113            current: Sequence::new(),
114            i: 0,
115            context_item: None,
116            last: None,
117            current_item: None,
118            depth: 0,
119            max_depth: Some(MAXDEPTH),
120            rd: None,
121            templates: vec![],
122            current_templates: vec![],
123            callables: HashMap::new(),
124            vars: HashMap::new(),
125            pre_vars: Vec::new(),
126            current_grouping_key: None,
127            current_group: Sequence::new(),
128            keys: HashMap::new(),
129            key_values: HashMap::new(),
130            od: OutputDefinition::new(),
131            base_url: None,
132            namespaces: None,
133            policy: None,
134        }
135    }
136    /// Sets the outer context and the context item.
137    pub fn context(&mut self, s: Sequence<N>, i: usize) {
138        self.context_item = Some(s[i].clone());
139        self.context = s;
140        self.i = i;
141    }
142    /// Sets the context item (which must be a member of the context) without changing the outer context.
143    pub fn context_item(&mut self, i: Option<Item<N>>) {
144        self.context_item = i;
145    }
146    /// Sets the "current" item.
147    pub fn current_item(&mut self, i: Item<N>) {
148        self.current_item = Some(i);
149    }
150    /// Sets the current context
151    pub fn current(&mut self, s: Sequence<N>) {
152        self.current = s;
153    }
154    /// Sets the result document. Any nodes created by the transformation are owned by this document.
155    pub fn result_document(&mut self, rd: N) {
156        self.rd = Some(rd);
157    }
158    /// Declare a key
159    pub fn declare_key(&mut self, name: String, m: Pattern<N>, u: Transform<N>) {
160        if let Some(v) = self.keys.get_mut(&name) {
161            v.push((m, u))
162        } else {
163            self.keys.insert(name.clone(), vec![(m, u)]);
164        }
165        // Initialise the key values store with an empty hashmap
166        if self.key_values.get_mut(&name).is_some() {
167            // Already initialised
168        } else {
169            self.key_values.insert(name, HashMap::new());
170        }
171    }
172    /// Calculate the key values for a source document
173    pub fn populate_key_values<
174        F: FnMut(&str) -> Result<(), Error>,
175        G: FnMut(&str) -> Result<N, Error>,
176        H: FnMut(&Url) -> Result<String, Error>,
177    >(
178        &mut self,
179        stctxt: &mut StaticContext<N, F, G, H>,
180        sd: N,
181    ) -> Result<(), Error> {
182        populate_key_values(self, stctxt, sd)
183    }
184    pub fn dump_key_values(&self) {
185        self.key_values.iter().for_each(|(k, v)| {
186            println!("key \"{}\":", k);
187            v.iter()
188                .for_each(|(kk, vv)| println!("\tvalue \"{}\" {} nodes", kk, vv.len()))
189        })
190    }
191    /// Add a named attribute set. This replaces any previously declared attribute set with the same name
192    pub fn attribute_set(&mut self, _name: QName, _body: Vec<Transform<N>>) {}
193    /// Set the value of a variable. If the variable already exists, then this creates a new inner scope.
194    pub fn var_push(&mut self, name: String, value: Sequence<N>) {
195        match self.vars.get_mut(name.as_str()) {
196            Some(u) => {
197                // If the variable already has a value, then this is a new, inner scope
198                u.push(value);
199            }
200            None => {
201                // Otherwise this is the first scope for the variable
202                self.vars.insert(name, vec![value]);
203            }
204        }
205    }
206    /// Remove a variable
207    #[allow(dead_code)]
208    fn var_pop(&mut self, name: String) {
209        self.vars.get_mut(name.as_str()).map(|u| u.pop());
210    }
211    #[allow(dead_code)]
212    pub(crate) fn dump_vars(&self) -> String {
213        self.vars.iter().fold(String::new(), |mut acc, (k, v)| {
214            acc.push_str(format!("{}==\"{}\", ", k, v[0].to_string()).as_str());
215            acc
216        })
217    }
218    /// Add a stylesheet variable
219    pub fn pre_var_push(&mut self, name: String, x: Transform<N>) {
220        self.pre_vars.push((name, x));
221    }
222
223    /// Callable components: named templates and user-defined functions
224    pub fn callable_push(&mut self, qn: QName, c: Callable<N>) {
225        self.callables.insert(qn, c);
226    }
227    /// Find the callable component with the given QName
228    pub fn callable_get(&self, qn: &QName) -> Option<Callable<N>> {
229        self.callables.get(qn).cloned()
230    }
231
232    /// Returns the Base URL.
233    #[allow(dead_code)]
234    fn baseurl(&self) -> Option<Url> {
235        self.base_url.clone()
236    }
237    /// Set the Base URL. This is used to resolve relative URLs.
238    #[allow(dead_code)]
239    fn set_baseurl(&mut self, url: Url) {
240        self.base_url = Some(url);
241    }
242
243    /// Set the in-force security policy.
244    /// Returns an error if calculating security feature values fail.
245    pub fn policy(&mut self, policy: Rc<Policy<N>>) -> Result<(), Error> {
246        // Re-calculate all cached security-related values
247        calculate_features(self, &policy)?;
248
249        self.policy = Some(policy);
250
251        Ok(())
252    }
253    // Return the value of a security feature.
254    // This will be determined by an in-force security policy.
255    // If there is no security policy in force, then the feature is either not permitted or this library must supply a default value.
256    pub fn security_feature(
257        &self,
258        f: &QName,
259        a: ActualParameters<N>,
260    ) -> Result<SecurityResult, Error> {
261        if *f == *MAXDEPTH_QNAME {
262            self.policy
263                .as_ref()
264                .map_or_else(|| Ok(SecurityResult::NotPermitted), |p| p.get(f, a))
265        } else {
266            // Unknown feature, so default to not permitted
267            Ok(SecurityResult::NotPermitted)
268        }
269    }
270
271    // Does the work of evaluating the context,
272    // but without the initial setup.
273    pub(crate) fn evaluate_internal<
274        F: FnMut(&str) -> Result<(), Error>,
275        G: FnMut(&str) -> Result<N, Error>,
276        H: FnMut(&Url) -> Result<String, Error>,
277    >(
278        &self,
279        stctxt: &mut StaticContext<N, F, G, H>,
280    ) -> Result<Sequence<N>, Error> {
281        if self.context.is_empty() {
282            Ok(Sequence::new())
283        } else {
284            self.context_item.as_ref().map_or_else(
285                || {
286                    Err(Error::new(
287                        ErrorKind::DynamicAbsent,
288                        String::from("no context item"),
289                    ))
290                },
291                |i| {
292                    // There may be 0, 1, or more matching templates.
293                    // If there are more than one with the same priority and import level,
294                    // then take the one with the higher document order.
295                    let templates = self.find_templates(stctxt, i, &None)?;
296                    match templates.len() {
297                        0 => Err(Error::new(
298                            ErrorKind::DynamicAbsent,
299                            String::from("no matching template"),
300                        )),
301                        1 => self.dispatch(stctxt, &templates[0].body),
302                        _ => {
303                            if templates[0].priority == templates[1].priority
304                                && templates[0].import.len() == templates[1].import.len()
305                            {
306                                let mut candidates: Vec<Rc<Template<N>>> = templates
307                                    .iter()
308                                    .take_while(|t| {
309                                        t.priority == templates[0].priority
310                                            && t.import.len() == templates[0].import.len()
311                                    })
312                                    .cloned()
313                                    .collect();
314                                candidates.sort_unstable_by(|a, b| {
315                                    a.document_order.map_or(Ordering::Greater, |v| {
316                                        b.document_order.map_or(Ordering::Less, |u| v.cmp(&u))
317                                    })
318                                });
319                                self.dispatch(stctxt, &candidates.last().unwrap().body)
320                            } else {
321                                self.dispatch(stctxt, &templates[0].body)
322                            }
323                        }
324                    }
325                },
326            )
327        }
328    }
329    /// Evaluate finds a template matching the current item and evaluates the body of the template,
330    /// returning the resulting [Sequence].
331    /// ```rust
332    /// use std::rc::Rc;
333    /// use url::Url;
334    /// use xrust::ErrorKind;
335    /// use xrust::xdmerror::Error;
336    /// use xrust::item::{Item, Sequence, SequenceTrait, Node, NodeType};
337    /// use xrust::transform::Transform;
338    /// use xrust::transform::context::{Context, StaticContext, StaticContextBuilder};
339    /// use xrust::trees::smite::RNode;
340    /// use xrust::parser::ParseError;
341    /// use xrust::parser::xml::parse;
342    /// use xrust::xslt::from_document;
343    ///
344    /// // A little helper function to parse a string to a Document Node
345    /// fn make_from_str(s: &str) -> RNode {
346    ///   let mut d = RNode::new_document();
347    ///   parse(d.clone(), s,
348    ///     Some(|_: &_| Err(ParseError::MissingNameSpace)))
349    ///     .expect("failed to parse XML");
350    ///   d
351    /// }
352    ///
353    /// let sd = Item::Node(make_from_str("<Example/>"));
354    /// let style = make_from_str("<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
355    /// <xsl:template match='/'><xsl:apply-templates/></xsl:template>
356    /// <xsl:template match='child::Example'>This template will match</xsl:template>
357    /// </xsl:stylesheet>");
358    /// let mut stctxt = StaticContextBuilder::new()
359    ///     .message(|_| Ok(()))
360    ///     .fetcher(|_| Ok(String::new()))
361    ///     .parser(|s| Ok(make_from_str(s)))
362    ///     .build();
363    /// let mut context = from_document(style, None, |s| Ok(make_from_str(s)), |_| Ok(String::new())).expect("unable to compile stylesheet");
364    /// context.context(vec![sd], 0);
365    /// context.result_document(make_from_str("<Result/>"));
366    /// let sequence = context.evaluate(&mut stctxt).expect("evaluation failed");
367    /// assert_eq!(sequence.to_string(), "This template will match")
368    /// ```
369    pub fn evaluate<
370        F: FnMut(&str) -> Result<(), Error>,
371        G: FnMut(&str) -> Result<N, Error>,
372        H: FnMut(&Url) -> Result<String, Error>,
373    >(
374        &self,
375        stctxt: &mut StaticContext<N, F, G, H>,
376    ) -> Result<Sequence<N>, Error> {
377        // Define initial (stylesheet) variables by evaluating their transformation with the root node as the context
378        if self.context.is_empty() {
379            // There is no context item
380            Ok(Sequence::new())
381        } else {
382            let mut ctxt = self.clone();
383            // If the context item is a node then set the new context to the root node
384            // otherwise there is no context
385            ctxt.context(
386                self.context.get(self.i).map_or_else(Vec::new, |i| {
387                    if let Item::Node(n) = i {
388                        vec![Item::Node(n.owner_document())]
389                    } else {
390                        vec![]
391                    }
392                }),
393                0,
394            );
395            // Populate the context with stylesheet-level variables.
396            // Each variable creates a new context for the evaluation of subsequent variables.
397            for (name, x) in &self.pre_vars {
398                ctxt.var_push(name.clone(), ctxt.dispatch(stctxt, x)?);
399            }
400            let result = ctxt.evaluate_internal(stctxt)?;
401            // If any of the result items are nodes then add them as children of the result document
402            if let Some(mut rd) = ctxt.rd {
403                Ok(result
404                    .into_iter()
405                    .map(|i| {
406                        if let Item::Node(ref n) = i {
407                            if n.owner_document().is_same(&rd) {
408                                if n.is_unattached() {
409                                    // Attach it and leave it in the result
410                                    rd.push(n.clone())
411                                        .expect("unable to attach to result document");
412                                    //Some(i)
413                                    i
414                                } else {
415                                    // leave it where it is in the result document
416                                    //Some(i)
417                                    i
418                                }
419                            } else {
420                                // This is a node from the source document.
421                                // Copy it to the result document
422                                // and replace the node in the result with the copy
423                                let cp = n.deep_copy().expect("unable to copy node");
424                                rd.push(cp.clone())
425                                    .expect("unable to attach to result document");
426                                //Some(Item::Node(cp))
427                                Item::Node(cp)
428                            }
429                        } else {
430                            //Some(i)
431                            i
432                        }
433                    })
434                    .collect())
435            } else {
436                Ok(result)
437            }
438        }
439    }
440
441    /// Find a template with a matching [Pattern] in the given mode.
442    pub fn find_templates<
443        F: FnMut(&str) -> Result<(), Error>,
444        G: FnMut(&str) -> Result<N, Error>,
445        H: FnMut(&Url) -> Result<String, Error>,
446    >(
447        &self,
448        stctxt: &mut StaticContext<N, F, G, H>,
449        i: &Item<N>,
450        m: &Option<QName>,
451    ) -> Result<Vec<Rc<Template<N>>>, Error> {
452        let mut candidates =
453            self.templates
454                .iter()
455                .filter(|t| t.mode == *m)
456                .try_fold(vec![], |mut cand, t| {
457                    let e = t.pattern.matches(self, stctxt, i);
458                    if e {
459                        cand.push(t.clone())
460                    }
461                    Ok(cand)
462                })?;
463        if !candidates.is_empty() {
464            // Find the template(s) with the lowest priority.
465
466            candidates.sort_unstable_by(|a, b| (*a).cmp(b));
467            Ok(candidates)
468        } else {
469            Err(Error::new(
470                ErrorKind::Unknown,
471                format!("no matching template for item {:?} in mode \"{:?}\"", i, m),
472            ))
473        }
474    }
475
476    /// Interpret the given [Transform] object
477    /// ```rust
478    /// use std::rc::Rc;
479    /// use url::Url;
480    /// use xrust::xdmerror::{Error, ErrorKind};
481    /// use xrust::item::{Item, Sequence, SequenceTrait, Node, NodeType};
482    /// use xrust::transform::{Transform, NodeMatch, NodeTest, KindTest,  Axis};
483    /// use xrust::transform::context::{Context, ContextBuilder, StaticContext, StaticContextBuilder};
484    /// use xrust::trees::smite::RNode;
485    /// use xrust::parser::ParseError;
486    /// use xrust::parser::xml::parse;
487    ///
488    /// // A little helper function to parse a string to a Document Node
489    /// fn make_from_str(s: &str) -> RNode {
490    ///   let mut d = RNode::new_document();
491    ///   parse(d.clone(), s, Some(|_: &_| Err(ParseError::MissingNameSpace)))
492    ///     .expect("failed to parse XML");
493    ///   d
494    /// }
495    ///
496    /// // Equivalent to "child::*"
497    /// let t = Transform::Step(NodeMatch {axis: Axis::Child, nodetest: NodeTest::Kind(KindTest::Any)});
498    /// let sd = Item::Node(make_from_str("<Example/>"));
499    /// let mut stctxt = StaticContextBuilder::new()
500    ///    .message(|_| Ok(()))
501    ///    .fetcher(|_| Err(Error::new(ErrorKind::NotImplemented, "not implemented")))
502    ///    .parser(|_| Err(Error::new(ErrorKind::NotImplemented, "not implemented")))
503    ///     .build();
504    /// let context = ContextBuilder::new()
505    ///   .context(vec![sd])
506    ///   .build();
507    /// let sequence = context.dispatch(&mut stctxt, &t).expect("evaluation failed");
508    /// assert_eq!(sequence.to_xml(), "<Example/>")
509    /// ```
510    #[stacksafe]
511    pub fn dispatch<
512        F: FnMut(&str) -> Result<(), Error>,
513        G: FnMut(&str) -> Result<N, Error>,
514        H: FnMut(&Url) -> Result<String, Error>,
515        //L: FnMut(&NamespacePrefix) -> Result<NamespaceUri, ParseError>,
516    >(
517        &self,
518        stctxt: &mut StaticContext<N, F, G, H>,
519        t: &Transform<N>,
520    ) -> Result<Sequence<N>, Error> {
521        match t {
522            Transform::Root => root(self),
523            Transform::ContextItem => context(self),
524            Transform::CurrentItem => current(self),
525            Transform::Compose(v) => compose(self, stctxt, v),
526            Transform::Step(nm) => step(self, nm),
527            Transform::StepPredicated(nm, preds) => step_predicated(self, stctxt, nm, preds),
528            Transform::Filter(t) => filter(self, stctxt, t),
529            Transform::Empty => empty(self),
530            Transform::Literal(v) => literal(self, v),
531            Transform::LiteralElement(qn, t) => literal_element(self, stctxt, qn, t),
532            Transform::Element(qn, t) => element(self, stctxt, qn, t),
533            Transform::LiteralText(t, b) => literal_text(self, stctxt, t, b),
534            Transform::LiteralAttribute(qn, t) => literal_attribute(self, stctxt, qn, t),
535            Transform::LiteralComment(t) => literal_comment(self, stctxt, t),
536            Transform::LiteralProcessingInstruction(n, t) => {
537                literal_processing_instruction(self, stctxt, n, t)
538            }
539            Transform::NamespaceDeclaration(p, u, s) => {
540                namespace_declaration(self, stctxt, p, u, s)
541            }
542            Transform::SetAttribute(qn, v) => set_attribute(self, stctxt, qn, v),
543            Transform::SequenceItems(v) => make_sequence(self, stctxt, v),
544            Transform::Copy(f, t) => copy(self, stctxt, f, t),
545            Transform::DeepCopy(d) => deep_copy(self, stctxt, d),
546            Transform::Or(v) => tr_or(self, stctxt, v),
547            Transform::And(v) => tr_and(self, stctxt, v),
548            Transform::Union(b) => union(self, stctxt, b),
549            Transform::GeneralComparison(o, l, r) => general_comparison(self, stctxt, o, l, r),
550            Transform::ValueComparison(o, l, r) => value_comparison(self, stctxt, o, l, r),
551            Transform::Concat(v) => tr_concat(self, stctxt, v),
552            Transform::Range(s, e) => tr_range(self, stctxt, s, e),
553            Transform::Arithmetic(v) => arithmetic(self, stctxt, v),
554            Transform::Loop(v, b) => tr_loop(self, stctxt, v, b),
555            Transform::Switch(c, o) => switch(self, stctxt, c, o),
556            Transform::ForEach(g, s, b, o) => for_each(self, stctxt, g, s, b, o),
557            Transform::ApplyTemplates(s, m, o) => apply_templates(self, stctxt, s, m, o),
558            Transform::ApplyImports => apply_imports(self, stctxt),
559            Transform::NextMatch => next_match(self, stctxt),
560            Transform::VariableDeclaration(n, v, f, _) => {
561                declare_variable(self, stctxt, n.clone(), v, f)
562            }
563            Transform::VariableReference(n, _) => reference_variable(self, n),
564            Transform::Position => position(self),
565            Transform::Last => last(self),
566            Transform::Count(s) => tr_count(self, stctxt, s),
567            Transform::LocalName(s) => local_name(self, stctxt, s),
568            Transform::Name(s) => name(self, stctxt, s),
569            Transform::String(s) => string(self, stctxt, s),
570            Transform::StartsWith(s, t) => starts_with(self, stctxt, s, t),
571            Transform::EndsWith(s, t) => ends_with(self, stctxt, s, t),
572            Transform::Contains(s, t) => contains(self, stctxt, s, t),
573            Transform::Substring(s, t, l) => substring(self, stctxt, s, t, l),
574            Transform::SubstringBefore(s, t) => substring_before(self, stctxt, s, t),
575            Transform::SubstringAfter(s, t) => substring_after(self, stctxt, s, t),
576            Transform::NormalizeSpace(s) => normalize_space(self, stctxt, s),
577            Transform::Translate(s, m, t) => translate(self, stctxt, s, m, t),
578            Transform::GenerateId(s) => generate_id(self, stctxt, s),
579            Transform::Boolean(b) => boolean(self, stctxt, b),
580            Transform::Not(b) => not(self, stctxt, b),
581            Transform::True => tr_true(self),
582            Transform::False => tr_false(self),
583            Transform::Number(n) => number(self, stctxt, n),
584            Transform::Sum(s) => sum(self, stctxt, s),
585            Transform::Avg(s) => avg(self, stctxt, s),
586            Transform::Min(s) => min(self, stctxt, s),
587            Transform::Max(s) => max(self, stctxt, s),
588            Transform::Floor(n) => floor(self, stctxt, n),
589            Transform::Ceiling(n) => ceiling(self, stctxt, n),
590            Transform::Round(n, p) => round(self, stctxt, n, p),
591            Transform::CurrentGroup => current_group(self),
592            Transform::CurrentGroupingKey => current_grouping_key(self),
593            Transform::CurrentDateTime => current_date_time(self),
594            Transform::CurrentDate => current_date(self),
595            Transform::CurrentTime => current_time(self),
596            Transform::FormatDateTime(t, p, l, c, q) => {
597                format_date_time(self, stctxt, t, p, l, c, q)
598            }
599            Transform::FormatDate(t, p, l, c, q) => format_date(self, stctxt, t, p, l, c, q),
600            Transform::FormatTime(t, p, l, c, q) => format_time(self, stctxt, t, p, l, c, q),
601            Transform::FormatNumber(v, p, d) => format_number(self, stctxt, v, p, d),
602            Transform::FormatInteger(i, s) => format_integer(self, stctxt, i, s),
603            Transform::GenerateIntegers(start_at, select, n) => {
604                generate_integers(self, stctxt, start_at, select, n)
605            }
606            Transform::Key(n, v, _, _) => key(self, stctxt, n, v),
607            Transform::SystemProperty(p, ns) => system_property(self, stctxt, p, ns),
608            Transform::AvailableSystemProperties => available_system_properties(),
609            Transform::Document(uris, base) => document(self, stctxt, uris, base),
610            Transform::Invoke(qn, a, ns) => invoke(self, stctxt, qn, a, ns),
611            Transform::Message(b, s, e, t) => message(self, stctxt, b, s, e, t),
612            Transform::Error(k, m) => tr_error(self, k, m),
613            Transform::NotImplemented(s) => not_implemented(self, s),
614            _ => Err(Error::new(
615                ErrorKind::NotImplemented,
616                "not implemented".to_string(),
617            )),
618        }
619    }
620}
621
622fn calculate_features<N: Node>(ctxt: &mut Context<N>, policy: &Rc<Policy<N>>) -> Result<(), Error> {
623    match policy.get(&*MAXDEPTH_QNAME, ActualParameters::Named(vec![]))? {
624        SecurityResult::NotPermitted => ctxt.max_depth = Some(MAXDEPTH),
625        SecurityResult::Permitted(None) => ctxt.max_depth = None,
626        SecurityResult::Permitted(Some(v)) => {
627            ctxt.max_depth = Some(
628                v.parse::<usize>()
629                    .map_err(|_| Error::new(ErrorKind::ParseError, "not a number"))?,
630            )
631        }
632    }
633    Ok(())
634}
635
636impl<N: Node> From<Sequence<N>> for Context<N> {
637    fn from(value: Sequence<N>) -> Self {
638        let ci = if value.is_empty() {
639            None
640        } else {
641            Some(value[0].clone())
642        };
643        Context {
644            context_item: ci,
645            context: value,
646            i: 0,
647            last: None,
648            current_item: None,
649            current: Sequence::new(),
650            depth: 0,
651            max_depth: Some(MAXDEPTH),
652            rd: None,
653            templates: vec![],
654            current_templates: vec![],
655            callables: HashMap::new(),
656            vars: HashMap::new(),
657            pre_vars: Vec::new(),
658            keys: HashMap::new(),
659            key_values: HashMap::new(),
660            current_grouping_key: None,
661            current_group: Sequence::new(),
662            od: OutputDefinition::new(),
663            base_url: None,
664            namespaces: None,
665            policy: None,
666        }
667    }
668}
669
670/// Builder for a [Context]
671pub struct ContextBuilder<N: Node>(Context<N>);
672
673impl<N: Node> Default for ContextBuilder<N> {
674    fn default() -> Self {
675        Self::new()
676    }
677}
678
679impl<N: Node> ContextBuilder<N> {
680    pub fn new() -> Self {
681        ContextBuilder(Context::new())
682    }
683    /// Set the context sequence. The first item is the context item.
684    pub fn context(mut self, s: Sequence<N>) -> Self {
685        if !s.is_empty() {
686            self.0.context_item = Some(s[0].clone());
687        }
688        self.0.context = s;
689        self.0.i = 0;
690        self.0.last = None;
691        self
692    }
693    /// Set the context size reported by the last() function, decoupled from
694    /// context.len(). Used while filtering by a predicate.
695    pub fn last(mut self, n: usize) -> Self {
696        self.0.last = Some(n);
697        self
698    }
699    /// Set which item is the context item.
700    /// Does not check for validity.
701    /// Does not set the context item (see context_item()).
702    pub fn index(mut self, i: usize) -> Self {
703        self.0.i = i;
704        self
705    }
706    /// Sets the context item. This is usually context\[index\], but not always so this is not checked.
707    pub fn context_item(mut self, c: Option<Item<N>>) -> Self {
708        self.0.context_item = c;
709        self
710    }
711    pub fn current_item(mut self, i: Option<Item<N>>) -> Self {
712        self.0.current_item = i;
713        self
714    }
715    pub fn current(mut self, s: Sequence<N>) -> Self {
716        self.0.current = s;
717        self
718    }
719    pub fn depth(mut self, d: usize) -> Self {
720        self.0.depth = d;
721        self
722    }
723    /// Set the maximum depth of evaluation. None means no limit.
724    pub fn maximum_depth(mut self, m: Option<usize>) -> Self {
725        self.0.max_depth = m;
726        self
727    }
728    pub fn variable(mut self, n: String, v: Sequence<N>) -> Self {
729        self.0.var_push(n, v);
730        self
731    }
732    pub fn variables(mut self, v: HashMap<String, Vec<Sequence<N>>>) -> Self {
733        self.0.vars = v;
734        self
735    }
736    pub fn result_document(mut self, rd: N) -> Self {
737        self.0.rd = Some(rd);
738        self
739    }
740    pub fn template(mut self, t: Template<N>) -> Self {
741        self.0.templates.push(Rc::new(t));
742        self
743    }
744    pub fn template_all(mut self, v: Vec<Template<N>>) -> Self {
745        for t in v {
746            self.0.templates.push(Rc::new(t))
747        }
748        self
749    }
750    pub fn current_templates(mut self, c: Vec<Rc<Template<N>>>) -> Self {
751        self.0.current_templates = c;
752        self
753    }
754    pub fn current_group(mut self, c: Sequence<N>) -> Self {
755        self.0.current_group = c;
756        self
757    }
758    pub fn current_grouping_key(mut self, k: Rc<Value>) -> Self {
759        self.0.current_grouping_key = Some(k);
760        self
761    }
762    pub fn output_definition(mut self, od: OutputDefinition) -> Self {
763        self.0.od = od;
764        self
765    }
766    pub fn base_url(mut self, b: Url) -> Self {
767        self.0.base_url = Some(b);
768        self
769    }
770    pub fn callable(mut self, qn: QName, c: Callable<N>) -> Self {
771        self.0.callables.insert(qn, c);
772        self
773    }
774    pub fn namespaces(mut self, nm: NamespaceMap) -> Self {
775        self.0.namespaces = Some(Rc::new(nm));
776        self
777    }
778    /// Set the in-force security policy
779    pub fn policy(mut self, p: Rc<Policy<N>>) -> Result<Self, Error> {
780        calculate_features(&mut self.0, &p)?;
781        self.0.policy = Some(p);
782        Ok(self)
783    }
784    pub fn build(self) -> Context<N> {
785        self.0
786    }
787}
788
789/// Derive a new [Context] from an old [Context]. The context sequence and item in the old context becomes the "current" sequence and item in the new context.
790impl<N: Node> From<&Context<N>> for ContextBuilder<N> {
791    fn from(c: &Context<N>) -> Self {
792        c.context_item.as_ref().map_or_else(
793            || ContextBuilder(c.clone()).current(vec![]).current_item(None),
794            |i| {
795                ContextBuilder(c.clone())
796                    .current(c.context.clone())
797                    .current_item(Some(i.clone()))
798            },
799        )
800    }
801}
802
803/// The static context. This is not cloneable, since it includes the storage of a closure.
804/// The main feature of the static context is the ability to set up a callback for messages.
805/// See [StaticContextBuilder] for details.
806pub struct StaticContext<N: Node, F, G, H>
807where
808    F: FnMut(&str) -> Result<(), Error>,
809    G: FnMut(&str) -> Result<N, Error>, // Parses a string into a tree
810    H: FnMut(&Url) -> Result<String, Error>, // Fetches the data from a URL
811{
812    pub(crate) message: Option<F>,
813    pub(crate) parser: Option<G>,
814    pub(crate) fetcher: Option<H>,
815}
816impl<N: Node, F, G, H> Default for StaticContext<N, F, G, H>
817where
818    F: FnMut(&str) -> Result<(), Error>,
819    G: FnMut(&str) -> Result<N, Error>,
820    H: FnMut(&Url) -> Result<String, Error>,
821{
822    fn default() -> Self {
823        Self::new()
824    }
825}
826
827impl<N: Node, F, G, H> StaticContext<N, F, G, H>
828where
829    F: FnMut(&str) -> Result<(), Error>,
830    G: FnMut(&str) -> Result<N, Error>,
831    H: FnMut(&Url) -> Result<String, Error>,
832{
833    pub fn new() -> Self {
834        StaticContext {
835            message: None,
836            parser: None,
837            fetcher: None,
838        }
839    }
840}
841
842/// Builder for a [StaticContext].
843/// The main feature of the static context is the ability to set up a callback for messages.
844/// ```rust
845/// use std::rc::Rc;
846/// use qualname::{QName, NcName};
847/// use xrust::{Error, ErrorKind};
848/// use xrust::value::Value;
849/// use xrust::item::{Item, Sequence, SequenceTrait, Node, NodeType};
850/// use xrust::trees::smite::RNode;
851/// use xrust::transform::Transform;
852/// use xrust::transform::context::{Context, ContextBuilder, StaticContext, StaticContextBuilder};
853///
854/// let mut message = String::from("no message received");
855/// let xform = Transform::LiteralElement(
856///   QName::from_local_name(NcName::try_from("Example").unwrap()),
857///   Box::new(Transform::SequenceItems(vec![
858///    Transform::Message(
859///        Box::new(Transform::Literal(Item::Value(Rc::new(Value::from("a message from the transformation"))))),
860///        None,
861///        Box::new(Transform::Empty),
862///        Box::new(Transform::Empty),
863///    ),
864///    Transform::Literal(Item::Value(Rc::new(Value::from("element content")))),
865///   ]))
866/// );
867/// let mut context = ContextBuilder::new()
868///    .result_document(RNode::new_document())
869///    .build();
870/// let mut static_context = StaticContextBuilder::new()
871///    .message(|m| {message = String::from(m); Ok(())})
872///    .fetcher(|_| Ok(String::new()))
873///    .parser(|_| Err(Error::new(ErrorKind::NotImplemented, "not implemented")))
874///    .build();
875/// let sequence = context.dispatch(&mut static_context, &xform).expect("evaluation failed");
876///
877/// assert_eq!(sequence.to_xml(), "<Example>element content</Example>");
878/// assert_eq!(message, "a message from the transformation")
879/// ```
880pub struct StaticContextBuilder<
881    N: Node,
882    F: FnMut(&str) -> Result<(), Error>,
883    G: FnMut(&str) -> Result<N, Error>,
884    H: FnMut(&Url) -> Result<String, Error>,
885>(StaticContext<N, F, G, H>);
886
887impl<N: Node, F, G, H> Default for StaticContextBuilder<N, F, G, H>
888where
889    F: FnMut(&str) -> Result<(), Error>,
890    G: FnMut(&str) -> Result<N, Error>,
891    H: FnMut(&Url) -> Result<String, Error>,
892{
893    fn default() -> Self {
894        Self::new()
895    }
896}
897
898impl<N: Node, F, G, H> StaticContextBuilder<N, F, G, H>
899where
900    F: FnMut(&str) -> Result<(), Error>,
901    G: FnMut(&str) -> Result<N, Error>,
902    H: FnMut(&Url) -> Result<String, Error>,
903{
904    pub fn new() -> Self {
905        StaticContextBuilder(StaticContext::new())
906    }
907    pub fn message(mut self, f: F) -> Self {
908        self.0.message = Some(f);
909        self
910    }
911    pub fn parser(mut self, p: G) -> Self {
912        self.0.parser = Some(p);
913        self
914    }
915    pub fn fetcher(mut self, f: H) -> Self {
916        self.0.fetcher = Some(f);
917        self
918    }
919    pub fn build(self) -> StaticContext<N, F, G, H> {
920        self.0
921    }
922}
923
924/// Qualified Name (QName) for security features
925static TRANSFORMNS: LazyLock<Option<NamespaceUri>> = LazyLock::new(|| {
926    Some(
927        NamespaceUri::try_from("http://gitlab.gnome.org/World/Rust/markup-rs/xrust/transform")
928            .unwrap(),
929    )
930});
931static MAXDEPTH_QNAME: LazyLock<QName> = LazyLock::new(|| {
932    QName::new_from_parts(
933        NcName::try_from("maximum-depth").unwrap(),
934        TRANSFORMNS.clone(),
935    )
936});