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}