#![allow(non_camel_case_types)]
use crate::element::reader::ElementReader;
use crate::element::Element;
use crate::lazy::decoder::Decoder;
use crate::lazy::streaming_raw_reader::IonInput;
use crate::lazy::system_reader::SystemReader;
use crate::lazy::value::LazyValue;
use crate::read_config::ReadConfig;
use crate::result::IonFailure;
use crate::{IonError, IonResult, MacroTable, SymbolTable};
pub struct Reader<Encoding: Decoder, Input: IonInput> {
system_reader: SystemReader<Encoding, Input>,
}
impl<Encoding: Decoder, Input: IonInput> Reader<Encoding, Input> {
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> IonResult<Option<LazyValue<'_, Encoding>>> {
self.system_reader.next_value()
}
pub fn expect_next(&mut self) -> IonResult<LazyValue<'_, Encoding>> {
self.next()?
.ok_or_else(|| IonError::decoding_error("expected another top-level value"))
}
#[allow(dead_code)]
pub fn symbol_table(&self) -> &SymbolTable {
self.system_reader.symbol_table()
}
#[allow(dead_code)]
pub fn macro_table(&self) -> &MacroTable {
self.system_reader.macro_table()
}
}
impl<Encoding: Decoder, Input: IonInput> Reader<Encoding, Input> {
pub fn new(
config: impl Into<ReadConfig<Encoding>>,
ion_data: Input,
) -> IonResult<Reader<Encoding, Input>> {
let system_reader = SystemReader::new(config, ion_data);
Ok(Reader { system_reader })
}
}
use crate::lazy::expanded::lazy_element::LazyElement;
use crate::lazy::{expanded::template::TemplateMacro, text::raw::v1_1::reader::MacroAddress};
#[allow(dead_code)]
impl<Encoding: Decoder, Input: IonInput> Reader<Encoding, Input> {
pub fn register_template_src(&mut self, template_definition: &str) -> IonResult<MacroAddress> {
self.system_reader
.expanding_reader
.register_template_src(template_definition)
}
pub fn register_template(&mut self, template_macro: TemplateMacro) -> IonResult<MacroAddress> {
self.system_reader
.expanding_reader
.register_template(template_macro)
}
}
impl<Encoding: Decoder, Input: IonInput> Iterator for Reader<Encoding, Input> {
type Item = IonResult<LazyElement<Encoding>>;
fn next(&mut self) -> Option<Self::Item> {
match self.next() {
Ok(None) => None,
Ok(Some(lazy_value)) => Some(Ok(lazy_value.to_owned())),
Err(e) => Some(Err(e)),
}
}
}
#[allow(dead_code)] pub struct LazyElementIterator<'iter, Encoding: Decoder, Input: IonInput> {
lazy_reader: &'iter mut Reader<Encoding, Input>,
}
impl<Encoding: Decoder, Input: IonInput> Iterator for LazyElementIterator<'_, Encoding, Input> {
type Item = IonResult<LazyElement<Encoding>>;
fn next(&mut self) -> Option<Self::Item> {
match self.lazy_reader.next() {
Ok(None) => None,
Ok(Some(lazy_value)) => Some(Ok(lazy_value.to_owned())),
Err(e) => Some(Err(e)),
}
}
}
pub struct ElementIterator<'iter, Encoding: Decoder, Input: IonInput> {
lazy_reader: &'iter mut Reader<Encoding, Input>,
}
impl<Encoding: Decoder, Input: IonInput> Iterator for ElementIterator<'_, Encoding, Input> {
type Item = IonResult<Element>;
fn next(&mut self) -> Option<Self::Item> {
match self.lazy_reader.next() {
Ok(None) => None,
Ok(Some(lazy_value)) => Some(lazy_value.try_into()),
Err(e) => Some(Err(e)),
}
}
}
impl<Encoding: Decoder, Input: IonInput> ElementReader for Reader<Encoding, Input> {
type ElementIterator<'a>
= ElementIterator<'a, Encoding, Input>
where
Self: 'a;
fn read_next_element(&mut self) -> IonResult<Option<Element>> {
let lazy_value = match self.next()? {
None => return Ok(None),
Some(lazy_value) => lazy_value,
};
let element: Element = lazy_value.try_into()?;
Ok(Some(element))
}
fn elements(&mut self) -> Self::ElementIterator<'_> {
ElementIterator { lazy_reader: self }
}
}
#[cfg(test)]
mod tests {
use crate::element::element_writer::ElementWriter;
use crate::element::Element;
use crate::lazy::encoder::writer::Writer;
use crate::lazy::encoding::BinaryEncoding_1_0;
use crate::lazy::value_ref::ValueRef;
use crate::write_config::WriteConfig;
use crate::{ion_list, ion_sexp, ion_struct, v1_0, Int, IonResult, IonType};
use super::*;
fn to_binary_ion(text_ion: &str) -> IonResult<Vec<u8>> {
let buffer = Vec::new();
let config = WriteConfig::<BinaryEncoding_1_0>::new();
let mut writer = Writer::new(config, buffer)?;
let elements = Element::read_all(text_ion)?;
writer.write_elements(&elements)?;
writer.flush()?;
writer.close()
}
#[test]
fn sequence_iter() -> IonResult<()> {
let ion_data = to_binary_ion(
r#"
(foo baz baz)
(1 2 3)
(a b c)
"#,
)?;
let mut reader = Reader::new(v1_0::Binary, ion_data)?;
while let Some(top_level_value) = reader.next()? {
if let ValueRef::SExp(sexp) = top_level_value.read()? {
for lazy_value in &sexp {
println!("{:?}", lazy_value?.read()?)
}
}
}
Ok(())
}
#[test]
fn test_rewind() -> IonResult<()> {
let data = to_binary_ion(
r#"
[
"yo",
77,
true,
{name:"hi", name: "hello"},
]
"#,
)?;
let mut reader = Reader::new(v1_0::Binary, data)?;
let first_value = reader.expect_next()?;
let list = first_value.read()?.expect_list()?;
let lazy_values = list.iter().collect::<IonResult<Vec<_>>>()?;
assert_eq!(lazy_values[1].read()?.expect_int()?, Int::from(77));
assert!(lazy_values[2].read()?.expect_bool()?);
Ok(())
}
#[test]
fn materialize() -> IonResult<()> {
let data = to_binary_ion(
r#"
[
"yo",
77,
true,
{name:"hi", name: "hello"},
]
null.int
(null null.string)
"#,
)?;
let mut reader = Reader::new(v1_0::Binary, data)?;
let list: Element = ion_list![
"yo",
77,
true,
ion_struct! {
"name": "hi",
"name": "hello"
}
]
.into();
assert_eq!(reader.read_next_element()?, Some(list));
assert_eq!(
reader.read_next_element()?,
Some(Element::null(IonType::Int))
);
let sexp: Element = ion_sexp!(IonType::Null IonType::String).into();
assert_eq!(reader.read_next_element()?, Some(sexp));
assert_eq!(reader.read_next_element()?, None);
Ok(())
}
#[test]
fn ivm_resets_symbol_table_in_concatenated_documents() -> IonResult<()> {
use crate::v1_0::Binary;
let mut output = Vec::new();
output = Element::read_one("annotation1::{id1: \"one\"}")?.encode_to(output, Binary)?;
output = Element::read_one("annotation2::{id2: \"two\"}")?.encode_to(output, Binary)?;
output = Element::read_one("annotation3::{id3: \"three\"}")?.encode_to(output, Binary)?;
let elements: Vec<Element> = Element::read_all(&output)?.into_iter().collect();
assert_eq!(elements.len(), 3);
assert_eq!(elements[0].annotations().first(), Some("annotation1"));
let s0 = elements[0].as_struct().unwrap();
assert!(s0.get("id1").is_some());
assert!(s0.get("id2").is_none());
assert_eq!(elements[1].annotations().first(), Some("annotation2"));
let s1 = elements[1].as_struct().unwrap();
assert!(s1.get("id2").is_some());
assert!(s1.get("id1").is_none());
assert_eq!(elements[2].annotations().first(), Some("annotation3"));
let s2 = elements[2].as_struct().unwrap();
assert!(s2.get("id3").is_some());
assert!(s2.get("id1").is_none());
Ok(())
}
}
#[cfg(all(test, feature = "experimental-ion-1-1"))]
mod tests_1_1 {
use crate::lazy::text::raw::v1_1::reader::MacroAddress;
use crate::{v1_1, IonResult, MacroTable, Reader};
fn expand_macro_test(
macro_source: &str,
encode_macro_fn: impl FnOnce(MacroAddress) -> Vec<u8>,
test_fn: impl FnOnce(Reader<v1_1::Binary, &[u8]>) -> IonResult<()>,
) -> IonResult<()> {
let macro_address = MacroTable::FIRST_USER_MACRO_ID;
let opcode_byte = u8::try_from(macro_address).unwrap();
let binary_ion = encode_macro_fn(opcode_byte as usize);
let mut reader = Reader::new(v1_1::Binary, binary_ion.as_slice())?;
let actual_address = reader.register_template_src(macro_source)?;
assert_eq!(
macro_address, actual_address,
"Assigned macro address did not match expected address."
);
test_fn(reader)
}
#[test]
fn expand_binary_template_macro() -> IonResult<()> {
let macro_source = "(macro seventeen () 17)";
let encode_macro_fn = |address| vec![address as u8];
expand_macro_test(macro_source, encode_macro_fn, |mut reader| {
assert_eq!(reader.expect_next()?.read()?.expect_i64()?, 17);
Ok(())
})
}
#[test]
fn expand_binary_template_macro_with_one_arg() -> IonResult<()> {
let macro_source = r#"
(macro greet (name)
(.make_string "Hello, " (%name) "!")
)
"#;
#[rustfmt::skip]
let encode_macro_fn = |address| vec![
address as u8,
0x98,
0x4D, 0x69, 0x63, 0x68, 0x65, 0x6C, 0x6C, 0x65,
];
expand_macro_test(macro_source, encode_macro_fn, |mut reader| {
assert_eq!(
reader.expect_next()?.read()?.expect_string()?,
"Hello, Michelle!"
);
Ok(())
})
}
#[test]
fn expand_binary_template_macro_with_multiple_outputs() -> IonResult<()> {
let macro_source = r#"
(macro questions (food)
(.values
(.make_string "What color is a " (%food) "?")
(.make_string "How much potassium is in a " (%food) "?")
(.make_string "What wine should I pair with a " (%food) "?")))
"#;
#[rustfmt::skip]
let encode_macro_fn = |address| vec![
address as u8,
0x96,
0x62, 0x61, 0x6E, 0x61, 0x6E, 0x61
];
expand_macro_test(macro_source, encode_macro_fn, |mut reader| {
assert_eq!(
reader.expect_next()?.read()?.expect_string()?,
"What color is a banana?"
);
assert_eq!(
reader.expect_next()?.read()?.expect_string()?,
"How much potassium is in a banana?"
);
assert_eq!(
reader.expect_next()?.read()?.expect_string()?,
"What wine should I pair with a banana?"
);
Ok(())
})
}
}