xee_interpreter/context/
dynamic_context_builder.rs

1use std::{cell::RefCell, ops::Deref, rc::Rc};
2
3use ahash::{HashMap, HashMapExt};
4use iri_string::types::{IriStr, IriString};
5
6use crate::{interpreter, sequence, xml};
7
8use super::{DynamicContext, Variables};
9
10/// A builder for constructing a [`DynamicContext`].
11///
12/// This needs to be supplied a [`StaticContext`] (or a reference to one) in
13/// order to construct it.
14///
15/// You can supply a context item, documents, variables and the like in order
16/// to construct a dynamic context used to execute an XPath instruction.
17#[derive(Debug, Clone)]
18pub struct DynamicContextBuilder<'a> {
19    program: &'a interpreter::Program,
20    context_item: Option<sequence::Item>,
21    documents: DocumentsRef,
22    variables: Variables,
23    current_datetime: chrono::DateTime<chrono::offset::FixedOffset>,
24    default_collection: Option<sequence::Sequence>,
25    collections: HashMap<IriString, sequence::Sequence>,
26    default_uri_collection: Option<sequence::Sequence>,
27    uri_collections: HashMap<IriString, sequence::Sequence>,
28    environment_variables: HashMap<String, String>,
29}
30
31#[derive(Debug, Clone)]
32pub struct DocumentsRef(Rc<RefCell<xml::Documents>>);
33
34impl Deref for DocumentsRef {
35    type Target = RefCell<xml::Documents>;
36
37    fn deref(&self) -> &Self::Target {
38        &self.0
39    }
40}
41
42impl From<xml::Documents> for DocumentsRef {
43    fn from(documents: xml::Documents) -> Self {
44        Self(Rc::new(RefCell::new(documents)))
45    }
46}
47
48impl DocumentsRef {
49    pub fn new() -> Self {
50        Self(Rc::new(RefCell::new(xml::Documents::new())))
51    }
52}
53
54impl Default for DocumentsRef {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60impl<'a> DynamicContextBuilder<'a> {
61    /// Construct a new `DynamicContextBuilder` with the given `StaticContext`.
62    pub(crate) fn new(program: &'a interpreter::Program) -> Self {
63        Self {
64            program,
65            context_item: None,
66            documents: DocumentsRef::new(),
67            variables: Variables::new(),
68            current_datetime: chrono::offset::Local::now().into(),
69            default_collection: None,
70            collections: HashMap::new(),
71            default_uri_collection: None,
72            uri_collections: HashMap::new(),
73            environment_variables: HashMap::new(),
74        }
75    }
76
77    /// Set the context item of the [`DynamicContext`].
78    ///
79    /// Without this, the [`DynamicContext`] will have no context item.
80    pub fn context_item(&mut self, context_item: sequence::Item) -> &mut Self {
81        self.context_item = Some(context_item);
82        self
83    }
84
85    /// Set a node as the context item of the [`DynamicContext`].
86    pub fn context_node(&mut self, node: xot::Node) -> &mut Self {
87        self.context_item(sequence::Item::Node(node));
88        self
89    }
90
91    /// Set the documents of the [`DynamicContext`].
92    ///
93    /// You can give it either owned documents or a [`DocumentsRef`].
94    pub fn documents(&mut self, documents: impl Into<DocumentsRef>) -> &mut Self {
95        self.documents = documents.into();
96        self
97    }
98
99    /// Set the variables of the [`DynamicContext`].
100    ///
101    /// Without this, the [`DynamicContext`] will have no variables.
102    pub fn variables(&mut self, variables: Variables) -> &mut Self {
103        self.variables = variables;
104        self
105    }
106
107    /// Set the current datetime of the [`DynamicContext`].
108    ///
109    /// Without this, the [`DynamicContext`] will have the current datetime.
110    pub fn current_datetime(
111        &mut self,
112        current_datetime: chrono::DateTime<chrono::offset::FixedOffset>,
113    ) -> &mut Self {
114        self.current_datetime = current_datetime;
115        self
116    }
117
118    /// Set the default collection
119    pub fn default_collection(&mut self, sequence: sequence::Sequence) -> &mut Self {
120        self.default_collection = Some(sequence);
121        self
122    }
123
124    /// Set a collection
125    pub fn collection(&mut self, uri: &IriStr, sequence: sequence::Sequence) -> &mut Self {
126        self.collections.insert((*uri).into(), sequence);
127        self
128    }
129
130    /// Set the default URI collection
131    pub fn default_uri_collection(&mut self, uris: &[&IriStr]) -> &mut Self {
132        self.default_uri_collection = Some(Self::uris_into_sequence(uris));
133        self
134    }
135
136    /// Set a URI collection
137    pub fn uri_collection(&mut self, uri: &IriStr, uris: &[&IriStr]) -> &mut Self {
138        self.uri_collections
139            .insert((*uri).into(), Self::uris_into_sequence(uris));
140        self
141    }
142
143    /// Set the environment variables
144    pub fn environment_variables(
145        &mut self,
146        environment_variables: HashMap<String, String>,
147    ) -> &mut Self {
148        self.environment_variables = environment_variables;
149        self
150    }
151
152    /// Initialize the environment variables from the current system environment.
153    pub fn initialize_env(&mut self) -> &mut Self {
154        self.environment_variables = std::env::vars().collect();
155        self
156    }
157
158    fn uris_into_sequence(uris: &[&IriStr]) -> sequence::Sequence {
159        // turn the URIs into a sequence
160        let items: Vec<sequence::Item> = uris
161            .iter()
162            .map(|uri| {
163                let iri_string: IriString = (*uri).into();
164                iri_string.into()
165            })
166            .collect();
167        items.into()
168    }
169
170    /// Build the `DynamicContext`.
171    pub fn build(&self) -> DynamicContext {
172        DynamicContext::new(
173            self.program,
174            self.context_item.clone(),
175            self.documents.clone(),
176            self.variables.clone(),
177            self.current_datetime,
178            self.default_collection.clone(),
179            self.collections.clone(),
180            self.default_uri_collection.clone(),
181            self.uri_collections.clone(),
182            self.environment_variables.clone(),
183        )
184    }
185}