xee_interpreter/sequence/
item.rs

1use xot::Xot;
2
3use crate::atomic;
4use crate::context;
5use crate::error;
6use crate::function;
7
8/// An XPath item. These are the items that make up an XPath sequence.
9#[derive(Debug, Clone, PartialEq)]
10pub enum Item {
11    /// An atomic value.
12    ///
13    /// One of the value types defined by XPath, indicated by an `xs:*` type
14    /// such as `xs:integer` or `xs:string`.
15    Atomic(atomic::Atomic),
16    /// A node in an XML document.
17    ///
18    /// This is defined using the [`xot`] library.
19    Node(xot::Node),
20    /// An XPath function type.
21    Function(function::Function),
22}
23
24// a static assertion to ensure that Item never grows in size
25#[cfg(target_arch = "x86_64")]
26static_assertions::assert_eq_size!(Item, [u8; 24]);
27
28impl Item {
29    /// Try to get the atomic value of the item.
30    pub fn to_atomic(&self) -> error::Result<atomic::Atomic> {
31        match self {
32            Item::Atomic(a) => Ok(a.clone()),
33            _ => Err(error::Error::XPTY0004),
34        }
35    }
36
37    /// Try to get the node value of the item.
38    pub fn to_node(&self) -> error::Result<xot::Node> {
39        match self {
40            Item::Node(n) => Ok(*n),
41            _ => Err(error::Error::XPTY0004),
42        }
43    }
44
45    /// Try to get the function value of the item.
46    pub fn to_function(&self) -> error::Result<function::Function> {
47        match self {
48            Item::Function(f) => Ok(f.clone()),
49            _ => Err(error::Error::XPTY0004),
50        }
51    }
52
53    /// Try to get the value as an XPath Map.
54    pub fn to_map(&self) -> error::Result<function::Map> {
55        if let Item::Function(function::Function::Map(map)) = self {
56            Ok(map.clone())
57        } else {
58            Err(error::Error::XPTY0004)
59        }
60    }
61
62    /// Try to get the value as an XPath Array.
63    pub fn to_array(&self) -> error::Result<function::Array> {
64        if let Item::Function(function::Function::Array(array)) = self {
65            Ok(array.clone())
66        } else {
67            Err(error::Error::XPTY0004)
68        }
69    }
70
71    /// Obtain the [effective boolean
72    /// value](https://www.w3.org/TR/xpath-31/#id-ebv) of the item.
73    ///
74    /// - If the item is a node, it's true.
75    ///
76    /// - If the item is a boolean, it's the value of the boolean.
77    ///
78    /// - If the item is a string, it's false if it's empty, otherwise true.
79    ///
80    /// - If the item is a numeric type, it's false if it's NaN or zero,
81    ///   otherwise true.
82    ///
83    /// - Functions are always errors.
84    pub fn effective_boolean_value(&self) -> error::Result<bool> {
85        match self {
86            Item::Atomic(a) => a.effective_boolean_value(),
87            // If its operand is a sequence whose first item is a node, fn:boolean returns true;
88            // this is the case when a single node is on the stack, just like if it
89            // were in a sequence.
90            Item::Node(_) => Ok(true),
91            Item::Function(_) => Err(error::Error::FORG0006),
92        }
93    }
94
95    /// Convert an atomic value into a value of type `V`.
96    pub fn try_into_value<V>(&self) -> error::Result<V>
97    where
98        V: TryFrom<atomic::Atomic, Error = error::Error>,
99    {
100        match self {
101            Item::Atomic(a) => a.clone().try_into(),
102            // atomic::Atomic::try_from(a.clone()),
103            _ => Err(error::Error::XPTY0004),
104        }
105    }
106
107    /// Construct the string value.
108    ///
109    /// - For an atomic value, it casts it to a string using the canonical
110    ///   lexical representation rules as defined by XML Schema.
111    ///
112    /// - For a node, it returns the [string value of the
113    ///   node](https://www.w3.org/TR/xpath-31/#id-typed-value).
114    ///
115    /// - For a function, it errors.
116    pub fn string_value(&self, xot: &Xot) -> error::Result<String> {
117        match self {
118            Item::Atomic(atomic) => Ok(atomic.string_value()),
119            Item::Node(node) => Ok(xot.string_value(*node)),
120            Item::Function(_) => Err(error::Error::FOTY0014),
121        }
122    }
123
124    /// Display representation of an item
125    /// For atomics this is a true parseable XPath representation.
126    /// For node and function that does not exist, so we generate a plausible
127    /// version for display purposes only.
128    pub fn display_representation(
129        &self,
130        xot: &Xot,
131        context: &context::DynamicContext,
132    ) -> error::Result<String> {
133        match self {
134            Item::Atomic(atomic) => Ok(atomic.xpath_representation()),
135            Item::Node(node) => node_display_representation(*node, xot),
136            Item::Function(function) => Ok(function.display_representation(xot, context)),
137        }
138    }
139
140    /// Check whether this item is represents an XPath Map.
141    pub(crate) fn is_map(&self) -> bool {
142        match self {
143            Item::Function(function) => matches!(function, function::Function::Map(_)),
144            _ => false,
145        }
146    }
147
148    /// Check whether this item is represents an XPath Array.
149    pub(crate) fn is_array(&self) -> bool {
150        match self {
151            Item::Function(function) => matches!(function, function::Function::Array(_)),
152            _ => false,
153        }
154    }
155}
156
157fn node_display_representation(node: xot::Node, xot: &Xot) -> error::Result<String> {
158    match xot.value(node) {
159        xot::Value::Attribute(attribute) => {
160            let value = attribute.value();
161            let (name, namespace) = xot.name_ns_str(attribute.name());
162            let name = if !namespace.is_empty() {
163                format!("Q{{{}}}{}", namespace, name)
164            } else {
165                name.to_string()
166            };
167            Ok(format!("Attribute {}=\"{}\"", name, value))
168        }
169        xot::Value::Namespace(namespace) => {
170            let prefix_id = namespace.prefix();
171            let namespace_id = namespace.namespace();
172            let prefix_str = xot.prefix_str(prefix_id);
173            let namespace_str = xot.namespace_str(namespace_id);
174            Ok(format!("Namespace {}:{}", prefix_str, namespace_str))
175        }
176        xot::Value::Text(text) => Ok(format!("Text \"{}\"", text.get())),
177        // for everything else we can just serialize the node
178        _ => Ok(xot.serialize_xml_string(
179            {
180                xot::output::xml::Parameters {
181                    indentation: Default::default(),
182                    ..Default::default()
183                }
184            },
185            node,
186        )?),
187    }
188}
189
190impl<T> From<T> for Item
191where
192    T: Into<atomic::Atomic>,
193{
194    fn from(a: T) -> Self {
195        Self::Atomic(a.into())
196    }
197}
198
199impl TryFrom<Item> for atomic::Atomic {
200    type Error = error::Error;
201
202    fn try_from(item: Item) -> error::Result<atomic::Atomic> {
203        match item {
204            Item::Atomic(a) => Ok(a),
205            _ => Err(error::Error::XPTY0004),
206        }
207    }
208}
209
210impl TryFrom<&Item> for atomic::Atomic {
211    type Error = error::Error;
212
213    fn try_from(item: &Item) -> error::Result<atomic::Atomic> {
214        match item {
215            Item::Atomic(a) => Ok(a.clone()),
216            _ => Err(error::Error::XPTY0004),
217        }
218    }
219}
220
221impl From<xot::Node> for Item {
222    fn from(node: xot::Node) -> Self {
223        Self::Node(node)
224    }
225}
226
227impl TryFrom<Item> for xot::Node {
228    type Error = error::Error;
229
230    fn try_from(item: Item) -> error::Result<Self> {
231        match item {
232            Item::Node(node) => Ok(node),
233            _ => Err(error::Error::XPTY0004),
234        }
235    }
236}
237
238impl TryFrom<&Item> for xot::Node {
239    type Error = error::Error;
240
241    fn try_from(item: &Item) -> error::Result<Self> {
242        match item {
243            Item::Node(node) => Ok(*node),
244            _ => Err(error::Error::XPTY0004),
245        }
246    }
247}
248
249impl TryFrom<Item> for function::Function {
250    type Error = error::Error;
251
252    fn try_from(item: Item) -> error::Result<Self> {
253        match item {
254            Item::Function(f) => Ok(f.clone()),
255            _ => Err(error::Error::XPTY0004),
256        }
257    }
258}
259
260impl TryFrom<&Item> for function::Function {
261    type Error = error::Error;
262
263    fn try_from(item: &Item) -> error::Result<Self> {
264        match item {
265            Item::Function(f) => Ok(f.clone()),
266            _ => Err(error::Error::XPTY0004),
267        }
268    }
269}
270
271impl From<function::Function> for Item {
272    fn from(f: function::Function) -> Self {
273        Self::Function(f)
274    }
275}
276
277impl From<function::Array> for Item {
278    fn from(array: function::Array) -> Self {
279        Self::Function(function::Function::Array(array))
280    }
281}
282
283impl From<function::Map> for Item {
284    fn from(map: function::Map) -> Self {
285        Self::Function(function::Function::Map(map))
286    }
287}
288
289pub enum AtomizedItemIter<'a> {
290    Atomic(std::iter::Once<atomic::Atomic>),
291    Node(std::iter::Once<atomic::Atomic>),
292    Array(AtomizedArrayIter<'a>),
293    // TODO: properly handle functions; for now they error
294    Erroring(std::iter::Once<error::Result<atomic::Atomic>>),
295}
296
297impl<'a> AtomizedItemIter<'a> {
298    pub(crate) fn new(item: &'a Item, xot: &'a Xot) -> Self {
299        match item {
300            Item::Atomic(a) => Self::Atomic(std::iter::once(a.clone())),
301            Item::Node(n) => {
302                let s = xot.string_value(*n);
303                let value = atomic::Atomic::Untyped(s.into());
304                Self::Node(std::iter::once(value))
305            }
306            Item::Function(function) => match function {
307                function::Function::Array(a) => Self::Array(AtomizedArrayIter::new(a, xot)),
308                _ => Self::Erroring(std::iter::once(Err(error::Error::FOTY0013))),
309            },
310        }
311    }
312}
313
314impl Iterator for AtomizedItemIter<'_> {
315    type Item = error::Result<atomic::Atomic>;
316
317    fn next(&mut self) -> Option<Self::Item> {
318        match self {
319            Self::Atomic(iter) => iter.next().map(Ok),
320            Self::Node(iter) => iter.next().map(Ok),
321            Self::Array(iter) => iter.next(),
322            Self::Erroring(iter) => iter.next(),
323        }
324    }
325
326    fn size_hint(&self) -> (usize, Option<usize>) {
327        match self {
328            Self::Atomic(iter) => iter.size_hint(),
329            Self::Node(iter) => iter.size_hint(),
330            Self::Array(iter) => iter.size_hint(),
331            Self::Erroring(iter) => iter.size_hint(),
332        }
333    }
334}
335
336// This complex atomized node iter is overkill, until such time when
337// a node can deliver more than atomic value.
338// #[derive(Debug, Clone)]
339// pub struct AtomizedNodeIter {
340//     typed_value: Vec<atomic::Atomic>,
341//     typed_value_index: usize,
342// }
343
344// impl AtomizedNodeIter {
345//     fn new(node: xot::Node, xot: &Xot) -> Self {
346//         Self {
347//             typed_value: typed_value(xot, node),
348//             typed_value_index: 0,
349//         }
350//     }
351// }
352
353// fn typed_value(xot: &Xot, node: xot::Node) -> Vec<atomic::Atomic> {
354//     // for now we don't know any types of nodes yet; everything is untyped
355//     let s = xot.string_value(node);
356//     vec![atomic::Atomic::Untyped(s.into())]
357// }
358
359// impl Iterator for AtomizedNodeIter {
360//     type Item = atomic::Atomic;
361
362//     fn next(&mut self) -> Option<Self::Item> {
363//         if self.typed_value_index < self.typed_value.len() {
364//             let item = self.typed_value[self.typed_value_index].clone();
365//             self.typed_value_index += 1;
366//             Some(item)
367//         } else {
368//             None
369//         }
370//     }
371
372//     fn size_hint(&self) -> (usize, Option<usize>) {
373//         let remaining = self.typed_value.len() - self.typed_value_index;
374//         (remaining, Some(remaining))
375//     }
376// }
377
378pub struct AtomizedArrayIter<'a> {
379    xot: &'a Xot,
380    array: &'a function::Array,
381    array_index: usize,
382    iter: Option<Box<dyn Iterator<Item = error::Result<atomic::Atomic>> + 'a>>,
383}
384
385impl<'a> AtomizedArrayIter<'a> {
386    fn new(array: &'a function::Array, xot: &'a Xot) -> Self {
387        Self {
388            xot,
389            array,
390            array_index: 0,
391            iter: None,
392        }
393    }
394}
395
396impl Iterator for AtomizedArrayIter<'_> {
397    type Item = error::Result<atomic::Atomic>;
398
399    fn next(&mut self) -> Option<Self::Item> {
400        loop {
401            // if there there are any more atoms in this array entry,
402            // supply those
403            if let Some(iter) = &mut self.iter {
404                if let Some(item) = iter.next() {
405                    return Some(item);
406                } else {
407                    self.iter = None;
408                }
409            }
410            let array = &self.array.0;
411            // if we're at the end of the array, we're done
412            if self.array_index >= array.len() {
413                return None;
414            }
415            let sequence = &array[self.array_index];
416            self.array_index += 1;
417
418            self.iter = Some(Box::new(sequence.atomized(self.xot)));
419        }
420    }
421
422    fn size_hint(&self) -> (usize, Option<usize>) {
423        // a very low lower bound, but it's the best we can do
424        // we could potentially improve this by adding all the lens of the sequences
425        // and then subtracing the lens of the sequences we have seen so far, but
426        // this is a more involved calculation that touches a lot of places in memory
427        let remaining = self.array.0.len() - self.array_index;
428        (remaining, None)
429    }
430}