diff --git a/src/binary/non_blocking/raw_binary_reader.rs b/src/binary/non_blocking/raw_binary_reader.rs index ccfc37b0..3722c3ac 100644 --- a/src/binary/non_blocking/raw_binary_reader.rs +++ b/src/binary/non_blocking/raw_binary_reader.rs @@ -5,6 +5,7 @@ use crate::binary::non_blocking::type_descriptor::{Header, TypeDescriptor}; use crate::binary::uint::DecodedUInt; use crate::binary::var_uint::VarUInt; use crate::binary::IonTypeCode; +use crate::element::{Blob, Clob}; use crate::result::{ decoding_error, decoding_error_raw, illegal_operation, illegal_operation_raw, incomplete_data_error, @@ -769,8 +770,8 @@ impl> IonReader for RawBinaryBufferReader { self.read_symbol_id().map(RawSymbolToken::SymbolId) } - fn read_blob(&mut self) -> IonResult> { - self.read_blob_bytes().map(Vec::from) + fn read_blob(&mut self) -> IonResult { + self.read_blob_bytes().map(Vec::from).map(Blob::from) } fn map_blob(&mut self, f: F) -> IonResult @@ -781,8 +782,8 @@ impl> IonReader for RawBinaryBufferReader { self.read_blob_bytes().map(f) } - fn read_clob(&mut self) -> IonResult> { - self.read_clob_bytes().map(Vec::from) + fn read_clob(&mut self) -> IonResult { + self.read_clob_bytes().map(Vec::from).map(Clob::from) } fn map_clob(&mut self, f: F) -> IonResult diff --git a/src/binary/raw_binary_reader.rs b/src/binary/raw_binary_reader.rs index 93fdeff7..5c9f8628 100644 --- a/src/binary/raw_binary_reader.rs +++ b/src/binary/raw_binary_reader.rs @@ -20,6 +20,7 @@ use crate::{ }; use std::io; +use crate::element::{Blob, Clob}; use crate::raw_symbol_token::RawSymbolToken; use crate::result::{decoding_error_raw, IonError}; use crate::stream_reader::IonReader; @@ -572,8 +573,8 @@ impl IonReader for RawBinaryReader { Ok(RawSymbolToken::SymbolId(symbol_id)) } - fn read_blob(&mut self) -> IonResult> { - self.map_blob(|b| Vec::from(b)) + fn read_blob(&mut self) -> IonResult { + self.map_blob(|b| Vec::from(b)).map(Blob::from) } fn map_blob(&mut self, f: F) -> IonResult @@ -587,8 +588,8 @@ impl IonReader for RawBinaryReader { self.read_slice(number_of_bytes, |buffer: &[u8]| Ok(f(buffer))) } - fn read_clob(&mut self) -> IonResult> { - self.map_clob(|c| Vec::from(c)) + fn read_clob(&mut self) -> IonResult { + self.map_clob(|c| Vec::from(c)).map(Clob::from) } fn map_clob(&mut self, f: F) -> IonResult @@ -1181,6 +1182,7 @@ mod tests { use crate::binary::constants::v1_0::IVM; use crate::binary::raw_binary_reader::RawBinaryReader; + use crate::element::{Blob, Clob}; use crate::raw_reader::{RawStreamItem, RawStreamItem::*}; use crate::raw_symbol_token::local_sid_token; use crate::result::{IonError, IonResult}; @@ -1446,7 +1448,7 @@ mod tests { fn test_read_clob_empty() -> IonResult<()> { let mut cursor = ion_cursor_for(&[0x90]); assert_eq!(cursor.next()?, Value(IonType::Clob)); - assert_eq!(cursor.read_clob()?, vec![]); + assert_eq!(cursor.read_clob()?, Clob::from(vec![])); Ok(()) } @@ -1462,7 +1464,7 @@ mod tests { fn test_read_blob_empty() -> IonResult<()> { let mut cursor = ion_cursor_for(&[0xA0]); assert_eq!(cursor.next()?, Value(IonType::Blob)); - assert_eq!(cursor.read_blob()?, vec![]); + assert_eq!(cursor.read_blob()?, Blob::from(vec![])); Ok(()) } @@ -1470,7 +1472,7 @@ mod tests { fn test_read_blob_123() -> IonResult<()> { let mut cursor = ion_cursor_for(&[0xA3, 0x01, 0x02, 0x03]); assert_eq!(cursor.next()?, Value(IonType::Blob)); - assert_eq!(cursor.read_blob()?, vec![1u8, 2, 3]); + assert_eq!(cursor.read_blob()?, Blob::from(vec![1u8, 2, 3])); Ok(()) } diff --git a/src/binary/raw_binary_writer.rs b/src/binary/raw_binary_writer.rs index 142222d0..98049952 100644 --- a/src/binary/raw_binary_writer.rs +++ b/src/binary/raw_binary_writer.rs @@ -910,6 +910,7 @@ mod writer_tests { use rstest::*; use super::*; + use crate::element::{Blob, Clob}; use crate::raw_symbol_token::{local_sid_token, RawSymbolToken}; use crate::reader::{Reader, ReaderBuilder}; use crate::symbol::Symbol; @@ -1111,17 +1112,20 @@ mod writer_tests { .map(|s| s.as_bytes()) .collect(); + let clobs: Vec = values.iter().map(|b| Clob::from(*b)).collect(); + let blobs: Vec = values.iter().map(|b| Blob::from(*b)).collect(); + binary_writer_scalar_test( - &values, + clobs.as_slice(), IonType::Clob, - |writer, v| writer.write_clob(*v), + |writer, v| writer.write_clob(v), |reader| reader.read_clob(), )?; binary_writer_scalar_test( - &values, + blobs.as_slice(), IonType::Blob, - |writer, v| writer.write_blob(*v), + |writer, v| writer.write_blob(v), |reader| reader.read_blob(), ) } diff --git a/src/element/bytes.rs b/src/element/bytes.rs new file mode 100644 index 00000000..dc2b7b71 --- /dev/null +++ b/src/element/bytes.rs @@ -0,0 +1,50 @@ +/// An owned, immutable byte array. +/// ```rust +/// use ion_rs::element::Bytes; +/// let ivm: &[u8] = &[0xEA_u8, 0x01, 0x00, 0xE0]; // Ion 1.0 version marker +/// let bytes: Bytes = ivm.into(); +/// assert_eq!(&bytes, ivm); +/// ``` +/// ```rust +/// use ion_rs::element::Bytes; +/// let bytes: Bytes = "hello".into(); +/// assert_eq!(&bytes, "hello".as_bytes()); +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Bytes { + data: Vec, +} + +impl PartialEq<[u8]> for Bytes { + fn eq(&self, other: &[u8]) -> bool { + self.as_ref().eq(other) + } +} + +impl From> for Bytes { + fn from(data: Vec) -> Self { + Bytes { data } + } +} + +impl From<&[u8]> for Bytes { + fn from(data: &[u8]) -> Self { + Bytes { + data: data.to_vec(), + } + } +} + +impl From<&str> for Bytes { + fn from(text: &str) -> Self { + Bytes { + data: text.as_bytes().into(), + } + } +} + +impl AsRef<[u8]> for Bytes { + fn as_ref(&self) -> &[u8] { + &self.data + } +} diff --git a/src/element/element_stream_reader.rs b/src/element/element_stream_reader.rs index 34963360..0b01e53b 100644 --- a/src/element/element_stream_reader.rs +++ b/src/element/element_stream_reader.rs @@ -2,7 +2,7 @@ use crate::result::{decoding_error, illegal_operation, illegal_operation_raw}; use crate::text::parent_container::ParentContainer; use crate::element::iterators::SymbolsIterator; -use crate::element::Element; +use crate::element::{Blob, Clob, Element}; use crate::{ Decimal, Int, IonError, IonReader, IonResult, IonType, Str, StreamItem, Symbol, Timestamp, }; @@ -265,8 +265,8 @@ impl IonReader for ElementStreamReader { self.current_value_as("symbol value", |v| v.as_symbol().map(|i| i.to_owned())) } - fn read_blob(&mut self) -> IonResult> { - self.map_blob(|b| Vec::from(b)) + fn read_blob(&mut self) -> IonResult { + self.map_blob(|b| Vec::from(b)).map(Blob::from) } fn map_blob(&mut self, f: F) -> IonResult @@ -280,8 +280,8 @@ impl IonReader for ElementStreamReader { } } - fn read_clob(&mut self) -> IonResult> { - self.map_clob(|c| Vec::from(c)) + fn read_clob(&mut self) -> IonResult { + self.map_clob(|c| Vec::from(c)).map(Clob::from) } fn map_clob(&mut self, f: F) -> IonResult @@ -380,7 +380,6 @@ mod reader_tests { use rstest::*; use super::*; - use crate::element::Value; use crate::result::IonResult; use crate::stream_reader::IonReader; use crate::types::decimal::Decimal; @@ -504,9 +503,9 @@ mod reader_tests { next_type(reader, IonType::List, false); reader.step_in()?; next_type(reader, IonType::Blob, false); - assert_eq!(&reader.read_blob()?, "encoded".as_bytes()); + assert_eq!(reader.read_blob()?, Blob::from("encoded")); next_type(reader, IonType::Clob, false); - assert_eq!(&reader.read_clob()?, "hello".as_bytes()); + assert_eq!(reader.read_clob()?, Clob::from("hello")); next_type(reader, IonType::Float, false); assert_eq!(reader.read_f64()?, 4.5); next_type(reader, IonType::Decimal, false); @@ -535,8 +534,8 @@ mod reader_tests { #[case(" 2007-07-12T ", Timestamp::with_ymd(2007, 7, 12).build().unwrap())] #[case(" foo ", Symbol::owned("foo"))] #[case(" \"hi!\" ", "hi!".to_owned())] - #[case(" {{ZW5jb2RlZA==}} ", Value::Blob("encoded".as_bytes().to_vec()))] - #[case(" {{\"hello\"}} ", Value::Clob("hello".as_bytes().to_vec()))] + #[case(" {{ZW5jb2RlZA==}} ", Blob::from("encoded"))] + #[case(" {{\"hello\"}} ", Clob::from("hello"))] fn test_read_single_top_level_values>( #[case] text: &str, #[case] expected_value: E, diff --git a/src/element/lob.rs b/src/element/lob.rs new file mode 100644 index 00000000..8622b61a --- /dev/null +++ b/src/element/lob.rs @@ -0,0 +1,116 @@ +use crate::element::Bytes; + +/// An in-memory representation of an Ion blob. +/// +/// ```rust +/// use ion_rs::element::Blob; +/// let ivm: &[u8] = &[0xEA_u8, 0x01, 0x00, 0xE0]; // Ion 1.0 version marker +/// let blob: Blob = ivm.into(); +/// assert_eq!(&blob, ivm); +/// assert_eq!(blob.as_slice().len(), 4); +/// ``` +/// ```rust +/// use ion_rs::element::Blob; +/// let blob: Blob = "hello".into(); +/// assert_eq!(&blob, "hello".as_bytes()); +/// assert_eq!(blob.as_slice().len(), 5); +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Blob(pub Bytes); + +impl Blob { + pub fn as_slice(&self) -> &[u8] { + self.as_ref() + } +} + +/// An in-memory representation of an Ion clob. +/// +/// ```rust +/// use ion_rs::element::Clob; +/// let clob: Clob = "hello".into(); +/// assert_eq!(&clob, "hello".as_bytes()); +/// assert_eq!(clob.as_slice().len(), 5); +/// ``` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Clob(pub Bytes); + +impl Clob { + pub fn as_slice(&self) -> &[u8] { + self.as_ref() + } +} + +impl AsRef<[u8]> for Blob { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl AsRef<[u8]> for Clob { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl PartialEq<[u8]> for Blob { + fn eq(&self, other: &[u8]) -> bool { + self.as_ref().eq(other) + } +} + +impl PartialEq<[u8]> for Clob { + fn eq(&self, other: &[u8]) -> bool { + self.as_ref().eq(other) + } +} + +impl From for Bytes { + fn from(blob: Blob) -> Self { + blob.0 + } +} + +impl From for Bytes { + fn from(clob: Clob) -> Self { + clob.0 + } +} + +impl From> for Blob { + fn from(data: Vec) -> Self { + let bytes: Bytes = data.into(); + Blob(bytes) + } +} + +impl From> for Clob { + fn from(data: Vec) -> Self { + let bytes: Bytes = data.into(); + Clob(bytes) + } +} + +impl From<&[u8]> for Blob { + fn from(data: &[u8]) -> Self { + Blob::from(data.to_vec()) + } +} + +impl From<&[u8]> for Clob { + fn from(data: &[u8]) -> Self { + Clob::from(data.to_vec()) + } +} + +impl From<&str> for Blob { + fn from(text: &str) -> Self { + text.as_bytes().into() + } +} + +impl From<&str> for Clob { + fn from(text: &str) -> Self { + text.as_bytes().into() + } +} diff --git a/src/element/mod.rs b/src/element/mod.rs index eb226086..efda4b4e 100644 --- a/src/element/mod.rs +++ b/src/element/mod.rs @@ -22,9 +22,11 @@ use num_bigint::BigInt; use std::fmt::{Display, Formatter}; pub mod builders; +mod bytes; mod element_stream_reader; mod iterators; mod list; +mod lob; pub mod reader; mod sequence; mod sexp; @@ -32,6 +34,9 @@ mod r#struct; pub mod writer; // Re-export the Value variant types and traits so they can be accessed directly from this module. +pub use self::bytes::Bytes; +pub use lob::{Blob, Clob}; + pub use list::List; pub use r#struct::Struct; pub use sequence::Sequence; @@ -64,8 +69,8 @@ pub enum Value { String(Str), Symbol(Symbol), Bool(bool), - Blob(Vec), - Clob(Vec), + Blob(Bytes), + Clob(Bytes), SExp(Sequence), List(Sequence), Struct(Struct), @@ -176,7 +181,21 @@ impl From<&[u8]> for Value { impl From> for Value { fn from(value: Vec) -> Self { - Value::Blob(value) + Value::Blob(value.into()) + } +} + +impl From for Value { + fn from(blob: Blob) -> Self { + let bytes: Bytes = blob.into(); + Value::Blob(bytes) + } +} + +impl From for Value { + fn from(clob: Clob) -> Self { + let bytes: Bytes = clob.into(); + Value::Clob(bytes) } } @@ -415,21 +434,21 @@ impl Element { pub fn as_lob(&self) -> Option<&[u8]> { match &self.value { - Value::Blob(bytes) | Value::Clob(bytes) => Some(bytes), + Value::Blob(bytes) | Value::Clob(bytes) => Some(bytes.as_ref()), _ => None, } } pub fn as_blob(&self) -> Option<&[u8]> { match &self.value { - Value::Blob(bytes) => Some(bytes), + Value::Blob(bytes) => Some(bytes.as_ref()), _ => None, } } pub fn as_clob(&self) -> Option<&[u8]> { match &self.value { - Value::Clob(bytes) => Some(bytes), + Value::Clob(bytes) => Some(bytes.as_ref()), _ => None, } } diff --git a/src/element/reader.rs b/src/element/reader.rs index c7d9b0d3..9d20fafc 100644 --- a/src/element/reader.rs +++ b/src/element/reader.rs @@ -147,8 +147,8 @@ impl<'a, R: IonReader> ElementLoader<'a, R> Timestamp => Value::Timestamp(self.reader.read_timestamp()?), Symbol => Value::Symbol(self.reader.read_symbol()?), String => Value::String(self.reader.read_string()?), - Clob => Value::Clob(self.reader.read_clob()?), - Blob => Value::Blob(self.reader.read_blob()?), + Clob => Value::Clob(self.reader.read_clob()?.into()), + Blob => Value::Blob(self.reader.read_blob()?.into()), // It's a collection; recursively materialize all of this value's children List => Value::List(self.materialize_sequence()?), SExp => Value::SExp(self.materialize_sequence()?), diff --git a/src/raw_reader.rs b/src/raw_reader.rs index 05aeb01d..feb483d0 100644 --- a/src/raw_reader.rs +++ b/src/raw_reader.rs @@ -1,3 +1,4 @@ +use crate::element::{Blob, Clob}; use crate::raw_symbol_token::RawSymbolToken; use crate::stream_reader::IonReader; use crate::types::string::Str; @@ -109,7 +110,7 @@ impl IonReader for Box { (**self).read_symbol() } - fn read_blob(&mut self) -> IonResult> { + fn read_blob(&mut self) -> IonResult { (**self).read_blob() } @@ -121,7 +122,7 @@ impl IonReader for Box { todo!("Cannot use `map_blob` via dynamic dispatch. Use `read_blob` instead. See: https://github.com/amazon-ion/ion-rust/issues/335") } - fn read_clob(&mut self) -> IonResult> { + fn read_clob(&mut self) -> IonResult { (**self).read_clob() } diff --git a/src/reader.rs b/src/reader.rs index f0249f40..f84fe0fb 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -7,6 +7,7 @@ use delegate::delegate; use crate::binary::constants::v1_0::IVM; use crate::constants::v1_0::system_symbol_ids; use crate::data_source::ToIonDataSource; +use crate::element::{Blob, Clob}; use crate::raw_reader::{RawReader, RawStreamItem}; use crate::raw_symbol_token::RawSymbolToken; use crate::result::{decoding_error, decoding_error_raw, IonResult}; @@ -435,9 +436,9 @@ impl IonReader for UserReader { fn read_string(&mut self) -> IonResult; fn map_string(&mut self, f: F) -> IonResult where F: FnOnce(&str) -> U; fn map_string_bytes(&mut self, f: F) -> IonResult where F: FnOnce(&[u8]) -> U; - fn read_blob(&mut self) -> IonResult>; + fn read_blob(&mut self) -> IonResult; fn map_blob(&mut self, f: F) -> IonResult where F: FnOnce(&[u8]) -> U; - fn read_clob(&mut self) -> IonResult>; + fn read_clob(&mut self) -> IonResult; fn map_clob(&mut self, f: F) -> IonResult where F: FnOnce(&[u8]) -> U; fn read_timestamp(&mut self) -> IonResult; fn step_in(&mut self) -> IonResult<()>; diff --git a/src/stream_reader.rs b/src/stream_reader.rs index f59bc6e6..e1686e1e 100644 --- a/src/stream_reader.rs +++ b/src/stream_reader.rs @@ -1,3 +1,4 @@ +use crate::element::{Blob, Clob}; use crate::result::IonResult; use crate::types::decimal::Decimal; use crate::types::integer::Int; @@ -132,7 +133,7 @@ pub trait IonReader { /// Attempts to read the current item as an Ion blob and return it as a `Vec`. If the /// current item is not a blob or an IO error is encountered while reading, returns [crate::IonError]. - fn read_blob(&mut self) -> IonResult>; + fn read_blob(&mut self) -> IonResult; /// Takes a function that expects a byte slice and, once the blob's bytes are loaded, calls that /// function passing the blob's bytes as a parameter. This allows users to avoid materializing the @@ -146,7 +147,7 @@ pub trait IonReader { /// Attempts to read the current item as an Ion clob and return it as a `Vec`. If the /// current item is not a clob or an IO error is encountered while reading, returns [crate::IonError]. - fn read_clob(&mut self) -> IonResult>; + fn read_clob(&mut self) -> IonResult; /// Takes a function that expects a byte slice and, once the clob's bytes are loaded, calls that /// function passing the clob's bytes as a parameter. This allows users to avoid materializing the diff --git a/src/system_reader.rs b/src/system_reader.rs index f96080f0..6543754a 100644 --- a/src/system_reader.rs +++ b/src/system_reader.rs @@ -4,6 +4,7 @@ use std::ops::Range; use delegate::delegate; use crate::constants::v1_0::{system_symbol_ids, SYSTEM_SYMBOLS}; +use crate::element::{Blob, Clob}; use crate::raw_reader::{RawReader, RawStreamItem}; use crate::raw_symbol_token::RawSymbolToken; use crate::result::{decoding_error, decoding_error_raw, illegal_operation, IonError, IonResult}; @@ -711,9 +712,9 @@ impl IonReader for SystemReader { fn read_f32(&mut self) -> IonResult; fn read_f64(&mut self) -> IonResult; fn read_decimal(&mut self) -> IonResult; - fn read_blob(&mut self) -> IonResult>; + fn read_blob(&mut self) -> IonResult; fn map_blob(&mut self, f: F) -> IonResult where F: FnOnce(&[u8]) -> U; - fn read_clob(&mut self) -> IonResult>; + fn read_clob(&mut self) -> IonResult; fn map_clob(&mut self, f: F) -> IonResult where F: FnOnce(&[u8]) -> U; fn read_timestamp(&mut self) -> IonResult; fn depth(&self) -> usize; diff --git a/src/text/non_blocking/raw_text_reader.rs b/src/text/non_blocking/raw_text_reader.rs index 5546800e..4770935f 100644 --- a/src/text/non_blocking/raw_text_reader.rs +++ b/src/text/non_blocking/raw_text_reader.rs @@ -1,5 +1,6 @@ use std::fmt::Display; +use crate::element::{Blob, Clob}; use crate::types::string::Str; use nom::Err::{Error, Failure, Incomplete}; @@ -850,8 +851,8 @@ impl> IonReader for RawTextReader { } } - fn read_blob(&mut self) -> IonResult> { - self.map_blob(|b| Vec::from(b)) + fn read_blob(&mut self) -> IonResult { + self.map_blob(|b| Vec::from(b)).map(Blob::from) } fn map_blob(&mut self, f: F) -> IonResult @@ -865,8 +866,8 @@ impl> IonReader for RawTextReader { } } - fn read_clob(&mut self) -> IonResult> { - self.map_clob(|c| Vec::from(c)) + fn read_clob(&mut self) -> IonResult { + self.map_clob(|c| Vec::from(c)).map(Clob::from) } fn map_clob(&mut self, f: F) -> IonResult diff --git a/src/text/raw_text_reader.rs b/src/text/raw_text_reader.rs index 191e0e33..87fd8241 100644 --- a/src/text/raw_text_reader.rs +++ b/src/text/raw_text_reader.rs @@ -1,4 +1,5 @@ use crate::data_source::ToIonDataSource; +use crate::element::{Blob, Clob}; use crate::raw_reader::RawStreamItem; use crate::raw_symbol_token::RawSymbolToken; use crate::result::IonResult; @@ -167,8 +168,8 @@ impl IonReader for RawTextReader { self.reader.read_symbol() } - fn read_blob(&mut self) -> IonResult> { - self.map_blob(|b| Vec::from(b)) + fn read_blob(&mut self) -> IonResult { + self.map_blob(|b| Vec::from(b)).map(Blob::from) } fn map_blob(&mut self, f: F) -> IonResult @@ -179,8 +180,8 @@ impl IonReader for RawTextReader { self.reader.map_blob(f) } - fn read_clob(&mut self) -> IonResult> { - self.map_clob(|c| Vec::from(c)) + fn read_clob(&mut self) -> IonResult { + self.map_clob(|c| Vec::from(c)).map(Clob::from) } fn map_clob(&mut self, f: F) -> IonResult