ion_rs/element/
builders.rs

1use crate::element::{Element, Sequence, Struct};
2use crate::Symbol;
3
4/// Constructs [Sequence], [List], and [SExp] values incrementally.
5///
6/// Building a [Sequence]:
7/// ```
8/// use ion_rs::element::{Element, Sequence};
9/// let actual: Sequence = Sequence::builder().push(1).push(true).push("foo").build();
10/// let expected: Sequence = Sequence::new([
11///     Element::integer(1),
12///     Element::boolean(true),
13///     Element::string("foo"),
14/// ]);
15/// assert_eq!(actual, expected);
16/// ```
17/// Building a [List]:
18/// ```
19/// use ion_rs::element::{Element, List, Sequence};
20/// let actual: List = Sequence::builder()
21///     .push(1)
22///     .push(true)
23///     .push("foo")
24///     .build_list();
25/// let expected: List = List(Sequence::new([
26///     Element::integer(1),
27///     Element::boolean(true),
28///     Element::string("foo"),
29/// ]));
30/// assert_eq!(actual, expected);
31/// ```
32/// Building a [SExp]:
33/// ```
34/// use ion_rs::element::{Element, SExp, Sequence};
35/// let actual: SExp = Sequence::builder()
36///     .push(1)
37///     .push(true)
38///     .push("foo")
39///     .build_sexp();
40/// let expected: SExp = SExp(Sequence::new([
41///     Element::integer(1),
42///     Element::boolean(true),
43///     Element::string("foo"),
44/// ]));
45/// assert_eq!(actual, expected);
46/// ```
47pub struct SequenceBuilder {
48    values: Vec<Element>,
49}
50
51impl SequenceBuilder {
52    /// Crate visible; users should call [`Sequence::builder()`] instead.
53    pub(crate) fn new() -> Self {
54        Self { values: Vec::new() }
55    }
56
57    /// Helper method for [`Sequence::clone_builder()`].
58    pub(crate) fn with_initial_elements(elements: &[Element]) -> Self {
59        let mut new_elements = Vec::with_capacity(elements.len());
60        new_elements.extend_from_slice(elements);
61        Self {
62            values: new_elements,
63        }
64    }
65
66    /// Adds the provided element to the end of the [`Sequence`] being constructed.
67    pub fn push<E: Into<Element>>(mut self, element: E) -> Self {
68        self.values.push(element.into());
69        self
70    }
71
72    /// Removes the element at the specified position from the [`Sequence`] being constructed.
73    /// If the index is out of bounds, this method will panic.
74    pub fn remove(mut self, index: usize) -> Self {
75        // This has O(n) behavior; the removals could be
76        // buffered until the build() if needed.
77        self.values.remove(index);
78        self
79    }
80
81    /// Builds a [`Sequence`] with the previously specified elements.
82    pub fn build(self) -> Sequence {
83        self.values.into()
84    }
85
86    /// Builds a [`List`] with the previously specified elements.
87    pub fn build_list(self) -> List {
88        List(self.build())
89    }
90
91    /// Builds a [`SExp`] with the previously specified elements.
92    pub fn build_sexp(self) -> SExp {
93        SExp(self.build())
94    }
95}
96
97/// Constructs [Struct] values incrementally.
98///
99/// ```
100/// use ion_rs::element::Element;
101/// use ion_rs::ion_struct;
102/// let actual: Element = ion_struct! {
103///     "a": 1,
104///     "b": true,
105///     "c": "foo"
106/// }
107/// .into();
108/// let expected = Element::read_one(r#"{a: 1, b: true, c: "foo"}"#).unwrap();
109/// assert_eq!(actual, expected);
110/// ```
111///
112/// ```
113/// use ion_rs::element::{Element, Struct};
114/// use ion_rs::ion_struct;
115/// let base_struct: Struct = ion_struct! {
116///     "foo": 1,
117///     "bar": 2,
118///     "baz": 3
119/// };
120///
121/// let modified_struct: Element = base_struct
122///     .clone_builder()
123///     .remove_field("bar")
124///     .with_field("quux", 4)
125///     .build()
126///     .into(); // Convert from `Struct` to `Element`
127///
128/// let expected = Element::read_one(r#"{foo: 1, baz: 3, quux: 4}"#).unwrap();
129/// assert_eq!(expected, modified_struct);
130/// ```
131pub struct StructBuilder {
132    fields: Vec<(Symbol, Element)>,
133}
134
135impl StructBuilder {
136    /// Crate visible; users should call [`Struct::builder()`] or [`Element::struct_builder`] instead.
137    pub(crate) fn new() -> Self {
138        StructBuilder { fields: Vec::new() }
139    }
140
141    /// Helper method for [`Struct::clone_builder()`].
142    pub(crate) fn with_initial_fields(elements: &[(Symbol, Element)]) -> Self {
143        let mut new_elements = Vec::with_capacity(elements.len());
144        new_elements.extend_from_slice(elements);
145        Self {
146            fields: new_elements,
147        }
148    }
149
150    /// Adds the provided `(name, value)` pair to the [`Struct`] being constructed.
151    pub fn with_field<S: Into<Symbol>, E: Into<Element>>(
152        mut self,
153        field_name: S,
154        field_value: E,
155    ) -> Self {
156        self.fields.push((field_name.into(), field_value.into()));
157        self
158    }
159
160    /// Adds all of the provided `(name, value)` pairs to the [`Struct`] being constructed.
161    ///
162    /// ```
163    /// use ion_rs::element::{Element, Struct};
164    /// use ion_rs::ion_struct;
165    ///
166    /// let struct1 = ion_struct! {
167    ///     "foo": 1,
168    ///     "bar": 2,
169    ///     "baz": 3
170    /// };
171    ///
172    /// let struct2 = ion_struct! {
173    ///     "a": 4,
174    ///     "b": 5,
175    ///     "c": 6
176    /// };
177    ///
178    /// let merged = struct1
179    ///     .clone_builder()
180    ///     .with_fields(struct2.fields())
181    ///     .build();
182    ///
183    /// let expected = Element::read_one("{foo: 1, bar: 2, baz: 3, a: 4, b: 5, c: 6}").unwrap();
184    /// ```
185    pub fn with_fields<S, E, I>(mut self, fields: I) -> Self
186    where
187        S: Into<Symbol>,
188        E: Into<Element>,
189        I: IntoIterator<Item = (S, E)>,
190    {
191        for (name, value) in fields.into_iter() {
192            let name: Symbol = name.into();
193            let value: Element = value.into();
194            self.fields.push((name, value));
195        }
196        self
197    }
198
199    /// Removes the first field with the specified name from the [`Struct`] being constructed.
200    pub fn remove_field<A: AsRef<str>>(mut self, field_to_remove: A) -> Self {
201        // TODO: This removes the first field with a matching name.
202        //       Do we need other versions for remove_all or remove_last?
203        // TODO: This has O(n) behavior; it could be optimized.
204        let field_to_remove: &str = field_to_remove.as_ref();
205        let _ = self
206            .fields
207            .iter()
208            .position(|(name, _)| name == &field_to_remove)
209            .map(|index| self.fields.remove(index));
210        self
211    }
212
213    /// Builds a [`Struct`] with the previously specified fields.
214    pub fn build(self) -> Struct {
215        Struct::from_iter(self.fields.into_iter())
216    }
217}
218
219/// Constructs a list [`Element`] with the specified child values.
220///
221/// ```
222/// use ion_rs::element::Element;
223/// use ion_rs::ion_list;
224/// // Construct a list Element from Rust values
225/// let actual: Element = ion_list!["foo", 7, false, ion_list![1.5f64, -8.25f64]].into();
226/// // Construct an Element from serialized Ion data
227/// let expected = Element::read_one(r#"["foo", 7, false, [1.5e0, -8.25e0]]"#).unwrap();
228/// // Compare the two Elements
229/// assert_eq!(expected, actual);
230/// ```
231///
232/// Child values can be anything that implements `Into<Element>`, which
233/// includes existing [Element] values.
234///
235/// ```
236/// // Construct a list Element from existing Elements
237/// use ion_rs::element::{Element, IntoAnnotatedElement};
238/// use ion_rs::ion_list;
239///
240/// let string_element: Element = "foo".into();
241/// let bool_element: Element = true.into();
242///
243/// let actual: Element = ion_list![
244///     string_element,
245///     bool_element,
246///     10i64.with_annotations(["bar"]), // .with_annotations() constructs an Element
247///     Element::clob("hello"),
248///     Element::symbol("world")
249/// ]
250/// .into();
251/// // Construct an Element from serialized Ion data
252/// let expected = Element::read_one(r#"["foo", true, bar::10, {{"hello"}}, world]"#).unwrap();
253/// // Compare the two Elements
254/// assert_eq!(expected, actual);
255/// ```
256#[macro_export]
257macro_rules! ion_list {
258    ($($element:expr),*) => {{
259        use $crate::element::Sequence;
260        Sequence::builder()$(.push($element))*.build_list()
261    }};
262}
263
264/// Constructs an s-expression [`Element`] with the specified child values.
265///
266/// ```
267/// use ion_rs::ion_sexp;
268/// use ion_rs::element::Element;
269/// // Construct an s-expression Element from Rust values
270/// let actual: Element = ion_sexp!("foo" 7 false ion_sexp!(1.5f64 8.25f64)).into();
271/// // Construct an Element from serialized Ion data
272/// let expected = Element::read_one(r#"("foo" 7 false (1.5e0 8.25e0))"#).unwrap();
273/// // Compare the two Elements
274/// assert_eq!(expected, actual);
275/// ```
276///
277/// Child values can be anything that implements `Into<Element>`, which
278/// includes existing [Element] values.
279///
280/// ```
281/// // Construct a s-expression Element from existing Elements
282/// use ion_rs::ion_sexp;
283/// use ion_rs::element::{Element, IntoAnnotatedElement};
284///
285/// let string_element: Element = "foo".into();
286/// let bool_element: Element = true.into();
287///
288/// let actual: Element = ion_sexp!(
289///     string_element
290///     bool_element
291///     10i64.with_annotations(["bar"]) // .with_annotations() constructs an Element
292///     Element::clob("hello")
293///     Element::symbol("world")
294/// ).into();
295/// // Construct an Element from serialized Ion data
296/// let expected = Element::read_one(r#"("foo" true bar::10 {{"hello"}} world)"#).unwrap();
297/// // Compare the two Elements
298/// assert_eq!(expected, actual);
299/// ```
300#[macro_export]
301macro_rules! ion_sexp {
302    ($($element:expr)*) => {{
303        use $crate::element::Sequence;
304        Sequence::builder()$(.push($element))*.build_sexp()
305    }};
306}
307
308/// Constructs an struct [`Element`] with the specified fields.
309///
310/// For each field, the name must implement `Into<Symbol>` and the value must implement
311/// `Into<Element>`.
312///
313/// ```
314/// use ion_rs::element::Element;
315/// use ion_rs::{ion_struct, IonType};
316/// let field_name_2 = "x";
317/// let prefix = "abc";
318/// let suffix = "def";
319/// // Construct an s-expression Element from Rust values
320/// let actual = ion_struct! {
321///     "w": "foo",
322/// //   ^--- Quoted strings are field name literals
323/// //   v--- Unquoted field names are interpreted as variables
324///     field_name_2: 7,
325///     "y": false,
326///     "z": ion_struct!{ "a": 1.5f64, "b": -8.25f64},
327/// //        Arbitrary expressions are acceptable, though some may require
328/// //   v--- an extra scope (braces: `{}`) to be understood properly.
329///      {format!("{}_{}", prefix, suffix)}: IonType::Null
330/// }
331/// .into();
332/// // Construct an Element from serialized Ion data
333/// let expected = Element::read_one(
334///     r#"{w: "foo", x: 7, y: false, z: {a: 1.5e0, b: -8.25e0}, abc_def: null}"#,
335/// )
336/// .unwrap();
337/// // Compare the two Elements
338/// assert_eq!(expected, actual);
339/// ```
340#[macro_export]
341macro_rules! ion_struct {
342    ($($field_name:tt : $element:expr),*) => {{
343        use $crate::element::Struct;
344        Struct::builder()$(.with_field($field_name, $element))*.build()
345    }};
346}
347
348use crate::types::{List, SExp};
349pub use {ion_list, ion_sexp, ion_struct};
350
351#[cfg(test)]
352mod tests {
353    use crate::element::builders::{SequenceBuilder, StructBuilder};
354    use crate::element::Element;
355    use crate::{ion_list, ion_sexp, ion_struct, Symbol};
356
357    #[test]
358    fn make_list_with_builder() {
359        let actual: Element = SequenceBuilder::new()
360            .push(1)
361            .push(true)
362            .push("foo")
363            .push(Symbol::owned("bar"))
364            .build_list()
365            .into();
366        let expected = Element::read_one(r#"[1, true, "foo", bar]"#).unwrap();
367        assert_eq!(actual, expected);
368    }
369
370    #[test]
371    fn make_list_with_macro() {
372        let actual: Element = ion_list![1, true, "foo", Symbol::owned("bar")].into();
373        let expected = Element::read_one(r#"[1, true, "foo", bar]"#).unwrap();
374        assert_eq!(actual, expected);
375    }
376
377    #[test]
378    fn make_list_with_builder_using_remove() {
379        let actual: Element = SequenceBuilder::new()
380            .push(1)
381            .push(true)
382            .push("foo")
383            .remove(1)
384            .remove(1)
385            .push(Symbol::owned("bar"))
386            .build_list()
387            .into();
388        let expected = Element::read_one("[1, bar]").unwrap();
389        assert_eq!(actual, expected);
390    }
391
392    #[test]
393    fn list_clone_builder() {
394        let original_list = ion_list![1, true, "foo", Symbol::owned("bar")];
395        let new_list: Element = original_list
396            .clone_builder()
397            .remove(1)
398            .push(88)
399            .build_list()
400            .into();
401        let expected_list = Element::read_one(r#"[1, "foo", bar, 88]"#).unwrap();
402        assert_eq!(new_list, expected_list);
403    }
404
405    #[test]
406    fn make_sexp_with_builder() {
407        let actual: Element = SequenceBuilder::new()
408            .push(1)
409            .push(true)
410            .push("foo")
411            .push(Symbol::owned("bar"))
412            .build_sexp()
413            .into();
414        let expected = Element::read_one(r#"(1 true "foo" bar)"#).unwrap();
415        assert_eq!(actual, expected);
416    }
417
418    #[test]
419    fn sexp_clone_builder() {
420        let original_sexp = ion_sexp!(1 true "foo" Symbol::owned("bar"));
421        let new_sexp: Element = original_sexp
422            .clone_builder()
423            .remove(1)
424            .push(88)
425            .build_sexp()
426            .into();
427        let expected_sexp = Element::read_one(r#"(1 "foo" bar 88)"#).unwrap();
428        assert_eq!(new_sexp, expected_sexp);
429    }
430
431    #[test]
432    fn make_sexp_with_builder_using_remove() {
433        let actual: Element = SequenceBuilder::new()
434            .push(1)
435            .push(true)
436            .push("foo")
437            .remove(1)
438            .remove(1)
439            .push(Symbol::owned("bar"))
440            .build_sexp()
441            .into();
442        let expected = Element::read_one("(1 bar)").unwrap();
443        assert_eq!(actual, expected);
444    }
445
446    #[test]
447    fn make_sexp_with_macro() {
448        let actual: Element = ion_sexp!(1 true "foo" Symbol::owned("bar")).into();
449        let expected = Element::read_one(r#"(1 true "foo" bar)"#).unwrap();
450        assert_eq!(actual, expected);
451    }
452
453    #[test]
454    fn make_struct_with_builder() {
455        let actual: Element = StructBuilder::new()
456            .with_field("a", 1)
457            .with_field("b", true)
458            .with_field("c", "foo")
459            .with_field("d", Symbol::owned("bar"))
460            .build()
461            .into();
462        let expected = Element::read_one(r#"{a: 1, b: true, c: "foo", d: bar}"#).unwrap();
463        assert_eq!(actual, expected);
464    }
465
466    #[test]
467    fn make_struct_with_macro() {
468        let actual: Element = ion_struct! {
469            "a": 1,
470            "b": true,
471            "c": "foo",
472            "d": Symbol::owned("bar")
473        }
474        .into();
475        let expected = Element::read_one(r#"{a: 1, b: true, c: "foo", d: bar}"#).unwrap();
476        assert_eq!(actual, expected);
477    }
478
479    #[test]
480    fn make_struct_with_builder_using_remove_field() {
481        let actual: Element = StructBuilder::new()
482            .with_field("a", 1)
483            .with_field("b", true)
484            .with_field("c", "foo")
485            .with_field("d", Symbol::owned("bar"))
486            .with_field("d", Symbol::owned("baz"))
487            // Removes the only 'b' field
488            .remove_field("b")
489            // Removes the first 'd' field, leaves the second
490            .remove_field("d")
491            .build()
492            .into();
493        let expected = Element::read_one(r#"{a: 1, c: "foo", d: baz}"#).unwrap();
494        assert_eq!(actual, expected);
495    }
496}