cbor_data/validated/
indexing.rs

1use crate::{constants::TAG_CBOR_ITEM, Cbor, CborOwned, ItemKind, TaggedItem, Visitor};
2use std::borrow::Cow;
3
4pub struct IndexVisitor<'a, I: Iterator> {
5    iter: Option<I>,
6    arr_idx: Option<u64>,
7    dict_idx: Option<PathElement<'a>>,
8}
9
10impl<'a, I: Iterator<Item = PathElement<'a>>> IndexVisitor<'a, I> {
11    pub fn new(iter: I) -> Self {
12        Self {
13            iter: Some(iter),
14            arr_idx: None,
15            dict_idx: None,
16        }
17    }
18
19    fn iter(&mut self) -> &mut I {
20        self.iter.as_mut().unwrap()
21    }
22}
23
24impl<'a, 'b, I: Iterator<Item = PathElement<'b>>> Visitor<'a, Option<Cow<'a, Cbor>>>
25    for IndexVisitor<'b, I>
26{
27    fn visit_simple(&mut self, item: TaggedItem<'a>) -> Result<(), Option<Cow<'a, Cbor>>> {
28        if let (Some(TAG_CBOR_ITEM), ItemKind::Bytes(bytes)) = (item.tags().single(), item.kind()) {
29            return if let Some(cbor) = bytes.as_slice() {
30                Cbor::unchecked(cbor).visit(self)
31            } else {
32                let cbor = CborOwned::unchecked(bytes.to_vec());
33                cbor.visit(self)
34                    .map_err(|res| res.map(|cbor| Cow::Owned(cbor.into_owned())))
35            };
36        }
37
38        if self.iter().next().is_some() {
39            Err(None)
40        } else {
41            Err(Some(Cow::Borrowed(item.cbor())))
42        }
43    }
44
45    fn visit_array_begin(
46        &mut self,
47        item: TaggedItem<'a>,
48        size: Option<u64>,
49    ) -> Result<bool, Option<Cow<'a, Cbor>>> {
50        if let Some(idx) = self.iter().next() {
51            let idx = match idx {
52                PathElement::String(_) => return Err(None),
53                PathElement::Number(x) => x,
54                PathElement::Item(_) => return Err(None),
55            };
56            if let Some(size) = size {
57                if size <= idx {
58                    return Err(None);
59                }
60            }
61            self.arr_idx = Some(idx);
62            self.dict_idx = None;
63            Ok(true)
64        } else {
65            Err(Some(Cow::Borrowed(item.cbor())))
66        }
67    }
68
69    fn visit_array_index(
70        &mut self,
71        _item: TaggedItem<'a>,
72        index: u64,
73    ) -> Result<bool, Option<Cow<'a, Cbor>>> {
74        Ok(index == self.arr_idx.unwrap())
75    }
76
77    fn visit_array_end(&mut self, _item: TaggedItem<'a>) -> Result<(), Option<Cow<'a, Cbor>>> {
78        // exhausted the indefinite length array without success
79        Err(None)
80    }
81
82    fn visit_dict_begin(
83        &mut self,
84        item: TaggedItem<'a>,
85        _size: Option<u64>,
86    ) -> Result<bool, Option<Cow<'a, Cbor>>> {
87        if let Some(idx) = self.iter().next() {
88            self.arr_idx = None;
89            self.dict_idx = Some(idx);
90            Ok(true)
91        } else {
92            Err(Some(Cow::Borrowed(item.cbor())))
93        }
94    }
95
96    fn visit_dict_key(
97        &mut self,
98        _item: TaggedItem<'a>,
99        key: TaggedItem<'a>,
100        _is_first: bool,
101    ) -> Result<bool, Option<Cow<'a, Cbor>>> {
102        Ok(match self.dict_idx.as_ref().unwrap() {
103            PathElement::String(idx) => matches!(key.kind(), ItemKind::Str(s) if s == idx),
104            PathElement::Number(idx) => matches!(key.kind(), ItemKind::Pos(p) if p == *idx),
105            PathElement::Item(idx) => &**idx == key.cbor(),
106        })
107    }
108
109    fn visit_dict_end(&mut self, _item: TaggedItem<'a>) -> Result<(), Option<Cow<'a, Cbor>>> {
110        // exhausted the dict without success
111        Err(None)
112    }
113}
114
115/// Path elements for indexing into CBOR structures
116#[derive(Debug, Clone, PartialEq)]
117pub enum PathElement<'a> {
118    /// matches only text string dictionary keys encoded as major type 3
119    String(Cow<'a, str>),
120    /// matches only array indices or numeric dictionary keys encoded as major type 0
121    Number(u64),
122    /// matches only dictionary keys of exactly the byte sequence as this element
123    Item(Cow<'a, Cbor>),
124}
125
126/// Iterator returned by [`index_str`](fn.index_str.html)
127#[derive(Debug, Clone, PartialEq)]
128pub struct IndexStr<'a>(&'a str);
129
130impl<'a> IndexStr<'a> {
131    pub fn new(s: &'a str) -> Option<Self> {
132        let mut test = Self(s);
133        (&mut test).count();
134        if test.0.is_empty() {
135            Some(Self(s))
136        } else {
137            None
138        }
139    }
140}
141
142impl<'a> Iterator for IndexStr<'a> {
143    type Item = PathElement<'a>;
144
145    fn next(&mut self) -> Option<Self::Item> {
146        while self.0.starts_with('.') {
147            self.0 = &self.0[1..];
148        }
149        if self.0.is_empty() {
150            return None;
151        }
152
153        if self.0.starts_with('[') {
154            let end = self.0.find(']')?;
155            let idx: u64 = self.0[1..end].parse().ok()?;
156            self.0 = &self.0[end + 1..];
157            Some(PathElement::Number(idx))
158        } else {
159            let mut pos = self.0.len();
160            for (p, ch) in self.0.char_indices() {
161                if ch == '.' || ch == '[' {
162                    pos = p;
163                    break;
164                }
165            }
166            let ret = PathElement::String(Cow::Borrowed(&self.0[..pos]));
167            self.0 = &self.0[pos..];
168            Some(ret)
169        }
170    }
171}