ion_rs/types/
struct.rs

1use crate::element::builders::StructBuilder;
2use crate::element::Element;
3use crate::ion_data::{IonEq, IonOrd};
4use crate::symbol_ref::AsSymbolRef;
5use crate::text::text_formatter::IonValueFormatter;
6use crate::Symbol;
7use smallvec::SmallVec;
8use std::cmp::Ordering;
9use std::collections::HashMap;
10use std::fmt::{Display, Formatter};
11
12// A convenient type alias for a vector capable of storing a single `usize` inline
13// without heap allocation. This type should not be used in public interfaces directly.
14type IndexVec = SmallVec<[usize; 1]>;
15
16// This collection is broken out into its own type to allow instances of it to be shared with Arc/Rc.
17#[derive(Debug, Clone)]
18struct Fields {
19    // Key/value pairs in the order they were inserted
20    by_index: Vec<(Symbol, Element)>,
21    // Maps symbols to a list of indexes where values may be found in `by_index` above
22    by_name: HashMap<Symbol, IndexVec>,
23}
24
25impl Fields {
26    /// Gets all of the indexes that contain a value associated with the given field name.
27    fn get_indexes<A: AsSymbolRef>(&self, field_name: A) -> Option<&IndexVec> {
28        field_name
29            .as_symbol_ref()
30            .text()
31            .map(|text| {
32                // If the symbol has defined text, look it up by &str
33                self.by_name.get(text)
34            })
35            .unwrap_or_else(|| {
36                // Otherwise, construct a (cheap, stack-allocated) Symbol with unknown text...
37                let symbol = Symbol::unknown_text();
38                // ...and use the unknown text symbol to look up matching field values
39                self.by_name.get(&symbol)
40            })
41    }
42
43    /// Iterates over the values found at the specified indexes.
44    fn get_values_at_indexes<'a>(&'a self, indexes: &'a IndexVec) -> FieldValuesIterator<'a> {
45        FieldValuesIterator {
46            current: 0,
47            indexes: Some(indexes),
48            by_index: &self.by_index,
49        }
50    }
51
52    /// Gets the last value in the Struct that is associated with the specified field name.
53    ///
54    /// Note that the Ion data model views a struct as a bag of (name, value) pairs and does not
55    /// have a notion of field ordering. In most use cases, field names are distinct and the last
56    /// appearance of a field in the struct's serialized form will have been the _only_ appearance.
57    /// If a field name appears more than once, this method makes the arbitrary decision to return
58    /// the value associated with the last appearance. If your application uses structs that repeat
59    /// field names, you are encouraged to use [`get_all`](Self::get_all) instead.
60    fn get_last<A: AsSymbolRef>(&self, field_name: A) -> Option<&Element> {
61        self.get_indexes(field_name)
62            .and_then(|indexes| indexes.last())
63            .and_then(|index| self.by_index.get(*index))
64            .map(|(_name, value)| value)
65    }
66
67    /// Iterates over all of the values associated with the given field name.
68    fn get_all<A: AsSymbolRef>(&self, field_name: A) -> FieldValuesIterator {
69        let indexes = self.get_indexes(field_name);
70        FieldValuesIterator {
71            current: 0,
72            indexes,
73            by_index: &self.by_index,
74        }
75    }
76
77    /// Iterates over all of the (field name, field value) pairs in the struct.
78    fn iter(&self) -> impl Iterator<Item = &(Symbol, Element)> {
79        self.by_index.iter()
80    }
81}
82
83/// Iterates over the (field name, field value) pairs in a Struct.
84pub struct FieldIterator<'a> {
85    values: Option<std::slice::Iter<'a, (Symbol, Element)>>,
86}
87
88impl<'a> FieldIterator<'a> {
89    fn new(data: &'a [(Symbol, Element)]) -> Self {
90        FieldIterator {
91            values: Some(data.iter()),
92        }
93    }
94
95    fn empty() -> FieldIterator<'static> {
96        FieldIterator { values: None }
97    }
98}
99
100impl<'a> Iterator for FieldIterator<'a> {
101    type Item = (&'a Symbol, &'a Element);
102
103    fn next(&mut self) -> Option<Self::Item> {
104        self.values
105            .as_mut()
106            // Get the next &(name, value) and convert it to (&name, &value)
107            .and_then(|iter| iter.next().map(|field| (&field.0, &field.1)))
108    }
109}
110
111/// Iterates over the values associated with a given field name in a Struct.
112pub struct FieldValuesIterator<'a> {
113    current: usize,
114    indexes: Option<&'a IndexVec>,
115    by_index: &'a Vec<(Symbol, Element)>,
116}
117
118impl<'a> Iterator for FieldValuesIterator<'a> {
119    type Item = &'a Element;
120
121    fn next(&mut self) -> Option<Self::Item> {
122        self.indexes
123            .and_then(|i| i.get(self.current))
124            .and_then(|i| {
125                self.current += 1;
126                self.by_index.get(*i)
127            })
128            .map(|(_name, value)| value)
129    }
130}
131
132/// An in-memory representation of an Ion Struct
133/// ```
134/// use ion_rs::element::Element;
135/// use ion_rs::ion_struct;
136/// # use ion_rs::IonResult;
137/// # fn main() -> IonResult<()> {
138/// let struct_ = ion_struct! {
139///   "foo": 1,
140///   "bar": true,
141///   "baz": "hello"
142/// };
143/// assert_eq!(struct_.len(), 3);
144/// assert_eq!(struct_.get("baz"), Some(&Element::string("hello")));
145/// # Ok(())
146/// # }
147/// ```
148#[derive(Debug, Clone)]
149pub struct Struct {
150    fields: Fields,
151}
152
153impl Display for Struct {
154    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
155        let mut ivf = IonValueFormatter { output: f };
156        ivf.format_struct(self).map_err(|_| std::fmt::Error)?;
157        Ok(())
158    }
159}
160
161impl Struct {
162    pub fn builder() -> StructBuilder {
163        StructBuilder::new()
164    }
165
166    pub fn clone_builder(&self) -> StructBuilder {
167        StructBuilder::with_initial_fields(&self.fields.by_index)
168    }
169
170    /// Returns an iterator over the field name/value pairs in this Struct.
171    pub fn fields(&self) -> impl Iterator<Item = (&Symbol, &Element)> {
172        self.fields
173            .iter()
174            // Here we convert from &(name, value) to (&name, &value).
175            // The former makes a stronger assertion about how the data is being stored. We don't
176            // want that to be a mandatory part of the public API.
177            .map(|(name, element)| (name, element))
178    }
179
180    fn fields_eq(&self, other: &Self) -> bool {
181        // For each field name in `self`, get the list of indexes that contain a value with that name.
182        for (field_name, field_value_indexes) in &self.fields.by_name {
183            let other_value_indexes = match other.fields.get_indexes(field_name) {
184                Some(indexes) => indexes,
185                // The other struct doesn't have a field with this name so they're not equal.
186                None => return false,
187            };
188
189            if field_value_indexes.len() != other_value_indexes.len() {
190                // The other struct has fields with the same name, but a different number of them.
191                return false;
192            }
193
194            for field_value in self.fields.get_values_at_indexes(field_value_indexes) {
195                if other
196                    .fields
197                    .get_values_at_indexes(other_value_indexes)
198                    .all(|other_value| !field_value.ion_eq(other_value))
199                {
200                    // Couldn't find an equivalent field in the other struct
201                    return false;
202                }
203            }
204        }
205
206        // If all of the above conditions hold, the two structs are equal.
207        true
208    }
209
210    /// Returns the number of fields in this Struct.
211    pub fn len(&self) -> usize {
212        self.fields.by_index.len()
213    }
214
215    /// Returns `true` if this struct has zero fields.
216    pub fn is_empty(&self) -> bool {
217        self.len() == 0
218    }
219
220    pub fn iter(&self) -> FieldIterator<'_> {
221        FieldIterator::new(&self.fields.by_index)
222    }
223
224    pub fn get<A: AsSymbolRef>(&self, field_name: A) -> Option<&Element> {
225        self.fields.get_last(field_name)
226    }
227
228    pub fn get_all<A: AsSymbolRef>(&self, field_name: A) -> FieldValuesIterator<'_> {
229        self.fields.get_all(field_name)
230    }
231}
232
233// Allows `for (name, value) in &my_struct {...}` syntax
234impl<'a> IntoIterator for &'a Struct {
235    type Item = (&'a Symbol, &'a Element);
236    type IntoIter = FieldIterator<'a>;
237
238    fn into_iter(self) -> Self::IntoIter {
239        self.iter()
240    }
241}
242
243impl<K, V> FromIterator<(K, V)> for Struct
244where
245    K: Into<Symbol>,
246    V: Into<Element>,
247{
248    /// Returns an owned struct from the given iterator of field names/values.
249    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
250        let mut by_index: Vec<(Symbol, Element)> = Vec::new();
251        let mut by_name: HashMap<Symbol, IndexVec> = HashMap::new();
252        for (field_name, field_value) in iter {
253            let field_name = field_name.into();
254            let field_value = field_value.into();
255
256            by_name
257                .entry(field_name.clone())
258                .or_insert_with(IndexVec::new)
259                .push(by_index.len());
260            by_index.push((field_name, field_value));
261        }
262
263        let fields = Fields { by_index, by_name };
264        Self { fields }
265    }
266}
267
268impl PartialEq for Struct {
269    fn eq(&self, other: &Self) -> bool {
270        // check if both fields have same length
271        self.len() == other.len()
272            // we need to test equality in both directions for both fields
273            // A good example for this is annotated vs not annotated values in struct
274            //  { a:4, a:4 } vs. { a:4, a:a::4 } // returns true
275            //  { a:4, a:a::4 } vs. { a:4, a:4 } // returns false
276            && self.fields_eq(other) && other.fields_eq(self)
277    }
278}
279
280impl Eq for Struct {}
281
282impl IonEq for Struct {
283    fn ion_eq(&self, other: &Self) -> bool {
284        self == other
285    }
286}
287
288impl IonOrd for Struct {
289    fn ion_cmp(&self, other: &Self) -> Ordering {
290        let mut these_fields = self.fields.by_index.iter().collect::<Vec<_>>();
291        let mut those_fields = other.fields.by_index.iter().collect::<Vec<_>>();
292        these_fields.sort_by(ion_cmp_field);
293        those_fields.sort_by(ion_cmp_field);
294
295        let mut i0 = these_fields.iter();
296        let mut i1 = those_fields.iter();
297        loop {
298            match [i0.next(), i1.next()] {
299                [None, Some(_)] => return Ordering::Less,
300                [None, None] => return Ordering::Equal,
301                [Some(_), None] => return Ordering::Greater,
302                [Some(a), Some(b)] => {
303                    let ord = ion_cmp_field(a, b);
304                    if ord != Ordering::Equal {
305                        return ord;
306                    }
307                }
308            }
309        }
310    }
311}
312
313fn ion_cmp_field(this: &&(Symbol, Element), that: &&(Symbol, Element)) -> Ordering {
314    let ord = this.0.ion_cmp(&that.0);
315    if !ord.is_eq() {
316        return ord;
317    }
318    IonOrd::ion_cmp(&this.1, &that.1)
319}
320
321#[cfg(test)]
322mod tests {
323    use crate::element::Element;
324    use crate::ion_struct;
325
326    #[test]
327    fn for_field_in_struct() {
328        // Simple example to exercise List's implementation of IntoIterator
329        let s = ion_struct! { "foo": 1, "bar": 2, "baz": 3};
330        let mut baz_value = None;
331        for (name, value) in &s {
332            if *name == "baz" {
333                baz_value = Some(value);
334            }
335        }
336        assert_eq!(baz_value, Some(&Element::integer(3)));
337    }
338}