Expand description

Provides a in-memory tree representation of Ion values that can be operated on in a dynamically typed way.

This module consists of two submodules that implement the value traits:

  • The owned module provides an implementation of values that have no associated lifetime. These types are convenient, but may imply extra copying/cloning.
  • The borrowed module provides an implementation of values that are tied to some associated lifetime and borrow a reference to their underlying data in some way (e.g. storing a &str in the value versus a fully owned String).
  • The reader module provides API and implementation to read Ion data into Element instances.
  • The writer module provides API and implementation to write Ion data from Element instances.

Examples

In general, users will use the ElementReader trait to read in data:

use ion_rs::IonType;
use ion_rs::result::IonResult;
use ion_rs::value::{Element, Struct};
use ion_rs::value::reader::element_reader;
use ion_rs::value::reader::ElementReader;

fn main() -> IonResult<()> {
    let mut iter = element_reader().iterate_over(br#""hello!""#)?;
    if let Some(Ok(elem)) = iter.next() {
        assert_eq!(IonType::String, elem.ion_type());
        assert_eq!("hello!", elem.as_str().unwrap());
    } else {
        panic!("Expected an element!");
    }
    assert!(iter.next().is_none());
    Ok(())
}

ElementReader::read_all is a convenient way to put all of the parsed Element into a Vec, with a single IonError wrapper:

let elems = element_reader().read_all(br#""hello" world"#)?;
assert_eq!(2, elems.len());
assert_eq!(IonType::String, elems[0].ion_type());
assert_eq!("hello", elems[0].as_str().unwrap());
assert_eq!(IonType::Symbol, elems[1].ion_type());
assert_eq!("world", elems[1].as_str().unwrap());

ElementReader::read_one is another convenient way to parse a single top-level element into a Element. This method will return an error if the data has a parsing error or if there is more than one Element in the stream:

// read a single value from binary: 42
let elem = element_reader().read_one(&[0xE0, 0x01, 0x00, 0xEA, 0x21, 0x2A])?;
assert_eq!(IonType::Integer, elem.ion_type());
assert_eq!(42, elem.as_i64().unwrap());

// cannot read two values in a stream this way: 42 0
assert!(element_reader().read_one(&[0xE0, 0x01, 0x00, 0xEA, 0x21, 0x2A, 0x20]).is_err());

// cannot read an empty stream (binary IVM only)
assert!(element_reader().read_one(&[0xE0, 0x01, 0x00, 0xEA]).is_err());

// normal malformed binary is a failure!
assert!(element_reader().read_one(&[0xE0, 0x01, 0x00, 0xEA, 0xF0]).is_err());

// also an error if malformed data happens after valid single value
assert!(element_reader().read_one(&[0xE0, 0x01, 0x00, 0xEA, 0x20, 0x30]).is_err());

To serialize data, users can use the ElementWriter trait to serialize data from Element to binary or text Ion:

use ion_rs::result::IonResult;
use ion_rs::value::Element;
use ion_rs::value::reader::{element_reader, ElementReader};
use ion_rs::value::writer::{ElementWriter, Format};

// a fixed buffer length to write to
const BUF_SIZE: usize = 8 * 1024 * 1024;

fn main() -> IonResult<()> {
    let elems = element_reader().read_all(b"null true 1")?;

    let mut buf = vec![0u8; BUF_SIZE];
    let mut writer = Format::Binary.element_writer_for_slice(&mut buf)?;
    writer.write_all(elems.iter())?;

    let output = writer.finish()?;
    assert_eq!(&[0xE0, 0x01, 0x00, 0xEA, 0x0F, 0x11, 0x21, 0x01], output);    
     
    Ok(())
}

Users should use the traits in this module to make their code work in contexts that have either borrowed or owned values. This can be done most easily by writing generic functions that can work with a reference of any type.

For example, consider a fairly contrived, but generic extract_text function that unwraps and converts SymbolToken::text() into an owned String:

use ion_rs::value::SymbolToken;
use ion_rs::value::borrowed::BorrowedSymbolToken;
use ion_rs::value::owned::OwnedSymbolToken;

fn extract_text<T: SymbolToken>(tok: &T) -> String {
    tok.text().unwrap().into()
}

let owned_token: OwnedSymbolToken = "hello".into();

// owned value to emphasize lifetime
let text = String::from("hello");
let borrowed_token: BorrowedSymbolToken = text.as_str().into();

let owned_text = extract_text(&owned_token);
let borrowed_text = extract_text(&borrowed_token);
assert_eq!(owned_text, borrowed_text);

This extends to the Element trait as well which is the “top-level” API type for any Ion datum. Consider a contrived function that extracts and returns the annotations of an underlying element as a Vec<String>. Note that it filters out any annotation that may not have text (so data could be dropped):

use ion_rs::value::{Element, SymbolToken};
use ion_rs::value::borrowed::{
    BorrowedValue,
    BorrowedElement,
    local_sid_token as borrowed_local_sid_token,
    text_token as borrowed_text_token
};
use ion_rs::value::owned::{
    OwnedValue,
    OwnedElement,
    local_sid_token as owned_local_sid_token,
    text_token as owned_text_token
};

fn extract_annotations<T: Element>(elem: &T) -> Vec<Option<String>> {
    elem.annotations().map(
        |tok| tok.text().map(|text_ref| text_ref.to_string())
    ).collect()
}

let owned_elem = OwnedElement::new(
    vec![
        owned_local_sid_token(300).with_source("foo", 12),
        owned_text_token("hello")
    ],
    OwnedValue::String("world".into())
);

// owned values to emphasize lifetime
let strings: Vec<String> =
    vec!["hello", "world"].iter().map(|x| x.to_string()).collect();
let borrowed_elem = BorrowedElement::new(
    vec![
        borrowed_local_sid_token(200).with_source("bar", 9),
        borrowed_text_token(&strings[0])
    ],
    BorrowedValue::String(&strings[1])
);

let owned_annotations = extract_annotations(&owned_elem);
let borrowed_annotations = extract_annotations(&borrowed_elem);
assert_eq!(owned_annotations, borrowed_annotations);

For reference here are a couple other value style APIs for JSON:

Modules

Provides borrowed implementations of SymbolToken, Element and its dependents.

Provides owned implementations of SymbolToken, Element and its dependents.

Provides APIs to read Ion data into Element from different sources such as slices or files.

Provides utility to serialize Ion data from Element into common targets such as byte buffers or files.

Enums

Container for either an integer that can fit in a 64-bit word or an arbitrarily sized BigInt.

Traits

Represents a either a borrowed or owned Ion datum. There are/will be specific APIs for borrowed and owned implementations, but this trait unifies operations on either.

The shared symbol table source of a given SymbolToken.

Provides convenient integer accessors for integer values that are like AnyInt

Represents the value of sequences of Ion elements (i.e. list and sexp).

Represents the value of struct of Ion elements.

A view of a symbolic token.