use ion_c_sys_macros::position_error;
use std::convert::{TryFrom, TryInto};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
use std::ptr;
use crate::result::*;
use crate::string::*;
use crate::*;
pub trait IonCReader {
fn next(&mut self) -> IonCResult<ION_TYPE>;
fn get_type(&self) -> IonCResult<ION_TYPE>;
fn step_in(&mut self) -> IonCResult<()>;
fn step_out(&mut self) -> IonCResult<()>;
fn depth(&self) -> IonCResult<i32>;
fn is_null(&self) -> IonCResult<bool>;
fn is_in_struct(&self) -> IonCResult<bool>;
fn get_field_name(&mut self) -> IonCResult<StrSliceRef>;
fn get_annotations(&mut self) -> IonCResult<StrSlicesRef>;
fn read_bool(&mut self) -> IonCResult<bool>;
fn read_i64(&mut self) -> IonCResult<i64>;
fn read_bigint(&mut self) -> IonCResult<BigInt>;
fn read_f64(&mut self) -> IonCResult<f64>;
fn read_bigdecimal(&mut self) -> IonCResult<BigDecimal>;
fn read_datetime(&mut self) -> IonCResult<IonDateTime>;
fn read_string(&mut self) -> IonCResult<StrSliceRef>;
fn read_bytes(&mut self) -> IonCResult<Vec<u8>>;
fn pos(&self) -> IonCResult<Position>;
}
pub struct IonCReaderHandle<'a> {
reader: hREADER,
referent: PhantomData<&'a [u8]>,
}
impl<'a> IonCReaderHandle<'a> {
pub fn try_from_buf(
src: &'a [u8],
options: &mut ION_READER_OPTIONS,
) -> Result<Self, IonCError> {
let mut reader = ptr::null_mut();
ionc!(ion_reader_open_buffer(
&mut reader,
src.as_ptr() as *mut u8,
src.len().try_into()?,
options,
))?;
Ok(IonCReaderHandle {
reader,
referent: PhantomData::default(),
})
}
}
impl<'a> IonCReader for IonCReaderHandle<'a> {
#[position_error]
#[inline]
fn next(&mut self) -> IonCResult<ION_TYPE> {
let mut tid = ptr::null_mut();
ionc!(ion_reader_next(self.reader, &mut tid))?;
Ok(tid)
}
#[position_error]
#[inline]
fn get_type(&self) -> IonCResult<ION_TYPE> {
let mut tid = ptr::null_mut();
ionc!(ion_reader_get_type(self.reader, &mut tid))?;
Ok(tid)
}
#[position_error]
#[inline]
fn step_in(&mut self) -> IonCResult<()> {
ionc!(ion_reader_step_in(self.reader))
}
#[position_error]
#[inline]
fn step_out(&mut self) -> IonCResult<()> {
ionc!(ion_reader_step_out(self.reader))
}
#[position_error]
#[inline]
fn depth(&self) -> IonCResult<i32> {
let mut depth = 0;
ionc!(ion_reader_get_depth(self.reader, &mut depth))?;
Ok(depth)
}
#[position_error]
#[inline]
fn is_null(&self) -> IonCResult<bool> {
let mut is_null = 0;
ionc!(ion_reader_is_null(self.reader, &mut is_null))?;
Ok(is_null != 0)
}
#[position_error]
#[inline]
fn is_in_struct(&self) -> IonCResult<bool> {
let mut is_in_struct = 0;
ionc!(ion_reader_is_in_struct(self.reader, &mut is_in_struct))?;
Ok(is_in_struct != 0)
}
#[position_error]
#[inline]
fn get_field_name(&mut self) -> IonCResult<StrSliceRef> {
let mut field = ION_STRING::default();
ionc!(ion_reader_get_field_name(self.reader, &mut field))?;
let field_str = field.as_str(PhantomData::<&'a u8>::default())?;
Ok(StrSliceRef::new(self, field_str))
}
fn get_annotations(&mut self) -> IonCResult<StrSlicesRef> {
let mut raw_len = 0;
ionc!(ion_reader_get_annotation_count(self.reader, &mut raw_len))?;
let len: usize = raw_len.try_into()?;
let mut annotations = Vec::new();
let mut curr = ION_STRING::default();
for i in 0..len {
ionc!(ion_reader_get_an_annotation(
self.reader,
i as c_int,
&mut curr
))?;
annotations.push(curr.as_str(PhantomData::<&'a u8>::default())?);
}
Ok(StrSlicesRef::new(self, annotations))
}
#[position_error]
#[inline]
fn read_bool(&mut self) -> IonCResult<bool> {
let mut value = 0;
ionc!(ion_reader_read_bool(self.reader, &mut value))?;
Ok(value != 0)
}
#[position_error]
#[inline]
fn read_i64(&mut self) -> IonCResult<i64> {
let mut value = 0;
ionc!(ion_reader_read_int64(self.reader, &mut value))?;
Ok(value)
}
#[position_error]
#[inline]
fn read_bigint(&mut self) -> IonCResult<BigInt> {
let mut value = ION_INT::default();
ionc!(ion_reader_read_ion_int(self.reader, &mut value))?;
value.try_to_bigint()
}
#[position_error]
#[inline]
fn read_f64(&mut self) -> IonCResult<f64> {
let mut value = 0.0;
ionc!(ion_reader_read_double(self.reader, &mut value))?;
Ok(value)
}
#[position_error]
#[inline]
fn read_bigdecimal(&mut self) -> IonCResult<BigDecimal> {
let mut value = ION_DECIMAL::default();
ionc!(ion_reader_read_ion_decimal(self.reader, &mut value))?;
value.try_to_bigdecimal()
}
#[position_error]
#[inline]
fn read_datetime(&mut self) -> IonCResult<IonDateTime> {
let mut value = ION_TIMESTAMP::default();
ionc!(ion_reader_read_timestamp(self.reader, &mut value))?;
value.try_to_iondt()
}
#[position_error]
#[inline]
fn read_string(&mut self) -> IonCResult<StrSliceRef> {
let mut value = ION_STRING::default();
ionc!(ion_reader_read_string(self.reader, &mut value))?;
let str_ref = value.as_str(PhantomData::<&'a u8>::default())?;
Ok(StrSliceRef::new(self, str_ref))
}
#[position_error]
#[inline]
fn read_bytes(&mut self) -> IonCResult<Vec<u8>> {
let mut len = 0;
ionc!(ion_reader_get_lob_size(self.reader, &mut len))?;
if len == 0 {
return Ok(Vec::new());
}
let mut read_len = 0;
let mut buf = vec![0; len.try_into()?];
ionc!(ion_reader_read_lob_bytes(
self.reader,
buf.as_mut_ptr(),
buf.len().try_into()?,
&mut read_len
))?;
if len != read_len {
Err(IonCError::from(ion_error_code_IERR_INVALID_STATE))
} else {
Ok(buf)
}
}
#[inline]
fn pos(&self) -> IonCResult<Position> {
reader_current_pos(&self.reader)
}
}
pub fn include_current_position(reader: &hREADER, err: IonCError) -> IonCError {
if !std::matches!(err.position, Position::Unknown) {
return err;
}
let pos = match reader_current_pos(reader) {
Ok(pos) => pos,
Err(_) => return err, };
err.with_position(pos)
}
#[inline]
fn reader_current_pos(reader: &hREADER) -> IonCResult<Position> {
let mut bytes: i64 = -1;
let mut line = -1;
let mut offset = -1;
ionc!(ion_reader_get_position(
*reader,
&mut bytes,
&mut line,
&mut offset
))?;
Ok(match (bytes, line, offset) {
(b, -1, -1) if b > 0 => Position::Offset(b),
(b, l, o) if b > 0 && l > 0 && o > 0 => Position::OffsetLineColumn(b, LineColumn(l, o)),
_ => Position::Unknown,
})
}
impl<'a> TryFrom<&'a [u8]> for IonCReaderHandle<'a> {
type Error = IonCError;
#[inline]
fn try_from(src: &'a [u8]) -> Result<Self, Self::Error> {
Self::try_from_buf(src, &mut ION_READER_OPTIONS::default())
}
}
impl<'a> TryFrom<&'a str> for IonCReaderHandle<'a> {
type Error = IonCError;
#[inline]
fn try_from(src: &'a str) -> Result<Self, Self::Error> {
Self::try_from(src.as_bytes())
}
}
impl Deref for IonCReaderHandle<'_> {
type Target = hREADER;
#[inline]
fn deref(&self) -> &Self::Target {
&self.reader
}
}
impl DerefMut for IonCReaderHandle<'_> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.reader
}
}
impl Drop for IonCReaderHandle<'_> {
fn drop(&mut self) {
if !self.reader.is_null() {
ionc!(ion_reader_close(self.reader)).unwrap()
}
}
}
#[cfg(test)]
mod reader_tests {
use super::*;
#[test]
fn position_error() -> IonCResult<()> {
let data = r#"{foo:"bar",baz:"#;
let mut reader = IonCReaderHandle::try_from(data)?;
reader.next()?;
let err = match reader.next() {
Err(e) => e,
Ok(t) => panic!("expected an error, found a {:?}", t),
};
assert_eq!(err.position, Position::text(15, 1, 16));
Ok(())
}
}