Skip to main content

ion_rs/types/
struct.rs

1use crate::element::builders::StructBuilder;
2use crate::element::Element;
3use crate::ion_data::{IonDataHash, IonDataOrd, IonEq};
4use crate::symbol_ref::AsSymbolRef;
5use crate::text::text_formatter::FmtValueFormatter;
6use crate::Symbol;
7use smallvec::SmallVec;
8use std::cmp::Ordering;
9use std::collections::{HashMap, VecDeque};
10use std::fmt::{Display, Formatter};
11use std::hash::Hasher;
12
13// A convenient type alias for a vector capable of storing a single `usize` inline
14// without heap allocation. This type should not be used in public interfaces directly.
15type IndexVec = SmallVec<[usize; 1]>;
16
17// This collection is broken out into its own type to allow instances of it to be shared with Arc/Rc.
18#[derive(Debug, Clone)]
19struct Fields {
20    // Key/value pairs in the order they were inserted
21    by_index: Vec<(Symbol, Element)>,
22    // Maps symbols to a list of indexes where values may be found in `by_index` above
23    by_name: HashMap<Symbol, IndexVec>,
24}
25
26impl Fields {
27    /// Gets all of the indexes that contain a value associated with the given field name.
28    fn get_indexes<A: AsSymbolRef>(&self, field_name: A) -> Option<&IndexVec> {
29        field_name
30            .as_symbol_ref()
31            .text()
32            .map(|text| {
33                // If the symbol has defined text, look it up by &str
34                self.by_name.get(text)
35            })
36            .unwrap_or_else(|| {
37                // Otherwise, construct a (cheap, stack-allocated) Symbol with unknown text...
38                let symbol = Symbol::unknown_text();
39                // ...and use the unknown text symbol to look up matching field values
40                self.by_name.get(&symbol)
41            })
42    }
43
44    /// Iterates over the values found at the specified indexes.
45    fn get_values_at_indexes<'a>(&'a self, indexes: &'a IndexVec) -> FieldValuesIterator<'a> {
46        FieldValuesIterator {
47            current: 0,
48            indexes: Some(indexes),
49            by_index: &self.by_index,
50        }
51    }
52
53    /// Gets the last value in the Struct that is associated with the specified field name.
54    ///
55    /// Note that the Ion data model views a struct as a bag of (name, value) pairs and does not
56    /// have a notion of field ordering. In most use cases, field names are distinct and the last
57    /// appearance of a field in the struct's serialized form will have been the _only_ appearance.
58    /// If a field name appears more than once, this method makes the arbitrary decision to return
59    /// the value associated with the last appearance. If your application uses structs that repeat
60    /// field names, you are encouraged to use [`get_all`](Self::get_all) instead.
61    fn get_last<A: AsSymbolRef>(&self, field_name: A) -> Option<&Element> {
62        self.get_indexes(field_name)
63            .and_then(|indexes| indexes.last())
64            .and_then(|index| self.by_index.get(*index))
65            .map(|(_name, value)| value)
66    }
67
68    /// Iterates over all of the values associated with the given field name.
69    fn get_all<A: AsSymbolRef>(&self, field_name: A) -> FieldValuesIterator<'_> {
70        let indexes = self.get_indexes(field_name);
71        FieldValuesIterator {
72            current: 0,
73            indexes,
74            by_index: &self.by_index,
75        }
76    }
77
78    /// Iterates over all of the (field name, field value) pairs in the struct.
79    fn iter(&self) -> impl Iterator<Item = &(Symbol, Element)> {
80        self.by_index.iter()
81    }
82}
83
84/// Iterates over the (field name, field value) pairs in a Struct.
85pub struct FieldIterator<'a> {
86    values: Option<std::slice::Iter<'a, (Symbol, Element)>>,
87}
88
89impl<'a> FieldIterator<'a> {
90    fn new(data: &'a [(Symbol, Element)]) -> Self {
91        FieldIterator {
92            values: Some(data.iter()),
93        }
94    }
95}
96
97impl<'a> Iterator for FieldIterator<'a> {
98    type Item = (&'a Symbol, &'a Element);
99
100    fn next(&mut self) -> Option<Self::Item> {
101        self.values
102            .as_mut()
103            // Get the next &(name, value) and convert it to (&name, &value)
104            .and_then(|iter| iter.next().map(|field| (&field.0, &field.1)))
105    }
106}
107
108/// Iterates over the (field name, field value) pairs in a Struct.
109pub struct OwnedFieldIterator {
110    fields: VecDeque<(Symbol, Element)>,
111}
112
113impl OwnedFieldIterator {
114    fn new(data: Vec<(Symbol, Element)>) -> Self {
115        OwnedFieldIterator {
116            fields: data.into(),
117        }
118    }
119}
120
121impl Iterator for OwnedFieldIterator {
122    type Item = (Symbol, Element);
123
124    fn next(&mut self) -> Option<Self::Item> {
125        self.fields.pop_front()
126    }
127
128    fn size_hint(&self) -> (usize, Option<usize>) {
129        let len = self.fields.len();
130        (len, Some(len))
131    }
132}
133
134/// Iterates over the values associated with a given field name in a Struct.
135pub(crate) struct FieldValuesIterator<'a> {
136    current: usize,
137    indexes: Option<&'a IndexVec>,
138    by_index: &'a Vec<(Symbol, Element)>,
139}
140
141impl<'a> Iterator for FieldValuesIterator<'a> {
142    type Item = &'a Element;
143
144    fn next(&mut self) -> Option<Self::Item> {
145        self.indexes
146            .and_then(|i| i.get(self.current))
147            .and_then(|i| {
148                self.current += 1;
149                self.by_index.get(*i)
150            })
151            .map(|(_name, value)| value)
152    }
153}
154
155/// An in-memory representation of an Ion Struct
156/// ```
157/// use ion_rs::{Element, ion_struct};
158/// # use ion_rs::IonResult;
159/// # fn main() -> IonResult<()> {
160/// let struct_ = ion_struct! {
161///   "foo": 1,
162///   "bar": true,
163///   "baz": "hello"
164/// };
165/// assert_eq!(struct_.len(), 3);
166/// assert_eq!(struct_.get("baz"), Some(&Element::string("hello")));
167/// # Ok(())
168/// # }
169/// ```
170#[derive(Debug, Clone)]
171pub struct Struct {
172    fields: Fields,
173}
174
175impl Display for Struct {
176    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
177        let mut ivf = FmtValueFormatter { output: f };
178        ivf.format_struct(self).map_err(|_| std::fmt::Error)?;
179        Ok(())
180    }
181}
182
183impl Struct {
184    pub fn builder() -> StructBuilder {
185        StructBuilder::new()
186    }
187
188    pub fn clone_builder(&self) -> StructBuilder {
189        StructBuilder::with_initial_fields(&self.fields.by_index)
190    }
191
192    /// Returns an iterator over the field name/value pairs in this Struct.
193    #[allow(clippy::map_identity)]
194    // ^-- This is a temporary workaround for a bug in Clippy that should be fixed in the next release.
195    // See: https://github.com/rust-lang/rust-clippy/issues/9280
196    pub fn fields(&self) -> impl Iterator<Item = (&Symbol, &Element)> {
197        self.fields
198            .iter()
199            // Here we convert from &(name, value) to (&name, &value).
200            // The former makes a stronger assertion about how the data is being stored. We don't
201            // want that to be a mandatory part of the public API.
202            .map(|(name, element)| (name, element))
203    }
204
205    fn fields_eq(&self, other: &Self, eq: impl Fn(&Element, &Element) -> bool) -> bool {
206        if self.fields.by_index.len() != self.fields.by_name.len() {
207            return self.fields_with_repeated_names_eq(other, eq);
208        }
209
210        for (field_name, field_value_indexes) in &self.fields.by_name {
211            let other_value_indexes = match other.fields.get_indexes(field_name) {
212                Some(indexes) => indexes,
213                // The other struct doesn't have a field with this name so they're not equal.
214                None => return false,
215            };
216
217            debug_assert_eq!(field_value_indexes.len(), 1);
218            if other_value_indexes.len() != 1 {
219                // The other struct has fields with the same name, but a different number of them.
220                return false;
221            }
222
223            // Direct indexing is safe here because we've already guaranteed that len() == 1
224            let self_value = &self.fields.by_index[field_value_indexes[0]].1;
225            let other_value = &other.fields.by_index[other_value_indexes[0]].1;
226            if !eq(self_value, other_value) {
227                return false;
228            }
229        }
230
231        true
232    }
233
234    /// Bitvector multiset matching fallback for structs with repeated field names.
235    /// O(p * m^2) where p is the number of unique field names and m is the max repeat count.
236    fn fields_with_repeated_names_eq(
237        &self,
238        other: &Self,
239        eq: impl Fn(&Element, &Element) -> bool,
240    ) -> bool {
241        for (field_name, field_value_indexes) in &self.fields.by_name {
242            let other_value_indexes = match other.fields.get_indexes(field_name) {
243                Some(indexes) => indexes,
244                None => return false,
245            };
246
247            if field_value_indexes.len() != other_value_indexes.len() {
248                return false;
249            }
250
251            let mut matched = vec![false; other_value_indexes.len()];
252            for field_value in self.fields.get_values_at_indexes(field_value_indexes) {
253                let found = other
254                    .fields
255                    .get_values_at_indexes(other_value_indexes)
256                    .enumerate()
257                    .find(|(i, other_value)| !matched[*i] && eq(field_value, other_value));
258                match found {
259                    Some((i, _)) => matched[i] = true,
260                    None => return false,
261                }
262            }
263        }
264
265        true
266    }
267
268    /// Returns the number of fields in this Struct.
269    pub fn len(&self) -> usize {
270        self.fields.by_index.len()
271    }
272
273    /// Returns `true` if this struct has zero fields.
274    pub fn is_empty(&self) -> bool {
275        self.len() == 0
276    }
277
278    pub fn iter(&self) -> FieldIterator<'_> {
279        FieldIterator::new(&self.fields.by_index)
280    }
281
282    /// Returns the value associated with the specified field name.
283    ///
284    /// If more than one field in this struct has that name, this method will return the value of
285    /// the _last_ field with that name. To access other fields with the same name, see
286    /// [`get_all`](Self::get_all).
287    pub fn get<A: AsSymbolRef>(&self, field_name: A) -> Option<&Element> {
288        self.fields.get_last(field_name)
289    }
290
291    /// Returns an iterator over all of the values associated with the specified field name.
292    pub fn get_all<A: AsSymbolRef>(&self, field_name: A) -> impl Iterator<Item = &Element> {
293        self.fields.get_all(field_name)
294    }
295}
296
297// Allows `for (name, value) in &my_struct {...}` syntax
298impl<'a> IntoIterator for &'a Struct {
299    type Item = (&'a Symbol, &'a Element);
300    type IntoIter = FieldIterator<'a>;
301
302    fn into_iter(self) -> Self::IntoIter {
303        self.iter()
304    }
305}
306
307// Allows `for (name, value) in my_struct {...}` syntax
308impl IntoIterator for Struct {
309    type Item = (Symbol, Element);
310    type IntoIter = OwnedFieldIterator;
311
312    fn into_iter(self) -> Self::IntoIter {
313        OwnedFieldIterator::new(self.fields.by_index)
314    }
315}
316
317impl<K, V> FromIterator<(K, V)> for Struct
318where
319    K: Into<Symbol>,
320    V: Into<Element>,
321{
322    /// Returns an owned struct from the given iterator of field names/values.
323    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
324        let mut by_index: Vec<(Symbol, Element)> = Vec::new();
325        let mut by_name: HashMap<Symbol, IndexVec> = HashMap::new();
326        for (field_name, field_value) in iter {
327            let field_name = field_name.into();
328            let field_value = field_value.into();
329
330            by_name
331                .entry(field_name.clone())
332                .or_default()
333                .push(by_index.len());
334            by_index.push((field_name, field_value));
335        }
336
337        let fields = Fields { by_index, by_name };
338        Self { fields }
339    }
340}
341
342impl PartialEq for Struct {
343    fn eq(&self, other: &Self) -> bool {
344        self.len() == other.len() && self.fields_eq(other, |a, b| a == b)
345    }
346}
347
348impl IonEq for Struct {
349    fn ion_eq(&self, other: &Self) -> bool {
350        self.len() == other.len() && self.fields_eq(other, |a, b| a.ion_eq(b))
351    }
352}
353
354impl IonDataOrd for Struct {
355    fn ion_cmp(&self, other: &Self) -> Ordering {
356        let mut these_fields = self.fields.by_index.iter().collect::<Vec<_>>();
357        let mut those_fields = other.fields.by_index.iter().collect::<Vec<_>>();
358        these_fields.sort_by(ion_cmp_field);
359        those_fields.sort_by(ion_cmp_field);
360
361        let mut i0 = these_fields.iter();
362        let mut i1 = those_fields.iter();
363        loop {
364            match [i0.next(), i1.next()] {
365                [None, Some(_)] => return Ordering::Less,
366                [None, None] => return Ordering::Equal,
367                [Some(_), None] => return Ordering::Greater,
368                [Some(a), Some(b)] => {
369                    let ord = ion_cmp_field(a, b);
370                    if ord != Ordering::Equal {
371                        return ord;
372                    }
373                }
374            }
375        }
376    }
377}
378
379fn ion_cmp_field(this: &&(Symbol, Element), that: &&(Symbol, Element)) -> Ordering {
380    let ord = this.0.ion_cmp(&that.0);
381    if !ord.is_eq() {
382        return ord;
383    }
384    IonDataOrd::ion_cmp(&this.1, &that.1)
385}
386
387impl IonDataHash for Struct {
388    fn ion_data_hash<H: Hasher>(&self, state: &mut H) {
389        let mut these_fields = self.fields.by_index.iter().collect::<Vec<_>>();
390        these_fields.sort_by(ion_cmp_field);
391        for (name, value) in these_fields {
392            name.ion_data_hash(state);
393            value.ion_data_hash(state);
394        }
395    }
396}
397
398#[cfg(test)]
399mod tests {
400    use crate::element::Element;
401    use crate::ion_data::IonEq;
402    use crate::ion_struct;
403
404    #[test]
405    fn duplicate_field_values_not_equal() {
406        let s1 = ion_struct! { "a": 1, "a": 1, "a": 2 };
407        let s2 = ion_struct! { "a": 1, "a": 2, "a": 2 };
408        assert_ne!(s1, s2);
409        assert!(!s1.ion_eq(&s2));
410    }
411
412    #[test]
413    fn same_multiset_different_order_is_equal() {
414        let s1 = ion_struct! { "a": 1, "a": 2, "b": 3 };
415        let s2 = ion_struct! { "b": 3, "a": 2, "a": 1 };
416        assert_eq!(s1, s2);
417        assert!(s1.ion_eq(&s2));
418    }
419
420    #[test]
421    fn nan_partial_eq_vs_ion_eq() {
422        let s1 = ion_struct! { "x": f64::NAN };
423        let s2 = ion_struct! { "x": f64::NAN };
424        // PartialEq uses standard f64 semantics: NaN != NaN
425        assert_ne!(s1, s2);
426        // IonEq treats NaN as equivalent
427        assert!(s1.ion_eq(&s2));
428    }
429
430    #[test]
431    fn signed_zero_partial_eq_vs_ion_eq() {
432        let s1 = ion_struct! { "x": 0.0f64 };
433        let s2 = ion_struct! { "x": -0.0f64 };
434        // PartialEq uses standard f64 semantics: 0.0 == -0.0
435        assert_eq!(s1, s2);
436        // IonEq distinguishes signed zeros
437        assert!(!s1.ion_eq(&s2));
438    }
439
440    #[test]
441    fn for_field_in_struct() {
442        // Simple example to exercise Struct's implementation of IntoIterator
443        let s = ion_struct! { "foo": 1, "bar": 2, "baz": 3};
444        let _fields = s.clone().iter().collect::<Vec<_>>(); // exercises `size_hint`
445        let mut baz_value = None;
446        for (name, value) in &s {
447            if *name == "baz" {
448                baz_value = Some(value);
449            }
450        }
451        assert_eq!(baz_value, Some(&Element::int(3)));
452    }
453
454    #[test]
455    fn for_field_in_owned_struct() {
456        // Simple example to exercise Struct's implementation of IntoIterator
457        let s = ion_struct! { "foo": 1, "bar": 2, "baz": 3};
458        let _fields = s.clone().into_iter().collect::<Vec<_>>(); // exercises `size_hint`
459        let mut baz_value = None;
460        for (name, value) in s {
461            if name == "baz" {
462                baz_value = Some(value);
463            }
464        }
465        assert_eq!(baz_value, Some(Element::int(3)));
466    }
467}