ion_rs/element/annotations.rs
1use crate::element::iterators::{AnnotationsIntoIter, SymbolsIterator};
2use crate::ion_data::{IonDataHash, IonDataOrd};
3use crate::Symbol;
4use std::cmp::Ordering;
5use std::hash::Hasher;
6
7/// An ordered sequence of symbols that convey additional, application-specific information about
8/// their associated Ion value.
9///
10/// The [`IntoAnnotations`] trait is a convenient way to convert collections of symbol convertible
11/// things (including [`&str`] and [`String`]) into this sequence.
12///
13/// ```
14/// use ion_rs::{Annotations, IntoAnnotations};
15/// let annotations: Annotations = ["foo", "bar", "baz"].into_annotations();
16/// for annotation in &annotations {
17/// assert_eq!(annotation.text().map(|s| s.len()), Some(3));
18/// }
19/// ```
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct Annotations {
22 symbols: Vec<Symbol>,
23}
24
25impl Annotations {
26 // This is limited to crate visibility to allow us to change `Vec<_>` to something
27 // else (`thinvec`?) in the future. Users are unlikely to need to construct an `Annotations`
28 // themselves, but can use a `From` impl if necessary.
29 pub(crate) fn new(symbols: Vec<Symbol>) -> Self {
30 Annotations { symbols }
31 }
32
33 /// Constructs an Annotations object representing an empty symbol sequence
34 pub fn empty() -> Self {
35 Annotations {
36 symbols: Vec::new(),
37 }
38 }
39
40 /// Returns an [`Iterator`] that yields each of the [`Symbol`]s in this annotations
41 /// sequence in order.
42 pub fn iter(&self) -> SymbolsIterator<'_> {
43 SymbolsIterator::new(self.symbols.as_slice())
44 }
45
46 /// Returns the number of annotations in this sequence.
47 /// ```
48 /// use ion_rs::{Annotations, IntoAnnotations};
49 /// let annotations: Annotations = ["foo", "bar", "baz"].into_annotations();
50 /// assert_eq!(annotations.len(), 3);
51 /// ```
52 pub fn len(&self) -> usize {
53 self.symbols.len()
54 }
55
56 /// Returns `true` if this sequence contains zero annotations. Otherwise, returns `false`.
57 /// ```
58 /// use ion_rs::{Annotations, IntoAnnotations};
59 /// let annotations: Annotations = ["foo", "bar", "baz"].into_annotations();
60 /// assert!(!annotations.is_empty());
61 /// ```
62 pub fn is_empty(&self) -> bool {
63 self.symbols.len() == 0
64 }
65
66 /// Returns `true` if any symbol in this annotations sequence is equal to the provided text.
67 /// Otherwise, returns `false`.
68 /// ```
69 /// use ion_rs::{Annotations, IntoAnnotations};
70 /// let annotations: Annotations = ["foo", "bar", "baz"].into_annotations();
71 /// assert!(annotations.contains("foo"));
72 /// assert!(annotations.contains("bar"));
73 /// assert!(annotations.contains("baz"));
74 ///
75 /// assert!(!annotations.contains("quux"));
76 /// assert!(!annotations.contains("quuz"));
77 /// ```
78 pub fn contains<S: AsRef<str>>(&self, query: S) -> bool {
79 let query: &str = query.as_ref();
80 self.iter().any(|symbol| symbol.text() == Some(query))
81 }
82
83 /// Returns the text of the first annotation in this sequence.
84 ///
85 /// If the sequence is empty, returns `None`.
86 /// If the first annotation in the sequence is `$0` (symbol ID 0), returns `None`.
87 /// Otherwise, returns a `Some(&str)` containing the text.
88 ///
89 /// To view the first annotation as a [Symbol] rather than a `&str`, use
90 /// `annotations.iter().next()`.
91 /// ```
92 /// use ion_rs::{Annotations, IntoAnnotations};
93 /// use ion_rs::Symbol;
94 /// let annotations: Annotations = ["foo", "bar", "baz"].into_annotations();
95 /// assert_eq!(annotations.first(), Some("foo"));
96 ///
97 /// let empty_sequence: Vec<&str> = vec![];
98 /// let annotations: Annotations = empty_sequence.into_annotations();
99 /// assert_eq!(annotations.first(), None);
100 ///
101 /// let annotations: Annotations = [Symbol::unknown_text()].into_annotations();
102 /// assert_eq!(annotations.first(), None)
103 /// ```
104 pub fn first(&self) -> Option<&str> {
105 self.iter().next().and_then(|a| a.text())
106 }
107}
108
109impl AsRef<[Symbol]> for Annotations {
110 fn as_ref(&self) -> &[Symbol] {
111 self.symbols.as_slice()
112 }
113}
114
115impl From<Vec<Symbol>> for Annotations {
116 fn from(value: Vec<Symbol>) -> Self {
117 Annotations::new(value)
118 }
119}
120
121impl<S: Into<Symbol>> FromIterator<S> for Annotations {
122 fn from_iter<T: IntoIterator<Item = S>>(iter: T) -> Self {
123 iter.into_annotations()
124 }
125}
126
127impl<'a> IntoIterator for &'a Annotations {
128 type Item = &'a Symbol;
129 type IntoIter = SymbolsIterator<'a>;
130
131 fn into_iter(self) -> Self::IntoIter {
132 SymbolsIterator::new(self.symbols.as_slice())
133 }
134}
135
136impl IntoIterator for Annotations {
137 type Item = Symbol;
138 type IntoIter = AnnotationsIntoIter;
139
140 fn into_iter(self) -> Self::IntoIter {
141 AnnotationsIntoIter::new(self.symbols.into_iter())
142 }
143}
144
145impl IonDataOrd for Annotations {
146 fn ion_cmp(&self, other: &Self) -> Ordering {
147 self.symbols.ion_cmp(&other.symbols)
148 }
149}
150
151impl IonDataHash for Annotations {
152 fn ion_data_hash<H: Hasher>(&self, state: &mut H) {
153 self.symbols.ion_data_hash(state)
154 }
155}
156
157/// Defines conversion into [`Annotations`].
158///
159/// This trait allows us to have a blanket implementations that can cover many type combinations
160/// without conflicting in ways that blanket [`From`] implementations can.
161///
162/// With this we can convert for some `T` that is string-like (`&str`, `String`, `Symbol`, etc...)
163/// we can convert from collections of that type like `[T]`, `[T; n]`, `Vec<T>`, and
164/// iterators of `T` generically.
165pub trait IntoAnnotations {
166 fn into_annotations(self) -> Annotations;
167}
168
169impl<S, I> IntoAnnotations for I
170where
171 S: Into<Symbol>,
172 I: IntoIterator<Item = S>,
173{
174 fn into_annotations(self) -> Annotations {
175 let symbols: Vec<Symbol> = self.into_iter().map(|a| a.into()).collect();
176 Annotations::new(symbols)
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_into_iter() {
186 let expected = vec!["a", "b", "c"].into_annotations();
187 let collect: Annotations = expected.clone().into_iter().collect();
188 assert_eq!(expected, collect);
189 }
190
191 #[test]
192 fn test_from_vec() {
193 let expected = vec!["d", "e", "f"].into_annotations();
194 let symbols: Vec<_> = expected.clone().into_iter().collect();
195 let from: Annotations = symbols.into();
196 assert_eq!(expected, from);
197 }
198}