xee_interpreter/context/
dynamic_context.rs

1use ahash::{AHashMap, HashMap};
2use iri_string::types::{IriStr, IriString};
3use std::fmt::Debug;
4
5use crate::function::{self, Function};
6use crate::{error::Error, interpreter::Program};
7use crate::{interpreter, sequence};
8
9use super::{DocumentsRef, StaticContext};
10
11/// A map of variables
12///
13/// These are variables to be passed into an XPath evaluation.
14///
15/// The key is the name of a variable, and the value is an item.
16pub type Variables = AHashMap<xot::xmlname::OwnedName, sequence::Sequence>;
17
18// a dynamic context is created for each xpath evaluation
19#[derive(Debug)]
20pub struct DynamicContext<'a> {
21    // we keep a reference to the program
22    program: &'a Program,
23
24    /// An optional context item
25    context_item: Option<sequence::Item>,
26    // we want to mutate documents during evaluation, and this happens in
27    // multiple spots. We use RefCell to manage that during runtime so we don't
28    // need to make the whole thing immutable.
29    documents: DocumentsRef,
30    variables: Variables,
31    // TODO: we want to be able to control the creation of this outside,
32    // as it needs to be the same for all evalutions of XSLT I believe
33    current_datetime: chrono::DateTime<chrono::offset::FixedOffset>,
34    // default collection
35    default_collection: Option<sequence::Sequence>,
36    // collections
37    collections: HashMap<IriString, sequence::Sequence>,
38    // default uri collection
39    default_uri_collection: Option<sequence::Sequence>,
40    // uri collections
41    uri_collections: HashMap<IriString, sequence::Sequence>,
42    // environment variables
43    environment_variables: HashMap<String, String>,
44}
45
46impl<'a> DynamicContext<'a> {
47    #[allow(clippy::too_many_arguments)]
48    pub(crate) fn new(
49        program: &'a Program,
50        context_item: Option<sequence::Item>,
51        documents: DocumentsRef,
52        variables: Variables,
53        current_datetime: chrono::DateTime<chrono::offset::FixedOffset>,
54        default_collection: Option<sequence::Sequence>,
55        collections: HashMap<IriString, sequence::Sequence>,
56        default_uri_collection: Option<sequence::Sequence>,
57        uri_collections: HashMap<IriString, sequence::Sequence>,
58        environment_variables: HashMap<String, String>,
59    ) -> Self {
60        Self {
61            program,
62            context_item,
63            documents,
64            variables,
65            current_datetime,
66            default_collection,
67            collections,
68            default_uri_collection,
69            uri_collections,
70            environment_variables,
71        }
72    }
73
74    /// The static context of the program.
75    pub fn static_context(&self) -> &StaticContext {
76        self.program.static_context()
77    }
78
79    /// Access the context item, if any.
80    pub fn context_item(&self) -> Option<&sequence::Item> {
81        self.context_item.as_ref()
82    }
83
84    /// The documents in this context.
85    pub fn documents(&self) -> DocumentsRef {
86        self.documents.clone()
87    }
88
89    /// The variables in this context.
90    pub fn variables(&self) -> &Variables {
91        &self.variables
92    }
93
94    /// Access the default collection
95    pub fn default_collection(&self) -> Option<&sequence::Sequence> {
96        self.default_collection.as_ref()
97    }
98
99    /// Access a collection by URI
100    pub fn collection(&self, uri: &IriStr) -> Option<&sequence::Sequence> {
101        self.collections.get(uri)
102    }
103
104    /// Access the default URI collection
105    pub fn default_uri_collection(&self) -> Option<&sequence::Sequence> {
106        self.default_uri_collection.as_ref()
107    }
108
109    /// Access a URI collection by URI
110    ///
111    /// Note that the URI does not have to be a proper URI as the specification
112    /// defines it as an xs:string
113    pub fn uri_collection(&self, uri: &IriStr) -> Option<&sequence::Sequence> {
114        self.uri_collections.get(uri)
115    }
116
117    /// Access an environment variable by name
118    pub fn environment_variable(&self, name: &str) -> Option<&str> {
119        self.environment_variables.get(name).map(String::as_str)
120    }
121
122    /// Access all environment variable names
123    pub fn environment_variable_names(&self) -> impl Iterator<Item = &str> {
124        self.environment_variables.keys().map(String::as_str)
125    }
126
127    pub(crate) fn arguments(&self) -> Result<Vec<sequence::Sequence>, Error> {
128        let mut arguments = Vec::new();
129        for variable_name in self.static_context().variable_names() {
130            let items = self.variables.get(variable_name).ok_or(Error::XPDY0002)?;
131            arguments.push(items.clone());
132        }
133        Ok(arguments)
134    }
135
136    fn create_current_datetime() -> chrono::DateTime<chrono::offset::FixedOffset> {
137        chrono::offset::Local::now().into()
138    }
139
140    pub(crate) fn current_datetime(&self) -> chrono::DateTime<chrono::offset::FixedOffset> {
141        self.current_datetime
142    }
143
144    pub fn implicit_timezone(&self) -> chrono::FixedOffset {
145        self.current_datetime.timezone()
146    }
147
148    /// Access information about a Function.
149    pub fn function_info<'b>(&self, function: &'b Function) -> interpreter::FunctionInfo<'a, 'b> {
150        self.program.function_info(function)
151    }
152
153    pub(crate) fn static_function_by_id(
154        &self,
155        id: function::StaticFunctionId,
156    ) -> &function::StaticFunction {
157        self.program.static_context().function_by_id(id)
158    }
159
160    pub(crate) fn inline_function_by_id(
161        &self,
162        id: function::InlineFunctionId,
163    ) -> &function::InlineFunction {
164        self.program.inline_function(id)
165    }
166}