xee_interpreter/context/
static_context.rs

1use std::cell::RefCell;
2use std::fmt::Debug;
3use std::rc::Rc;
4use std::sync::LazyLock;
5
6use iri_string::types::IriAbsoluteStr;
7use iri_string::types::IriAbsoluteString;
8use iri_string::types::IriReferenceStr;
9use xee_name::{Namespaces, VariableNames};
10use xee_xpath_ast::ast;
11use xee_xpath_ast::XPathParserContext;
12
13use crate::error;
14use crate::function;
15use crate::string::{Collation, Collations};
16
17static STATIC_FUNCTIONS: LazyLock<function::StaticFunctions> =
18    LazyLock::new(function::StaticFunctions::new);
19
20// use lazy static to initialize the default collation
21static DEFAULT_COLLATION: LazyLock<IriAbsoluteString> = LazyLock::new(|| {
22    "http://www.w3.org/2005/xpath-functions/collation/codepoint"
23        .try_into()
24        .unwrap()
25});
26
27#[derive(Debug)]
28pub struct StaticContext {
29    parser_context: XPathParserContext,
30    functions: &'static function::StaticFunctions,
31    // TODO: try to make collations static
32    collations: RefCell<Collations>,
33    static_base_uri: Option<IriAbsoluteString>,
34}
35
36impl Default for StaticContext {
37    fn default() -> Self {
38        Self::new(Namespaces::default(), VariableNames::default(), None)
39    }
40}
41
42impl From<XPathParserContext> for StaticContext {
43    fn from(parser_context: XPathParserContext) -> Self {
44        Self {
45            parser_context,
46            functions: &STATIC_FUNCTIONS,
47            collations: RefCell::new(Collations::new()),
48            static_base_uri: None,
49        }
50    }
51}
52
53impl StaticContext {
54    pub(crate) fn new(
55        namespaces: Namespaces,
56        variable_names: VariableNames,
57        static_base_uri: Option<IriAbsoluteString>,
58    ) -> Self {
59        Self {
60            parser_context: XPathParserContext::new(namespaces, variable_names),
61            functions: &STATIC_FUNCTIONS,
62            collations: RefCell::new(Collations::new()),
63            static_base_uri,
64        }
65    }
66
67    pub fn from_namespaces(namespaces: Namespaces) -> Self {
68        Self::new(namespaces, VariableNames::default(), None)
69    }
70
71    pub fn namespaces(&self) -> &Namespaces {
72        &self.parser_context.namespaces
73    }
74
75    pub fn variable_names(&self) -> &VariableNames {
76        &self.parser_context.variable_names
77    }
78
79    pub fn default_collation(&self) -> error::Result<Rc<Collation>> {
80        self.collation(self.default_collation_uri())
81    }
82
83    pub fn default_collation_uri(&self) -> &IriReferenceStr {
84        DEFAULT_COLLATION.as_ref()
85    }
86
87    pub(crate) fn resolve_collation_str(
88        &self,
89        collation: Option<&str>,
90    ) -> error::Result<Rc<Collation>> {
91        let collation: Option<&IriReferenceStr> = if let Some(collation) = collation {
92            collation.try_into().ok()
93        } else {
94            None
95        };
96        self.collation(collation.unwrap_or(self.default_collation_uri()))
97    }
98
99    pub fn static_base_uri(&self) -> Option<&IriAbsoluteStr> {
100        self.static_base_uri.as_deref()
101    }
102
103    pub(crate) fn collation(&self, uri: &IriReferenceStr) -> error::Result<Rc<Collation>> {
104        self.collations
105            .borrow_mut()
106            .load(self.static_base_uri(), uri)
107    }
108
109    /// Given an XPath string, parse into an XPath AST
110    ///
111    /// This uses the namespaces and variable names with which
112    /// this static context has been initialized.
113    pub fn parse_xpath(&self, s: &str) -> Result<ast::XPath, xee_xpath_ast::ParserError> {
114        self.parser_context.parse_xpath(s)
115    }
116
117    /// Parse an XPath string as it would appear in an XSLT value template.
118    /// This means it should have a closing `}` following the xpath expression.
119    pub fn parse_value_template_xpath(
120        &self,
121        s: &str,
122    ) -> Result<ast::XPath, xee_xpath_ast::ParserError> {
123        self.parser_context.parse_value_template_xpath(s)
124    }
125
126    /// Get a static function by id
127    pub fn function_by_id(
128        &self,
129        static_function_id: function::StaticFunctionId,
130    ) -> &function::StaticFunction {
131        self.functions.get_by_index(static_function_id)
132    }
133
134    /// Get a static function by name and arity
135    pub fn function_id_by_name(
136        &self,
137        name: &xot::xmlname::OwnedName,
138        arity: u8,
139    ) -> Option<function::StaticFunctionId> {
140        self.functions.get_by_name(name, arity)
141    }
142
143    /// Get an internal static function by name and arity
144    pub fn function_id_by_internal_name(
145        &self,
146        name: &xot::xmlname::OwnedName,
147        arity: u8,
148    ) -> Option<function::StaticFunctionId> {
149        self.functions.get_by_internal_name(name, arity)
150    }
151}