Module ion_rs::value[][src]

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 loader module provides API and implementation to load Ion data into Element instances.

Examples

In general, users will use the Loader trait to load in data:

use ion_rs::IonType;
use ion_rs::result::IonResult;
use ion_rs::value::{Element, Struct};
use ion_rs::value::loader::loader;
use ion_rs::value::loader::Loader;

fn main() -> IonResult<()> {
    let mut iter = loader().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(())
}

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

let elems = loader().load_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());

Loader::load_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:

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

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

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

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

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

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

borrowed

Provides borrowed implementations of SymbolToken, Element and its dependents.

loader

Provides utility to load Ion data into Element from different sources such as slices or files.

owned

Provides owned implementations of SymbolToken, Element and its dependents.

Enums

AnyInt

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

Traits

Builder
Element

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.

ImportSource

The shared symbol table source of a given SymbolToken.

IntAccess

Provides convenient integer accessors for integer values that are like AnyInt

Sequence

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

Struct

Represents the value of struct of Ion elements.

SymbolToken

A view of a symbolic token. This can be either a symbol value itself, an annotation, or an field name. A token may have text, a symbol id, or both. here text as None represents SID $0