use crate::Symbol;
use crate::{Element, Sequence, Struct};
pub struct SequenceBuilder {
values: Vec<Element>,
}
impl SequenceBuilder {
pub(crate) fn new() -> Self {
Self { values: Vec::new() }
}
pub(crate) fn with_initial_elements(elements: &[Element]) -> Self {
let mut new_elements = Vec::with_capacity(elements.len());
new_elements.extend_from_slice(elements);
Self {
values: new_elements,
}
}
pub fn push<E: Into<Element>>(mut self, element: E) -> Self {
self.values.push(element.into());
self
}
pub fn push_all<E: Into<Element>, I: IntoIterator<Item = E>>(mut self, elements: I) -> Self {
self.values.extend(elements.into_iter().map(|e| e.into()));
self
}
pub fn remove(mut self, index: usize) -> Self {
self.values.remove(index);
self
}
pub fn build(self) -> Sequence {
self.values.into()
}
pub fn build_list(self) -> List {
List(self.build())
}
pub fn build_sexp(self) -> SExp {
SExp(self.build())
}
}
pub struct StructBuilder {
fields: Vec<(Symbol, Element)>,
}
impl StructBuilder {
pub(crate) fn new() -> Self {
StructBuilder { fields: Vec::new() }
}
pub(crate) fn with_initial_fields(elements: &[(Symbol, Element)]) -> Self {
let mut new_elements = Vec::with_capacity(elements.len());
new_elements.extend_from_slice(elements);
Self {
fields: new_elements,
}
}
pub fn with_field<S: Into<Symbol>, E: Into<Element>>(
mut self,
field_name: S,
field_value: E,
) -> Self {
self.fields.push((field_name.into(), field_value.into()));
self
}
pub fn with_fields<S, E, I>(mut self, fields: I) -> Self
where
S: Into<Symbol>,
E: Into<Element>,
I: IntoIterator<Item = (S, E)>,
{
for (name, value) in fields.into_iter() {
let name: Symbol = name.into();
let value: Element = value.into();
self.fields.push((name, value));
}
self
}
pub fn remove_field<A: AsRef<str>>(mut self, field_to_remove: A) -> Self {
let field_to_remove: &str = field_to_remove.as_ref();
let _ = self
.fields
.iter()
.position(|(name, _)| name == &field_to_remove)
.map(|index| self.fields.remove(index));
self
}
pub fn build(self) -> Struct {
Struct::from_iter(self.fields)
}
}
#[macro_export]
macro_rules! ion_list {
($($element:expr),* $(,)?) => {{
use $crate::Sequence;
Sequence::builder()$(.push($element))*.build_list()
}};
}
#[macro_export]
macro_rules! ion_sexp {
($($element:expr)*) => {{
use $crate::Sequence;
Sequence::builder()$(.push($element))*.build_sexp()
}};
}
#[macro_export]
macro_rules! ion_struct {
($($field_name:tt : $element:expr),* $(,)?) => {{
use $crate::Struct;
Struct::builder()$(.with_field($field_name, $element))*.build()
}};
}
#[macro_export]
macro_rules! ion_seq {
($($element:expr),* $(,)?) => {{
use $crate::Sequence;
Sequence::builder()$(.push($element))*.build()
}};
($($element:expr)*) => {{
use $crate::Sequence;
Sequence::builder()$(.push($element))*.build()
}};
}
use crate::{List, SExp};
#[cfg(test)]
mod tests {
use crate::element::builders::{SequenceBuilder, StructBuilder};
use crate::element::Element;
use crate::Symbol;
#[test]
fn make_seq_with_macro() {
let expected = Element::read_all(r#"1 true "foo" "bar""#).unwrap();
assert_eq!(ion_seq!(1 true "foo" "bar"), expected);
assert_eq!(ion_seq![1, true, "foo", "bar"], expected);
assert_eq!(ion_seq![1, true, "foo", "bar",], expected);
}
#[test]
fn make_list_with_builder() {
let actual: Element = SequenceBuilder::new()
.push(1)
.push(true)
.push("foo")
.push(Symbol::owned("bar"))
.build_list()
.into();
let expected = Element::read_one(r#"[1, true, "foo", bar]"#).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn make_list_with_macro() {
let actual: Element = ion_list![1, true, "foo", Symbol::owned("bar")].into();
let expected = Element::read_one(r#"[1, true, "foo", bar]"#).unwrap();
assert_eq!(actual, expected);
let actual: Element = ion_list![1, true, "foo", Symbol::owned("bar"),].into();
assert_eq!(actual, expected);
}
#[test]
fn make_list_with_builder_using_remove() {
let actual: Element = SequenceBuilder::new()
.push(1)
.push(true)
.push("foo")
.remove(1)
.remove(1)
.push(Symbol::owned("bar"))
.build_list()
.into();
let expected = Element::read_one("[1, bar]").unwrap();
assert_eq!(actual, expected);
}
#[test]
fn list_clone_builder() {
let original_list = ion_list![1, true, "foo", Symbol::owned("bar")];
let new_list: Element = original_list
.clone_builder()
.remove(1)
.push(88)
.build_list()
.into();
let expected_list = Element::read_one(r#"[1, "foo", bar, 88]"#).unwrap();
assert_eq!(new_list, expected_list);
}
#[test]
fn make_sexp_with_builder() {
let actual: Element = SequenceBuilder::new()
.push(1)
.push(true)
.push("foo")
.push(Symbol::owned("bar"))
.build_sexp()
.into();
let expected = Element::read_one(r#"(1 true "foo" bar)"#).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn sexp_clone_builder() {
let original_sexp = ion_sexp!(1 true "foo" Symbol::owned("bar"));
let new_sexp: Element = original_sexp
.clone_builder()
.remove(1)
.push(88)
.build_sexp()
.into();
let expected_sexp = Element::read_one(r#"(1 "foo" bar 88)"#).unwrap();
assert_eq!(new_sexp, expected_sexp);
}
#[test]
fn make_sexp_with_builder_using_remove() {
let actual: Element = SequenceBuilder::new()
.push(1)
.push(true)
.push("foo")
.remove(1)
.remove(1)
.push(Symbol::owned("bar"))
.build_sexp()
.into();
let expected = Element::read_one("(1 bar)").unwrap();
assert_eq!(actual, expected);
}
#[test]
fn make_sexp_with_macro() {
let actual: Element = ion_sexp!(1 true "foo" Symbol::owned("bar")).into();
let expected = Element::read_one(r#"(1 true "foo" bar)"#).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn make_struct_with_builder() {
let actual: Element = StructBuilder::new()
.with_field("a", 1)
.with_field("b", true)
.with_field("c", "foo")
.with_field("d", Symbol::owned("bar"))
.build()
.into();
let expected = Element::read_one(r#"{a: 1, b: true, c: "foo", d: bar}"#).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn make_struct_with_macro() {
let actual: Element = ion_struct! {
"a": 1,
"b": true,
"c": "foo",
"d": Symbol::owned("bar")
}
.into();
let expected = Element::read_one(r#"{a: 1, b: true, c: "foo", d: bar}"#).unwrap();
assert_eq!(actual, expected);
let actual: Element = ion_struct! {
"a": 1,
"b": true,
"c": "foo",
"d": Symbol::owned("bar"), }
.into();
assert_eq!(actual, expected);
}
#[test]
fn make_struct_with_builder_using_remove_field() {
let actual: Element = StructBuilder::new()
.with_field("a", 1)
.with_field("b", true)
.with_field("c", "foo")
.with_field("d", Symbol::owned("bar"))
.with_field("d", Symbol::owned("baz"))
.remove_field("b")
.remove_field("d")
.build()
.into();
let expected = Element::read_one(r#"{a: 1, c: "foo", d: baz}"#).unwrap();
assert_eq!(actual, expected);
}
}