blob: 6fd61d131b1f6bd1e2a74f3b4c66bf79e4de47b8 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use crate::decode::decode_internal;
use crate::error::Details;
use crate::headers::{HeaderBuilder, RabinFingerprintHeader};
use crate::schema::ResolvedOwnedSchema;
use crate::types::Value;
use crate::{AvroResult, AvroSchema, Schema, from_value};
use serde::de::DeserializeOwned;
use std::io::Read;
use std::marker::PhantomData;
pub struct GenericSingleObjectReader {
write_schema: ResolvedOwnedSchema,
expected_header: Vec<u8>,
}
impl GenericSingleObjectReader {
pub fn new(schema: Schema) -> AvroResult<GenericSingleObjectReader> {
let header_builder = RabinFingerprintHeader::from_schema(&schema);
Self::new_with_header_builder(schema, header_builder)
}
pub fn new_with_header_builder<HB: HeaderBuilder>(
schema: Schema,
header_builder: HB,
) -> AvroResult<GenericSingleObjectReader> {
let expected_header = header_builder.build_header();
Ok(GenericSingleObjectReader {
write_schema: ResolvedOwnedSchema::try_from(schema)?,
expected_header,
})
}
pub fn read_value<R: Read>(&self, reader: &mut R) -> AvroResult<Value> {
let mut header = vec![0; self.expected_header.len()];
match reader.read_exact(&mut header) {
Ok(_) => {
if self.expected_header == header {
decode_internal(
self.write_schema.get_root_schema(),
self.write_schema.get_names(),
&None,
reader,
)
} else {
Err(
Details::SingleObjectHeaderMismatch(self.expected_header.clone(), header)
.into(),
)
}
}
Err(io_error) => Err(Details::ReadHeader(io_error).into()),
}
}
}
pub struct SpecificSingleObjectReader<T>
where
T: AvroSchema,
{
inner: GenericSingleObjectReader,
_model: PhantomData<T>,
}
impl<T> SpecificSingleObjectReader<T>
where
T: AvroSchema,
{
pub fn new() -> AvroResult<SpecificSingleObjectReader<T>> {
Ok(SpecificSingleObjectReader {
inner: GenericSingleObjectReader::new(T::get_schema())?,
_model: PhantomData,
})
}
}
impl<T> SpecificSingleObjectReader<T>
where
T: AvroSchema + From<Value>,
{
pub fn read_from_value<R: Read>(&self, reader: &mut R) -> AvroResult<T> {
self.inner.read_value(reader).map(|v| v.into())
}
}
impl<T> SpecificSingleObjectReader<T>
where
T: AvroSchema + DeserializeOwned,
{
pub fn read<R: Read>(&self, reader: &mut R) -> AvroResult<T> {
from_value::<T>(&self.inner.read_value(reader)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::encode::encode;
use crate::headers::GlueSchemaUuidHeader;
use crate::rabin::Rabin;
use crate::{AvroSchema, Error, Schema};
use apache_avro_test_helper::TestResult;
use serde::Deserialize;
use uuid::Uuid;
#[derive(Deserialize, Clone, PartialEq, Debug)]
struct TestSingleObjectReader {
a: i64,
b: f64,
c: Vec<String>,
}
impl AvroSchema for TestSingleObjectReader {
fn get_schema() -> Schema {
let schema = r#"
{
"type":"record",
"name":"TestSingleObjectWrtierSerialize",
"fields":[
{
"name":"a",
"type":"long"
},
{
"name":"b",
"type":"double"
},
{
"name":"c",
"type":{
"type":"array",
"items":"string"
}
}
]
}
"#;
Schema::parse_str(schema).unwrap()
}
}
impl From<Value> for TestSingleObjectReader {
fn from(obj: Value) -> TestSingleObjectReader {
if let Value::Record(fields) = obj {
let mut a = None;
let mut b = None;
let mut c = vec![];
for (field_name, v) in fields {
match (field_name.as_str(), v) {
("a", Value::Long(i)) => a = Some(i),
("b", Value::Double(d)) => b = Some(d),
("c", Value::Array(v)) => {
for inner_val in v {
if let Value::String(s) = inner_val {
c.push(s);
}
}
}
(key, value) => panic!("Unexpected pair: {key:?} -> {value:?}"),
}
}
TestSingleObjectReader {
a: a.unwrap(),
b: b.unwrap(),
c,
}
} else {
panic!("Expected a Value::Record but was {obj:?}")
}
}
}
impl From<TestSingleObjectReader> for Value {
fn from(obj: TestSingleObjectReader) -> Value {
Value::Record(vec![
("a".into(), obj.a.into()),
("b".into(), obj.b.into()),
(
"c".into(),
Value::Array(obj.c.into_iter().map(|s| s.into()).collect()),
),
])
}
}
#[test]
fn test_avro_3507_single_object_reader() -> TestResult {
let obj = TestSingleObjectReader {
a: 42,
b: 3.33,
c: vec!["cat".into(), "dog".into()],
};
let mut to_read = Vec::<u8>::new();
to_read.extend_from_slice(&[0xC3, 0x01]);
to_read.extend_from_slice(
&TestSingleObjectReader::get_schema()
.fingerprint::<Rabin>()
.bytes[..],
);
encode(
&obj.clone().into(),
&TestSingleObjectReader::get_schema(),
&mut to_read,
)
.expect("Encode should succeed");
let mut to_read = &to_read[..];
let generic_reader = GenericSingleObjectReader::new(TestSingleObjectReader::get_schema())
.expect("Schema should resolve");
let val = generic_reader
.read_value(&mut to_read)
.expect("Should read");
let expected_value: Value = obj.into();
pretty_assertions::assert_eq!(expected_value, val);
Ok(())
}
#[test]
fn avro_3642_test_single_object_reader_incomplete_reads() -> TestResult {
let obj = TestSingleObjectReader {
a: 42,
b: 3.33,
c: vec!["cat".into(), "dog".into()],
};
// The two-byte marker, to show that the message uses this single-record format
let to_read_1 = [0xC3, 0x01];
let mut to_read_2 = Vec::<u8>::new();
to_read_2.extend_from_slice(
&TestSingleObjectReader::get_schema()
.fingerprint::<Rabin>()
.bytes[..],
);
let mut to_read_3 = Vec::<u8>::new();
encode(
&obj.clone().into(),
&TestSingleObjectReader::get_schema(),
&mut to_read_3,
)
.expect("Encode should succeed");
let mut to_read = (&to_read_1[..]).chain(&to_read_2[..]).chain(&to_read_3[..]);
let generic_reader = GenericSingleObjectReader::new(TestSingleObjectReader::get_schema())
.expect("Schema should resolve");
let val = generic_reader
.read_value(&mut to_read)
.expect("Should read");
let expected_value: Value = obj.into();
pretty_assertions::assert_eq!(expected_value, val);
Ok(())
}
#[test]
fn test_avro_3507_reader_parity() -> TestResult {
let obj = TestSingleObjectReader {
a: 42,
b: 3.33,
c: vec!["cat".into(), "dog".into()],
};
let mut to_read = Vec::<u8>::new();
to_read.extend_from_slice(&[0xC3, 0x01]);
to_read.extend_from_slice(
&TestSingleObjectReader::get_schema()
.fingerprint::<Rabin>()
.bytes[..],
);
encode(
&obj.clone().into(),
&TestSingleObjectReader::get_schema(),
&mut to_read,
)
.expect("Encode should succeed");
let generic_reader = GenericSingleObjectReader::new(TestSingleObjectReader::get_schema())
.expect("Schema should resolve");
let specific_reader = SpecificSingleObjectReader::<TestSingleObjectReader>::new()
.expect("schema should resolve");
let mut to_read1 = &to_read[..];
let mut to_read2 = &to_read[..];
let mut to_read3 = &to_read[..];
let val = generic_reader
.read_value(&mut to_read1)
.expect("Should read");
let read_obj1 = specific_reader
.read_from_value(&mut to_read2)
.expect("Should read from value");
let read_obj2 = specific_reader
.read(&mut to_read3)
.expect("Should read from deserilize");
let expected_value: Value = obj.clone().into();
pretty_assertions::assert_eq!(obj, read_obj1);
pretty_assertions::assert_eq!(obj, read_obj2);
pretty_assertions::assert_eq!(val, expected_value);
Ok(())
}
#[test]
fn avro_rs_164_generic_reader_alternate_header() -> TestResult {
let schema_uuid = Uuid::parse_str("b2f1cf00-0434-013e-439a-125eb8485a5f")?;
let header_builder = GlueSchemaUuidHeader::from_uuid(schema_uuid);
let generic_reader = GenericSingleObjectReader::new_with_header_builder(
TestSingleObjectReader::get_schema(),
header_builder,
)
.expect("failed to build reader");
let data_to_read: Vec<u8> = vec![
3, 0, 178, 241, 207, 0, 4, 52, 1, 62, 67, 154, 18, 94, 184, 72, 90, 95,
];
let mut to_read = &data_to_read[..];
let read_result = generic_reader
.read_value(&mut to_read)
.map_err(Error::into_details);
matches!(read_result, Err(Details::ReadBytes(_)));
Ok(())
}
}