use bigdecimal::BigDecimal;
use bytes::BigEndian;
use bytes::ByteOrder;
use chrono::offset::FixedOffset;
use chrono::prelude::*;
use crate::binary::constants::v1_0::IVM;
use crate::cursor::{Cursor, StreamItem};
use crate::{
binary::{
constants::v1_0::length_codes,
header::{create_header_byte_jump_table, Header},
int::Int,
uint::UInt,
var_int::VarInt,
var_uint::VarUInt,
IonTypeCode,
},
data_source::IonDataSource,
result::{decoding_error, illegal_operation, illegal_operation_raw, IonResult},
types::{IonType, SymbolId},
};
use std::io;
use std::ops::Range;
#[derive(Clone, Debug)]
struct EncodedValue {
ion_type: IonType,
header: Header,
is_null: bool,
index_at_depth: usize,
field_id: Option<SymbolId>,
annotations: Vec<SymbolId>,
parent_index: Option<usize>,
field_id_offset: usize,
annotations_offset: usize,
header_offset: usize,
value_offset: usize,
value_length: usize,
value_end: usize,
}
impl EncodedValue {
fn header_range(&self) -> Range<usize> {
let start = self.header_offset;
let end = self.value_offset;
Range { start, end }
}
#[inline(always)]
fn value_length(&self) -> usize {
self.value_length
}
fn value_range(&self) -> Range<usize> {
let start = self.value_offset;
let end = self.value_end;
Range { start, end }
}
fn field_id_length(&self) -> Option<usize> {
if self.field_id.is_none() {
return None;
}
if !self.annotations.is_empty() {
return Some(self.annotations_offset - self.field_id_offset);
}
Some(self.header_offset - self.field_id_offset)
}
fn field_id_range(&self) -> Option<Range<usize>> {
if let Some(length) = self.field_id_length() {
let start = self.field_id_offset;
let end = start + length;
return Some(Range { start, end });
}
None
}
fn annotations_length(&self) -> Option<usize> {
if self.annotations.is_empty() {
return None;
}
Some(self.header_offset - self.annotations_offset)
}
fn annotations_range(&self) -> Option<Range<usize>> {
if let Some(length) = self.annotations_length() {
let start = self.annotations_offset;
let end = start + length;
return Some(Range { start, end });
}
None
}
}
impl Default for EncodedValue {
fn default() -> EncodedValue {
EncodedValue {
ion_type: IonType::Null,
header: Header {
ion_type: None,
ion_type_code: IonTypeCode::NullOrWhitespace,
length_code: length_codes::NULL,
},
field_id: None,
annotations: Vec::new(),
is_null: true,
index_at_depth: 0,
field_id_offset: 0,
annotations_offset: 0,
header_offset: 0,
value_offset: 0,
value_length: 0,
value_end: 0,
parent_index: None,
}
}
}
pub struct BinaryIonCursor<R>
where
R: IonDataSource,
{
data_source: R,
buffer: Vec<u8>,
cursor: CursorState,
header_cache: Vec<IonResult<Option<Header>>>,
}
#[derive(Clone, Debug)]
pub struct CursorState {
ion_version: (u8, u8),
bytes_read: usize,
depth: usize,
index_at_depth: usize,
is_in_struct: bool,
value: EncodedValue,
parents: Vec<EncodedValue>,
}
macro_rules! read_safety_checks {
( $binary_cursor:ident, $ion_type:expr ) => {
if $binary_cursor.cursor.value.ion_type != $ion_type || $binary_cursor.cursor.value.is_null
{
return Ok(None);
}
if $binary_cursor.finished_reading_value() {
return illegal_operation(format!(
"You cannot read the same {:?} value more than once.",
$ion_type
));
}
};
}
impl<R: IonDataSource> Cursor for BinaryIonCursor<R> {
type DataSource = R;
fn ion_version(&self) -> (u8, u8) {
self.cursor.ion_version
}
#[inline]
fn next(&mut self) -> IonResult<Option<StreamItem>> {
let _ = self.skip_current_value()?;
if let Some(ref parent) = self.cursor.parents.last() {
if self.cursor.bytes_read >= parent.value_end {
return Ok(None);
}
}
self.cursor.value.field_id = if self.cursor.is_in_struct {
Some(self.read_field_id()?)
} else {
None
};
let mut header = match self.read_next_value_header()? {
Some(header) => header,
None => return Ok(None),
};
self.cursor.value.header = header;
self.cursor.value.annotations.truncate(0);
if header.ion_type_code == IonTypeCode::Annotation {
if header.length_code == 0 {
self.cursor.ion_version = (1, 0);
self.skip_bytes(IVM.len() - 1)?;
return Ok(Some(StreamItem::VersionMarker));
}
let _ = self.read_annotations()?;
header = match self.read_next_value_header()? {
Some(header) => header,
None => return Ok(None),
};
self.cursor.value.header = header;
}
let _ = self.process_header_by_type_code(header)?;
self.cursor.index_at_depth += 1;
self.cursor.value.index_at_depth = self.cursor.index_at_depth;
Ok(Some(StreamItem::Value(
self.cursor.value.ion_type,
self.is_null(),
)))
}
fn ion_type(&self) -> Option<IonType> {
self.cursor.value.header.ion_type
}
fn annotation_ids(&self) -> &[SymbolId] {
&self.cursor.value.annotations
}
fn field_id(&self) -> Option<SymbolId> {
self.cursor.value.field_id
}
fn read_null(&mut self) -> IonResult<Option<IonType>> {
if self.is_null() {
return Ok(Some(self.cursor.value.ion_type));
}
Ok(None)
}
fn read_bool(&mut self) -> IonResult<Option<bool>> {
read_safety_checks!(self, IonType::Boolean);
let representation = self.cursor.value.header.length_code;
match representation {
0 => Ok(Some(false)),
1 => Ok(Some(true)),
_ => decoding_error(&format!(
"Found a boolean value with an illegal representation: {}",
representation
)),
}
}
fn read_i64(&mut self) -> IonResult<Option<i64>> {
read_safety_checks!(self, IonType::Integer);
let magnitude = self.read_value_as_uint()?.value();
use self::IonTypeCode::*;
let value = match self.cursor.value.header.ion_type_code {
PositiveInteger => magnitude as i64,
NegativeInteger => -(magnitude as i64),
itc @ _ => unreachable!("Unexpected IonTypeCode: {:?}", itc),
};
Ok(Some(value))
}
fn read_f32(&mut self) -> IonResult<Option<f32>> {
match self.read_f64() {
Ok(Some(value)) => Ok(Some(value as f32)),
Ok(None) => Ok(None),
Err(e) => Err(e),
}
}
fn read_f64(&mut self) -> IonResult<Option<f64>> {
read_safety_checks!(self, IonType::Float);
let number_of_bytes = self.cursor.value.value_length;
self.read_slice(number_of_bytes, |buffer: &[u8]| {
let value = match number_of_bytes {
0 => 0f64,
4 => f64::from(BigEndian::read_f32(buffer)),
8 => BigEndian::read_f64(buffer),
_ => {
return decoding_error(&format!(
"Encountered an illegal value for a Float length: {}",
number_of_bytes
))
}
};
Ok(Some(value))
})
}
fn read_big_decimal(&mut self) -> IonResult<Option<BigDecimal>> {
read_safety_checks!(self, IonType::Decimal);
if self.cursor.value.value_length == 0 {
return Ok(Some(BigDecimal::new(0i64.into(), 0)));
}
let exponent_var_int = self.read_var_int()?;
let coefficient_size_in_bytes =
self.cursor.value.value_length - exponent_var_int.size_in_bytes();
let exponent = exponent_var_int.value() as i64;
let coefficient = self.read_int(coefficient_size_in_bytes)?.value();
Ok(Some(BigDecimal::new(coefficient.into(), -exponent)))
}
fn read_string(&mut self) -> IonResult<Option<String>> {
self.string_ref_map(|s: &str| s.into())
}
fn string_ref_map<F, T>(&mut self, f: F) -> IonResult<Option<T>>
where
F: FnOnce(&str) -> T,
{
use std::str;
read_safety_checks!(self, IonType::String);
let length_in_bytes = self.cursor.value.value_length;
self.read_slice(length_in_bytes, |buffer: &[u8]| {
let string_ref = match str::from_utf8(buffer) {
Ok(utf8_text) => utf8_text,
Err(utf8_error) => {
return decoding_error(&format!(
"The requested string was not valid UTF-8: {:?}",
utf8_error
))
}
};
Ok(Some(f(string_ref)))
})
}
fn string_bytes_map<F, T>(&mut self, f: F) -> IonResult<Option<T>>
where
F: FnOnce(&[u8]) -> T,
{
read_safety_checks!(self, IonType::String);
let length_in_bytes = self.cursor.value.value_length;
self.read_slice(length_in_bytes, |buffer: &[u8]| Ok(Some(f(buffer))))
}
#[inline(always)]
fn read_symbol_id(&mut self) -> IonResult<Option<SymbolId>> {
read_safety_checks!(self, IonType::Symbol);
let symbol_id = self.read_value_as_uint()?.value() as usize;
Ok(Some(symbol_id))
}
fn read_blob_bytes(&mut self) -> IonResult<Option<Vec<u8>>> {
self.blob_ref_map(|b| b.into())
}
fn read_clob_bytes(&mut self) -> IonResult<Option<Vec<u8>>> {
self.clob_ref_map(|c| c.into())
}
fn read_datetime(&mut self) -> IonResult<Option<DateTime<FixedOffset>>> {
read_safety_checks!(self, IonType::Timestamp);
let offset_minutes = self.read_var_int()?.value();
let year = self.read_var_uint()?.value();
let mut month = 0;
let mut day = 0;
let mut hour = 0;
let mut minute = 0;
let mut second = 0;
loop {
if self.finished_reading_value() {
break;
}
month = self.read_var_uint()?.value();
if self.finished_reading_value() {
break;
}
day = self.read_var_uint()?.value();
if self.finished_reading_value() {
break;
}
hour = self.read_var_uint()?.value();
if self.finished_reading_value() {
break;
}
minute = self.read_var_uint()?.value();
if self.finished_reading_value() {
break;
}
second = self.read_var_uint()?.value();
break;
}
let naive_datetime = NaiveDate::from_ymd(year as i32, month as u32, day as u32).and_hms(
hour as u32,
minute as u32,
second as u32,
);
let offset = FixedOffset::west(offset_minutes as i32 * 60i32);
let datetime = offset.from_utc_datetime(&naive_datetime);
Ok(Some(datetime))
}
#[inline]
fn step_in(&mut self) -> IonResult<()> {
use self::IonType::*;
self.cursor.is_in_struct = match self.cursor.value.ion_type {
Struct => true,
List | SExpression => false,
_ => panic!("You cannot step into a(n) {:?}", self.cursor.value.ion_type),
};
self.cursor.value.parent_index = Some(self.cursor.parents.len());
self.cursor.parents.push(self.cursor.value.clone());
self.cursor.depth += 1;
self.cursor.index_at_depth = 0;
Ok(())
}
#[inline]
fn step_out(&mut self) -> IonResult<()> {
use std::mem;
let bytes_to_skip;
let mut parent = self
.cursor
.parents
.pop()
.ok_or_else(|| illegal_operation_raw("You cannot step out of the root level."))?;
bytes_to_skip = parent.value_end - self.cursor.bytes_read;
mem::swap(&mut self.cursor.value, &mut parent);
if let Some(ref parent) = self.cursor.parents.last() {
self.cursor.is_in_struct = parent.ion_type == IonType::Struct;
} else {
self.cursor.is_in_struct = false;
}
self.cursor.index_at_depth = self.cursor.value.index_at_depth;
self.cursor.depth -= 1;
self.skip_bytes(bytes_to_skip)?;
Ok(())
}
fn depth(&self) -> usize {
self.cursor.depth
}
}
const EMPTY_SLICE: &[u8] = &[];
impl<T> BinaryIonCursor<io::Cursor<T>>
where
T: AsRef<[u8]>,
{
pub fn raw_bytes(&self) -> Option<&[u8]> {
if self.ion_type().is_none() {
return None;
}
let start: usize;
if self.cursor.value.field_id.is_some() {
start = self.cursor.value.field_id_offset;
} else if !self.cursor.value.annotations.is_empty() {
start = self.cursor.value.annotations_offset;
} else {
start = self.cursor.value.header_offset;
}
let end = self.cursor.value.value_end;
let bytes = self.data_source.get_ref().as_ref();
Some(&bytes[start..end])
}
pub fn raw_header_bytes(&self) -> Option<&[u8]> {
if self.ion_type().is_none() {
return None;
}
let bytes = self.data_source.get_ref().as_ref();
return Some(&bytes[self.cursor.value.header_range()]);
}
pub fn raw_value_bytes(&self) -> Option<&[u8]> {
if self.ion_type().is_none() || self.cursor.value.value_length == 0 {
return None;
}
let bytes = self.data_source.get_ref().as_ref();
return Some(&bytes[self.cursor.value.value_range()]);
}
pub fn raw_field_id_bytes(&self) -> Option<&[u8]> {
if self.ion_type().is_none() {
return None;
}
if let Some(range) = self.cursor.value.field_id_range() {
let bytes = self.data_source.get_ref().as_ref();
return Some(&bytes[range]);
}
None
}
pub fn raw_annotations_bytes(&self) -> Option<&[u8]> {
if self.ion_type().is_none() {
return None;
}
if let Some(range) = self.cursor.value.annotations_range() {
let bytes = self.data_source.get_ref().as_ref();
return Some(&bytes[range]);
}
None
}
}
impl<R> BinaryIonCursor<R>
where
R: IonDataSource,
{
pub fn new(data_source: R) -> Self {
BinaryIonCursor {
data_source,
buffer: vec![0; 1024],
cursor: CursorState {
ion_version: (1, 0),
bytes_read: 0,
depth: 0,
index_at_depth: 0,
is_in_struct: false,
value: Default::default(),
parents: Vec::new(),
},
header_cache: create_header_byte_jump_table(),
}
}
pub fn is_null(&self) -> bool {
self.cursor.value.is_null
}
fn finished_reading_value(&mut self) -> bool {
self.cursor.value.value_length > 0 && self.cursor.bytes_read >= self.cursor.value.value_end
}
#[inline(always)]
fn read_var_uint(&mut self) -> IonResult<VarUInt> {
let var_uint = VarUInt::read(&mut self.data_source)?;
self.cursor.bytes_read += var_uint.size_in_bytes();
Ok(var_uint)
}
#[inline(always)]
fn read_var_int(&mut self) -> IonResult<VarInt> {
let var_int = VarInt::read(&mut self.data_source)?;
self.cursor.bytes_read += var_int.size_in_bytes() as usize;
Ok(var_int)
}
#[inline(always)]
fn read_value_as_uint(&mut self) -> IonResult<UInt> {
let number_of_bytes = self.cursor.value.value_length;
self.read_uint(number_of_bytes)
}
#[inline(always)]
fn read_uint(&mut self, number_of_bytes: usize) -> IonResult<UInt> {
let uint = UInt::read(&mut self.data_source, number_of_bytes)?;
self.cursor.bytes_read += uint.size_in_bytes();
Ok(uint)
}
#[inline(always)]
fn read_int(&mut self, number_of_bytes: usize) -> IonResult<Int> {
let int = Int::read(&mut self.data_source, number_of_bytes)?;
self.cursor.bytes_read += int.size_in_bytes();
Ok(int)
}
fn process_header_by_type_code(&mut self, header: Header) -> IonResult<()> {
self.cursor.value.ion_type = header.ion_type.unwrap();
self.cursor.value.header = header;
self.cursor.value.is_null = header.length_code == length_codes::NULL;
self.cursor.value.header_offset = self.cursor.bytes_read - 1;
use IonTypeCode::*;
let length = match header.ion_type_code {
NullOrWhitespace | Boolean => 0,
PositiveInteger | NegativeInteger | Decimal | Timestamp | String | Symbol | List
| SExpression | Clob | Blob => self.read_standard_length()?,
Float => self.read_float_length()?,
Struct => self.read_struct_length()?,
Annotation => return decoding_error("Found an annotation wrapping an annotation."),
Reserved => return decoding_error("Found an Ion Value with a Reserved type code."),
};
self.cursor.value.value_offset = self.cursor.bytes_read;
self.cursor.value.value_length = length;
self.cursor.value.value_end = self.cursor.value.value_offset + length;
Ok(())
}
#[inline(always)]
fn read_standard_length(&mut self) -> IonResult<usize> {
let length = match self.cursor.value.header.length_code {
length_codes::NULL => 0,
length_codes::VAR_UINT => self.read_var_uint()?.value(),
magnitude => magnitude as usize,
};
Ok(length)
}
fn read_float_length(&mut self) -> IonResult<usize> {
let length = match self.cursor.value.header.length_code {
0 => 0,
4 => 4,
8 => 8,
length_codes::NULL => 0,
_ => {
return decoding_error(format!(
"Found a Float value with an illegal length: {}",
self.cursor.value.header.length_code
))
}
};
Ok(length)
}
fn read_struct_length(&mut self) -> IonResult<usize> {
let length = match self.cursor.value.header.length_code {
length_codes::NULL => 0,
1 | length_codes::VAR_UINT => self.read_var_uint()?.value(),
magnitude => magnitude as usize,
};
Ok(length)
}
#[inline(always)]
fn read_next_value_header(&mut self) -> IonResult<Option<Header>> {
let next_byte: u8 = match self.next_byte() {
Ok(Some(byte)) => byte,
Ok(None) => return Ok(None),
Err(error) => return Err(error),
};
self.header_cache[next_byte as usize].clone()
}
fn next_byte(&mut self) -> IonResult<Option<u8>> {
let byte = self.data_source.next_byte();
self.cursor.bytes_read += 1;
byte
}
fn skip_bytes(&mut self, number_of_bytes: usize) -> IonResult<()> {
if number_of_bytes == 0 {
return Ok(());
}
self.data_source.skip_bytes(number_of_bytes)?;
self.cursor.bytes_read += number_of_bytes;
Ok(())
}
fn skip_current_value(&mut self) -> IonResult<()> {
if self.cursor.index_at_depth == 0 {
Ok(())
} else {
let bytes_to_skip = self.cursor.value.value_end - self.cursor.bytes_read;
self.skip_bytes(bytes_to_skip)
}
}
fn read_field_id(&mut self) -> IonResult<SymbolId> {
self.cursor.value.field_id_offset = self.cursor.bytes_read;
let var_uint = self.read_var_uint()?;
let field_id = var_uint.value();
Ok(field_id)
}
fn read_annotations(&mut self) -> IonResult<()> {
self.cursor.value.annotations_offset = self.cursor.bytes_read - 1;
let _annotations_and_value_length = self.read_standard_length()?;
let annotations_length = self.read_var_uint()?.value();
let mut bytes_read: usize = 0;
while bytes_read < annotations_length {
let var_uint = self.read_var_uint()?;
bytes_read += var_uint.size_in_bytes();
let annotation_symbol_id = var_uint.value();
self.cursor.value.annotations.push(annotation_symbol_id);
}
Ok(())
}
fn read_exact(&mut self, number_of_bytes: usize) -> IonResult<()> {
let buffer: &mut [u8] = if self.buffer.len() < number_of_bytes {
self.buffer.resize(number_of_bytes, 0);
self.buffer.as_mut()
} else {
let (required_buffer, _) = self.buffer.split_at_mut(number_of_bytes);
required_buffer
};
self.data_source.read_exact(buffer)?;
self.cursor.bytes_read += number_of_bytes;
Ok(())
}
fn read_slice<T, F>(&mut self, number_of_bytes: usize, slice_processor: F) -> IonResult<T>
where
F: FnOnce(&[u8]) -> IonResult<T>,
{
self.cursor.bytes_read += number_of_bytes;
self.data_source
.read_slice(number_of_bytes, &mut self.buffer, slice_processor)
}
pub fn blob_ref_map<F, T>(&mut self, f: F) -> IonResult<Option<T>>
where
F: FnOnce(&[u8]) -> T,
{
read_safety_checks!(self, IonType::Blob);
let number_of_bytes = self.cursor.value.value_length;
self.read_slice(number_of_bytes, |buffer: &[u8]| Ok(Some(f(buffer))))
}
pub fn clob_ref_map<F, T>(&mut self, f: F) -> IonResult<Option<T>>
where
F: FnOnce(&[u8]) -> T,
{
read_safety_checks!(self, IonType::Clob);
let number_of_bytes = self.cursor.value.value_length;
self.read_slice(number_of_bytes, |buffer: &[u8]| Ok(Some(f(buffer))))
}
}
#[cfg(test)]
mod tests {
use std::io;
use bigdecimal::BigDecimal;
use chrono::{FixedOffset, NaiveDate, TimeZone};
use crate::binary::constants::v1_0::IVM;
use crate::binary::cursor::BinaryIonCursor;
use crate::cursor::{Cursor, StreamItem, StreamItem::*};
use crate::result::IonResult;
use crate::types::IonType;
use std::convert::TryInto;
type TestDataSource = io::Cursor<Vec<u8>>;
fn ion_data(bytes: &[u8]) -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(&IVM);
data.extend_from_slice(bytes);
data
}
fn data_source_for(bytes: &[u8]) -> TestDataSource {
let data = ion_data(bytes);
io::Cursor::new(data)
}
fn ion_cursor_for(bytes: &[u8]) -> BinaryIonCursor<TestDataSource> {
let mut binary_cursor = BinaryIonCursor::new(data_source_for(bytes));
assert_eq!(binary_cursor.ion_type(), None);
assert_eq!(binary_cursor.next(), Ok(Some(VersionMarker)));
assert_eq!(binary_cursor.ion_version(), (1u8, 0u8));
binary_cursor
}
#[test]
fn test_read_null_null() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x0F]);
assert_eq!(cursor.next()?, Some(Value(IonType::Null, true)));
assert_eq!(cursor.read_null()?, Some(IonType::Null));
assert!(cursor.is_null());
Ok(())
}
#[test]
fn test_read_null_string() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x8F]);
assert_eq!(cursor.next()?, Some(Value(IonType::String, true)));
assert_eq!(cursor.read_null()?, Some(IonType::String));
assert!(cursor.is_null());
Ok(())
}
#[test]
fn test_read_bool_false() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x10]);
assert_eq!(cursor.next()?, Some(Value(IonType::Boolean, false)));
assert_eq!(cursor.read_bool()?, Some(false));
Ok(())
}
#[test]
fn test_read_bool_true() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x11]);
assert_eq!(cursor.next()?, Some(Value(IonType::Boolean, false)));
assert_eq!(cursor.read_bool()?, Some(true));
Ok(())
}
#[test]
fn test_read_i64_zero() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x20]);
assert_eq!(cursor.next()?, Some(Value(IonType::Integer, false)));
assert_eq!(cursor.read_i64()?, Some(0i64));
Ok(())
}
#[test]
fn test_read_i64_positive() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x21, 0x01]);
assert_eq!(cursor.next()?, Some(Value(IonType::Integer, false)));
assert_eq!(cursor.read_i64()?, Some(1i64));
Ok(())
}
#[test]
fn test_read_i64_negative() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x31, 0x01]);
assert_eq!(cursor.next()?, Some(Value(IonType::Integer, false)));
assert_eq!(cursor.read_i64()?, Some(-1i64));
Ok(())
}
#[test]
fn test_read_f64_zero() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x40]);
assert_eq!(cursor.next()?, Some(Value(IonType::Float, false)));
assert_eq!(cursor.read_f64()?, Some(0f64));
Ok(())
}
#[test]
fn test_read_big_decimal_zero() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x50]);
assert_eq!(cursor.next()?, Some(Value(IonType::Decimal, false)));
assert_eq!(
cursor.read_big_decimal()?,
Some(BigDecimal::new(0i64.into(), 0))
);
Ok(())
}
#[test]
fn test_read_big_decimal_positive_exponent() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x52, 0x81, 0x02]);
assert_eq!(cursor.next()?, Some(Value(IonType::Decimal, false)));
assert_eq!(cursor.read_big_decimal()?, Some(20.into()));
Ok(())
}
#[test]
fn test_read_big_decimal_negative_exponent() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x52, 0xC1, 0x02]);
assert_eq!(cursor.next()?, Some(Value(IonType::Decimal, false)));
assert_eq!(cursor.read_big_decimal()?, Some(0.2f64.try_into().unwrap()));
Ok(())
}
#[test]
fn test_read_timestamp() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x68, 0x80, 0x0F, 0xD0, 0x81, 0x81, 0x80, 0x80, 0x80]);
assert_eq!(cursor.next()?, Some(Value(IonType::Timestamp, false)));
let naive_datetime = NaiveDate::from_ymd(2000 as i32, 1 as u32, 1 as u32)
.and_hms(0 as u32, 0 as u32, 0 as u32);
let offset = FixedOffset::west(0);
let datetime = offset.from_utc_datetime(&naive_datetime);
assert_eq!(cursor.read_datetime()?, Some(datetime));
Ok(())
}
#[test]
fn test_read_symbol_10() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x71, 0x0A]);
assert_eq!(cursor.next()?, Some(Value(IonType::Symbol, false)));
assert_eq!(cursor.read_symbol_id()?, Some(10usize));
Ok(())
}
#[test]
fn test_read_string_empty() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x80]);
assert_eq!(cursor.next()?, Some(Value(IonType::String, false)));
assert_eq!(cursor.read_string()?, Some(String::from("")));
Ok(())
}
#[test]
fn test_read_string_foo() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x83, 0x66, 0x6f, 0x6f]);
assert_eq!(cursor.next()?, Some(Value(IonType::String, false)));
assert_eq!(cursor.read_string()?, Some(String::from("foo")));
Ok(())
}
#[test]
fn test_read_string_foo_twice_fails() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x83, 0x66, 0x6f, 0x6f]);
assert_eq!(cursor.next()?, Some(Value(IonType::String, false)));
assert_eq!(cursor.read_string()?, Some(String::from("foo")));
assert!(cursor.read_string().is_err());
Ok(())
}
#[test]
fn test_read_clob_empty() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x90]);
assert_eq!(cursor.next()?, Some(Value(IonType::Clob, false)));
assert_eq!(cursor.read_clob_bytes()?, Some(vec![]));
Ok(())
}
#[test]
fn test_read_clob_abc() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0x93, 0x61, 0x62, 0x63]);
assert_eq!(cursor.next()?, Some(Value(IonType::Clob, false)));
assert_eq!(
cursor.read_clob_bytes()?.unwrap().as_slice(),
"abc".as_bytes()
);
Ok(())
}
#[test]
fn test_read_blob_empty() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0xA0]);
assert_eq!(cursor.next()?, Some(Value(IonType::Blob, false)));
assert_eq!(cursor.read_blob_bytes()?, Some(vec![]));
Ok(())
}
#[test]
fn test_read_blob_123() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0xA3, 0x01, 0x02, 0x03]);
assert_eq!(cursor.next()?, Some(Value(IonType::Blob, false)));
assert_eq!(cursor.read_blob_bytes()?, Some(vec![1u8, 2, 3]));
Ok(())
}
#[test]
fn test_read_list_empty() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0xB0]);
assert_eq!(cursor.next()?, Some(Value(IonType::List, false)));
cursor.step_in()?;
assert_eq!(cursor.next()?, None);
cursor.step_out()?;
Ok(())
}
#[test]
fn test_read_list_123() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0xB6, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03]);
assert_eq!(cursor.next()?, Some(Value(IonType::List, false)));
let mut list = vec![];
cursor.step_in()?;
while let Some(Value(IonType::Integer, false)) = cursor.next()? {
list.push(cursor.read_i64()?.unwrap());
}
cursor.step_out()?;
assert_eq!(list, vec![1i64, 2, 3]);
Ok(())
}
#[test]
fn test_read_s_expression_empty() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0xC0]);
assert_eq!(cursor.next()?, Some(Value(IonType::SExpression, false)));
cursor.step_in()?;
assert_eq!(cursor.next()?, None);
cursor.step_out()?;
Ok(())
}
#[test]
fn test_read_s_expression_123() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0xC6, 0x21, 0x01, 0x21, 0x02, 0x21, 0x03]);
assert_eq!(cursor.next()?, Some(Value(IonType::SExpression, false)));
let mut sexp = vec![];
cursor.step_in()?;
while let Some(Value(IonType::Integer, false)) = cursor.next()? {
sexp.push(cursor.read_i64()?.unwrap());
}
cursor.step_out()?;
assert_eq!(sexp, vec![1i64, 2, 3]);
Ok(())
}
#[test]
fn test_read_struct_empty() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[0xD0]);
assert_eq!(cursor.next()?, Some(Value(IonType::Struct, false)));
cursor.step_in()?;
assert_eq!(cursor.next()?, None);
cursor.step_out()?;
Ok(())
}
#[test]
fn test_read_struct() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[
0xD9,
0x8A,
0x21, 0x01,
0x8B,
0x21, 0x02,
0x8C,
0x21, 0x03,
]);
assert_eq!(cursor.next()?, Some(Value(IonType::Struct, false)));
cursor.step_in()?;
assert_eq!(cursor.next()?, Some(Value(IonType::Integer, false)));
assert_eq!(cursor.field_id(), Some(10usize));
assert_eq!(cursor.read_i64()?, Some(1i64));
assert_eq!(cursor.next()?, Some(Value(IonType::Integer, false)));
assert_eq!(cursor.field_id(), Some(11usize));
assert_eq!(cursor.read_i64()?, Some(2i64));
assert_eq!(cursor.next()?, Some(Value(IonType::Integer, false)));
assert_eq!(cursor.field_id(), Some(12usize));
assert_eq!(cursor.read_i64()?, Some(3i64));
cursor.step_out()?;
Ok(())
}
#[test]
fn test_read_list_in_struct() -> IonResult<()> {
let mut cursor = ion_cursor_for(&[
0xDB,
0x8B,
0xB6,
0x21, 0x01,
0x21, 0x02,
0x21, 0x03,
0x8A,
0x21, 0x01,
]);
assert_eq!(cursor.next()?, Some(Value(IonType::Struct, false)));
cursor.step_in()?;
assert_eq!(cursor.next()?, Some(Value(IonType::List, false)));
assert_eq!(cursor.field_id(), Some(11usize));
cursor.step_in()?;
assert_eq!(cursor.next()?, Some(Value(IonType::Integer, false)));
assert_eq!(cursor.read_i64()?, Some(1i64));
assert_eq!(cursor.next()?, Some(Value(IonType::Integer, false)));
assert_eq!(cursor.read_i64()?, Some(2i64));
assert_eq!(cursor.next()?, Some(Value(IonType::Integer, false)));
assert_eq!(cursor.read_i64()?, Some(3i64));
assert_eq!(cursor.next()?, None);
cursor.step_out()?;
assert_eq!(cursor.next()?, Some(Value(IonType::Integer, false)));
assert_eq!(cursor.field_id(), Some(10usize));
assert_eq!(cursor.read_i64()?, Some(1i64));
assert_eq!(cursor.next()?, None);
cursor.step_out()?;
assert_eq!(cursor.next()?, None);
Ok(())
}
#[test]
fn test_raw_bytes() -> IonResult<()> {
let ion_data = &[
0xDB,
0x8B,
0xB6,
0x21, 0x01,
0x21, 0x02,
0x21, 0x03,
0x8A,
0x21, 0x01,
0xE3,
0x81,
0x8C,
0x10,
];
let mut cursor = ion_cursor_for(ion_data);
assert_eq!(
Some(StreamItem::Value(IonType::Struct, false)),
cursor.next()?
);
assert_eq!(cursor.raw_bytes(), Some(&ion_data[0..12]));
assert_eq!(cursor.raw_field_id_bytes(), None);
assert_eq!(cursor.raw_annotations_bytes(), None);
assert_eq!(cursor.raw_header_bytes(), Some(&ion_data[0..=0]));
assert_eq!(cursor.raw_value_bytes(), Some(&ion_data[1..12]));
cursor.step_in()?;
assert_eq!(
Some(StreamItem::Value(IonType::List, false)),
cursor.next()?
);
assert_eq!(cursor.raw_bytes(), Some(&ion_data[1..9]));
assert_eq!(cursor.raw_field_id_bytes(), Some(&ion_data[1..=1]));
assert_eq!(cursor.raw_annotations_bytes(), None);
assert_eq!(cursor.raw_header_bytes(), Some(&ion_data[2..=2]));
assert_eq!(cursor.raw_value_bytes(), Some(&ion_data[3..9]));
cursor.step_in()?;
assert_eq!(
Some(StreamItem::Value(IonType::Integer, false)),
cursor.next()?
);
assert_eq!(cursor.raw_bytes(), Some(&ion_data[3..=4]));
assert_eq!(cursor.raw_field_id_bytes(), None);
assert_eq!(cursor.raw_annotations_bytes(), None);
assert_eq!(cursor.raw_header_bytes(), Some(&ion_data[3..=3]));
assert_eq!(cursor.raw_value_bytes(), Some(&ion_data[4..=4]));
assert_eq!(
Some(StreamItem::Value(IonType::Integer, false)),
cursor.next()?
);
assert_eq!(cursor.raw_bytes(), Some(&ion_data[5..=6]));
assert_eq!(cursor.raw_field_id_bytes(), None);
assert_eq!(cursor.raw_annotations_bytes(), None);
assert_eq!(cursor.raw_header_bytes(), Some(&ion_data[5..=5]));
assert_eq!(cursor.raw_value_bytes(), Some(&ion_data[6..=6]));
assert_eq!(
Some(StreamItem::Value(IonType::Integer, false)),
cursor.next()?
);
assert_eq!(cursor.raw_bytes(), Some(&ion_data[7..=8]));
assert_eq!(cursor.raw_field_id_bytes(), None);
assert_eq!(cursor.raw_annotations_bytes(), None);
assert_eq!(cursor.raw_header_bytes(), Some(&ion_data[7..=7]));
assert_eq!(cursor.raw_value_bytes(), Some(&ion_data[8..=8]));
cursor.step_out()?;
assert_eq!(
Some(StreamItem::Value(IonType::Integer, false)),
cursor.next()?
);
assert_eq!(cursor.raw_bytes(), Some(&ion_data[9..=11]));
assert_eq!(cursor.raw_field_id_bytes(), Some(&ion_data[9..=9]));
assert_eq!(cursor.raw_annotations_bytes(), None);
assert_eq!(cursor.raw_header_bytes(), Some(&ion_data[10..=10]));
assert_eq!(cursor.raw_value_bytes(), Some(&ion_data[11..=11]));
cursor.step_out()?;
assert_eq!(
Some(StreamItem::Value(IonType::Boolean, false)),
cursor.next()?
);
assert_eq!(cursor.raw_bytes(), Some(&ion_data[12..16]));
assert_eq!(cursor.raw_field_id_bytes(), None);
assert_eq!(cursor.raw_annotations_bytes(), Some(&ion_data[12..=14]));
assert_eq!(cursor.annotation_ids(), &[12]);
assert_eq!(cursor.raw_header_bytes(), Some(&ion_data[15..=15]));
assert_eq!(cursor.raw_value_bytes(), None);
Ok(())
}
}