This is an automated email from the ASF dual-hosted git repository. mgrigorov pushed a commit to branch edition-2024 in repository https://gitbox.apache.org/repos/asf/avro-rs.git
commit 713a6e51285f1d6810a9b89a1cf1dde7be69dfcb Author: Martin Tzvetanov Grigorov <[email protected]> AuthorDate: Thu Nov 28 13:34:47 2024 +0200 chore: Bump Rust edition to 2024 See https://blog.rust-lang.org/2024/11/27/Rust-2024-public-testing.html ``` Install the most recent nightly with rustup update nightly. In your project, run cargo +nightly fix --edition. Edit Cargo.toml and change the edition field to say edition = "2024" and, if you have a rust-version specified, set rust-version = "1.85". Run cargo +nightly check to verify your project now works in the new edition. Run some tests, and try out the new features! ``` Signed-off-by: Martin Tzvetanov Grigorov <[email protected]> --- Cargo.toml | 2 +- avro/benches/serde.rs | 4 +- avro/benches/serde_json.rs | 2 +- avro/benches/single.rs | 2 +- avro/examples/benchmark.rs | 2 +- avro/examples/generate_interop_data.rs | 2 +- avro/examples/specific_single_object.rs | 4 +- avro/src/bigdecimal.rs | 6 +- avro/src/bytes.rs | 2 +- avro/src/codec.rs | 2 +- avro/src/de.rs | 19 +- avro/src/decimal.rs | 2 +- avro/src/decode.rs | 4 +- avro/src/encode.rs | 4 +- avro/src/error.rs | 24 +- avro/src/headers.rs | 2 +- avro/src/lib.rs | 11 +- avro/src/rabin.rs | 4 +- avro/src/reader.rs | 42 ++-- avro/src/schema.rs | 74 +++--- avro/src/schema_compatibility.rs | 46 ++-- avro/src/schema_equality.rs | 2 +- avro/src/ser.rs | 4 +- avro/src/ser_schema.rs | 37 ++- avro/src/types.rs | 390 ++++++++++++++++++++---------- avro/src/util.rs | 4 +- avro/src/validator.rs | 2 +- avro/src/writer.rs | 15 +- avro/tests/append_to_existing.rs | 3 +- avro/tests/avro-3786.rs | 6 +- avro/tests/avro-3787.rs | 2 +- avro/tests/codecs.rs | 2 +- avro/tests/io.rs | 2 +- avro/tests/schema.rs | 14 +- avro/tests/shared.rs | 2 +- avro/tests/to_from_avro_datum_schemata.rs | 6 +- avro/tests/union_schema.rs | 4 +- avro/tests/validators.rs | 8 +- avro_derive/src/lib.rs | 34 ++- avro_derive/tests/derive.rs | 7 +- wasm-demo/tests/demos.rs | 2 +- 41 files changed, 495 insertions(+), 311 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cc1eaf6..e2ac210 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ resolver = "2" version = "0.19.0" license = "Apache-2.0" repository = "https://github.com/apache/avro-rs" -edition = "2021" +edition = "2024" rust-version = "1.85.0" keywords = ["avro", "data", "serialization"] categories = ["encoding"] diff --git a/avro/benches/serde.rs b/avro/benches/serde.rs index 0255bd3..36484c3 100644 --- a/avro/benches/serde.rs +++ b/avro/benches/serde.rs @@ -16,11 +16,11 @@ // under the License. use apache_avro::{ + AvroResult, Reader, Writer, schema::Schema, types::{Record, Value}, - AvroResult, Reader, Writer, }; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; use serde::Serialize; use std::time::Duration; diff --git a/avro/benches/serde_json.rs b/avro/benches/serde_json.rs index 97bd430..e6f96e6 100644 --- a/avro/benches/serde_json.rs +++ b/avro/benches/serde_json.rs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; use serde_json::Value; use std::collections::HashMap; diff --git a/avro/benches/single.rs b/avro/benches/single.rs index 39d6c9c..48a06ca 100644 --- a/avro/benches/single.rs +++ b/avro/benches/single.rs @@ -20,7 +20,7 @@ use apache_avro::{ to_avro_datum, types::{Record, Value}, }; -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{Criterion, criterion_group, criterion_main}; const RAW_SMALL_SCHEMA: &str = r#" { diff --git a/avro/examples/benchmark.rs b/avro/examples/benchmark.rs index 53dfb1d..b604ab4 100644 --- a/avro/examples/benchmark.rs +++ b/avro/examples/benchmark.rs @@ -16,9 +16,9 @@ // under the License. use apache_avro::{ + Reader, Writer, schema::Schema, types::{Record, Value}, - Reader, Writer, }; use apache_avro_test_helper::TestResult; use std::{ diff --git a/avro/examples/generate_interop_data.rs b/avro/examples/generate_interop_data.rs index e3be06c..7b3c369 100644 --- a/avro/examples/generate_interop_data.rs +++ b/avro/examples/generate_interop_data.rs @@ -16,9 +16,9 @@ // under the License. use apache_avro::{ + Codec, Writer, schema::Schema, types::{Record, Value}, - Codec, Writer, }; use std::{ collections::HashMap, diff --git a/avro/examples/specific_single_object.rs b/avro/examples/specific_single_object.rs index 461e9db..15ab480 100644 --- a/avro/examples/specific_single_object.rs +++ b/avro/examples/specific_single_object.rs @@ -37,7 +37,9 @@ fn main() -> anyhow::Result<()> { assert_eq!(bytes_written, 15); assert_eq!( buffer, - vec![195, 1, 166, 59, 243, 49, 82, 230, 8, 161, 54, 6, 102, 111, 111] + vec![ + 195, 1, 166, 59, 243, 49, 82, 230, 8, 161, 54, 6, 102, 111, 111 + ] ); } Err(err) => { diff --git a/avro/src/bigdecimal.rs b/avro/src/bigdecimal.rs index 723e709..ca9bed9 100644 --- a/avro/src/bigdecimal.rs +++ b/avro/src/bigdecimal.rs @@ -16,10 +16,10 @@ // under the License. use crate::{ + AvroResult, Error, decode::{decode_len, decode_long}, encode::{encode_bytes, encode_long}, types::Value, - AvroResult, Error, }; pub use bigdecimal::BigDecimal; use num_bigint::BigInt; @@ -70,7 +70,7 @@ pub(crate) fn deserialize_big_decimal(bytes: &Vec<u8>) -> AvroResult<BigDecimal> #[cfg(test)] mod tests { use super::*; - use crate::{types::Record, Codec, Reader, Schema, Writer}; + use crate::{Codec, Reader, Schema, Writer, types::Record}; use apache_avro_test_helper::TestResult; use bigdecimal::{One, Zero}; use pretty_assertions::assert_eq; @@ -173,7 +173,7 @@ mod tests { }?; let x1res: &BigDecimal = match big_decimal_value { - Value::BigDecimal(ref s) => Ok(s), + Value::BigDecimal(s) => Ok(s), other => Err(format!("Expected Value::BigDecimal, got: {other:?}")), }?; assert_eq!(&val, x1res); diff --git a/avro/src/bytes.rs b/avro/src/bytes.rs index 5c10df2..8ac98b5 100644 --- a/avro/src/bytes.rs +++ b/avro/src/bytes.rs @@ -288,7 +288,7 @@ pub mod serde_avro_slice_opt { #[cfg(test)] mod tests { use super::*; - use crate::{from_value, to_value, types::Value, Schema}; + use crate::{Schema, from_value, to_value, types::Value}; use serde::{Deserialize, Serialize}; #[test] diff --git a/avro/src/codec.rs b/avro/src/codec.rs index d616efa..4062654 100644 --- a/avro/src/codec.rs +++ b/avro/src/codec.rs @@ -16,7 +16,7 @@ // under the License. //! Logic for all supported compression codecs in Avro. -use crate::{types::Value, AvroResult, Error}; +use crate::{AvroResult, Error, types::Value}; use strum_macros::{EnumIter, EnumString, IntoStaticStr}; /// Settings for the `Deflate` codec. diff --git a/avro/src/de.rs b/avro/src/de.rs index b43f634..8d0c640 100644 --- a/avro/src/de.rs +++ b/avro/src/de.rs @@ -16,15 +16,16 @@ // under the License. //! Logic for serde-compatible deserialization. -use crate::{bytes::DE_BYTES_BORROWED, types::Value, Error}; +use crate::{Error, bytes::DE_BYTES_BORROWED, types::Value}; use serde::{ + Deserialize, de::{self, DeserializeSeed, Deserializer as _, Visitor}, - forward_to_deserialize_any, Deserialize, + forward_to_deserialize_any, }; use std::{ collections::{ - hash_map::{Keys, Values}, HashMap, + hash_map::{Keys, Values}, }, slice::Iter, }; @@ -355,13 +356,13 @@ impl<'de> de::Deserializer<'de> for &Deserializer<'de> { self.input ))), }, - Value::Record(ref fields) => visitor.visit_map(RecordDeserializer::new(fields)), - Value::Array(ref fields) => visitor.visit_seq(SeqDeserializer::new(fields)), - Value::String(ref s) => visitor.visit_borrowed_str(s), + Value::Record(fields) => visitor.visit_map(RecordDeserializer::new(fields)), + Value::Array(fields) => visitor.visit_seq(SeqDeserializer::new(fields)), + Value::String(s) => visitor.visit_borrowed_str(s), Value::Uuid(uuid) => visitor.visit_str(&uuid.to_string()), - Value::Map(ref items) => visitor.visit_map(MapDeserializer::new(items)), - Value::Bytes(ref bytes) | Value::Fixed(_, ref bytes) => visitor.visit_bytes(bytes), - Value::Decimal(ref d) => visitor.visit_bytes(&d.to_vec()?), + Value::Map(items) => visitor.visit_map(MapDeserializer::new(items)), + Value::Bytes(bytes) | Value::Fixed(_, bytes) => visitor.visit_bytes(bytes), + Value::Decimal(d) => visitor.visit_bytes(&d.to_vec()?), Value::Enum(_, s) => visitor.visit_borrowed_str(s), value => Err(de::Error::custom(format!( "incorrect value of type: {:?}", diff --git a/avro/src/decimal.rs b/avro/src/decimal.rs index e871e58..24b8d43 100644 --- a/avro/src/decimal.rs +++ b/avro/src/decimal.rs @@ -17,7 +17,7 @@ use crate::{AvroResult, Error}; use num_bigint::{BigInt, Sign}; -use serde::{de::SeqAccess, Deserialize, Serialize, Serializer}; +use serde::{Deserialize, Serialize, Serializer, de::SeqAccess}; #[derive(Debug, Clone, Eq)] pub struct Decimal { diff --git a/avro/src/decode.rs b/avro/src/decode.rs index fa7b241..3f31d6a 100644 --- a/avro/src/decode.rs +++ b/avro/src/decode.rs @@ -16,6 +16,7 @@ // under the License. use crate::{ + AvroResult, Error, bigdecimal::deserialize_big_decimal, decimal::Decimal, duration::Duration, @@ -26,7 +27,6 @@ use crate::{ }, types::Value, util::{safe_len, zag_i32, zag_i64}, - AvroResult, Error, }; use std::{ borrow::Borrow, @@ -357,6 +357,7 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>( #[allow(clippy::expect_fun_call)] mod tests { use crate::{ + Decimal, decode::decode, encode::{encode, tests::success}, schema::{DecimalSchema, FixedSchema, Schema}, @@ -364,7 +365,6 @@ mod tests { Value, Value::{Array, Int, Map}, }, - Decimal, }; use apache_avro_test_helper::TestResult; use pretty_assertions::assert_eq; diff --git a/avro/src/encode.rs b/avro/src/encode.rs index 73fa978..8f28d10 100644 --- a/avro/src/encode.rs +++ b/avro/src/encode.rs @@ -16,6 +16,7 @@ // under the License. use crate::{ + AvroResult, bigdecimal::serialize_big_decimal, error::Error, schema::{ @@ -24,7 +25,6 @@ use crate::{ }, types::{Value, ValueKind}, util::{zig_i32, zig_i64}, - AvroResult, }; use log::error; use std::{borrow::Borrow, collections::HashMap, io::Write}; @@ -63,7 +63,7 @@ pub(crate) fn encode_internal<W: Write, S: Borrow<Schema>>( enclosing_namespace: &Namespace, writer: &mut W, ) -> AvroResult<usize> { - if let Schema::Ref { ref name } = schema { + if let Schema::Ref { name } = schema { let fully_qualified_name = name.fully_qualified_name(enclosing_namespace); let resolved = names .get(&fully_qualified_name) diff --git a/avro/src/error.rs b/avro/src/error.rs index b169b30..e68495c 100644 --- a/avro/src/error.rs +++ b/avro/src/error.rs @@ -66,7 +66,9 @@ pub enum Error { MemoryAllocation { desired: usize, maximum: usize }, /// Describe a specific error happening with decimal representation - #[error("Number of bytes requested for decimal sign extension {requested} is less than the number of bytes needed to decode {needed}")] + #[error( + "Number of bytes requested for decimal sign extension {requested} is less than the number of bytes needed to decode {needed}" + )] SignExtend { requested: usize, needed: usize }, #[error("Failed to read boolean bytes: {0}")] @@ -350,7 +352,9 @@ pub enum Error { #[error("Invalid namespace {0}. It must match the regex '{1}'")] InvalidNamespace(String, &'static str), - #[error("Invalid schema: There is no type called '{0}', if you meant to define a non-primitive schema, it should be defined inside `type` attribute. Please review the specification")] + #[error( + "Invalid schema: There is no type called '{0}', if you meant to define a non-primitive schema, it should be defined inside `type` attribute. Please review the specification" + )] InvalidSchemaRecord(String), #[error("Duplicate enum symbol {0}")] @@ -503,9 +507,7 @@ pub enum Error { value_kind: ValueKind, supported_schema: Vec<SchemaKind>, }, - #[error( - "Internal buffer not drained properly. Re-initialize the single object writer struct!" - )] + #[error("Internal buffer not drained properly. Re-initialize the single object writer struct!")] IllegalSingleObjectWriterState, #[error("Codec '{0}' is not supported/enabled")] @@ -520,7 +522,9 @@ pub enum Error { #[derive(thiserror::Error, PartialEq)] pub enum CompatibilityError { - #[error("Incompatible schema types! Writer schema is '{writer_schema_type}', but reader schema is '{reader_schema_type}'")] + #[error( + "Incompatible schema types! Writer schema is '{writer_schema_type}', but reader schema is '{reader_schema_type}'" + )] WrongType { writer_schema_type: String, reader_schema_type: String, @@ -532,7 +536,9 @@ pub enum CompatibilityError { expected_type: Vec<SchemaKind>, }, - #[error("Incompatible schemata! Field '{0}' in reader schema does not match the type in the writer schema")] + #[error( + "Incompatible schemata! Field '{0}' in reader schema does not match the type in the writer schema" + )] FieldTypeMismatch(String, #[source] Box<CompatibilityError>), #[error("Incompatible schemata! Field '{0}' in reader schema must have a default value")] @@ -547,7 +553,9 @@ pub enum CompatibilityError { #[error("Incompatible schemata! Name and size don't match for fixed")] FixedMismatch, - #[error("Incompatible schemata! The name must be the same for both schemas. Writer's name {writer_name} and reader's name {reader_name}")] + #[error( + "Incompatible schemata! The name must be the same for both schemas. Writer's name {writer_name} and reader's name {reader_name}" + )] NameMismatch { writer_name: String, reader_name: String, diff --git a/avro/src/headers.rs b/avro/src/headers.rs index 971b182..dae44f1 100644 --- a/avro/src/headers.rs +++ b/avro/src/headers.rs @@ -18,7 +18,7 @@ //! Handling of Avro magic headers use uuid::Uuid; -use crate::{rabin::Rabin, schema::SchemaFingerprint, AvroResult, Schema}; +use crate::{AvroResult, Schema, rabin::Rabin, schema::SchemaFingerprint}; /// This trait represents that an object is able to construct an Avro message header. It is /// implemented for some known header types already. If you need a header type that is not already diff --git a/avro/src/lib.rs b/avro/src/lib.rs index 993ae78..0f26246 100644 --- a/avro/src/lib.rs +++ b/avro/src/lib.rs @@ -898,16 +898,16 @@ pub use decimal::Decimal; pub use duration::{Days, Duration, Millis, Months}; pub use error::Error; pub use reader::{ - from_avro_datum, from_avro_datum_reader_schemata, from_avro_datum_schemata, read_marker, - GenericSingleObjectReader, Reader, SpecificSingleObjectReader, + GenericSingleObjectReader, Reader, SpecificSingleObjectReader, from_avro_datum, + from_avro_datum_reader_schemata, from_avro_datum_schemata, read_marker, }; pub use schema::{AvroSchema, Schema}; pub use ser::to_value; pub use util::{max_allocation_bytes, set_serde_human_readable}; pub use uuid::Uuid; pub use writer::{ - to_avro_datum, to_avro_datum_schemata, write_avro_datum_ref, GenericSingleObjectWriter, - SpecificSingleObjectWriter, Writer, WriterBuilder, + GenericSingleObjectWriter, SpecificSingleObjectWriter, Writer, WriterBuilder, to_avro_datum, + to_avro_datum_schemata, write_avro_datum_ref, }; #[cfg(feature = "derive")] @@ -919,9 +919,8 @@ pub type AvroResult<T> = Result<T, Error>; #[cfg(test)] mod tests { use crate::{ - from_avro_datum, + Codec, Reader, Schema, Writer, from_avro_datum, types::{Record, Value}, - Codec, Reader, Schema, Writer, }; use pretty_assertions::assert_eq; diff --git a/avro/src/rabin.rs b/avro/src/rabin.rs index eb34477..3d90333 100644 --- a/avro/src/rabin.rs +++ b/avro/src/rabin.rs @@ -17,8 +17,8 @@ //! Implementation of the Rabin fingerprint algorithm use digest::{ - consts::U8, core_api::OutputSizeUser, generic_array::GenericArray, FixedOutput, - FixedOutputReset, HashMarker, Output, Reset, Update, + FixedOutput, FixedOutputReset, HashMarker, Output, Reset, Update, consts::U8, + core_api::OutputSizeUser, generic_array::GenericArray, }; use std::sync::OnceLock; diff --git a/avro/src/reader.rs b/avro/src/reader.rs index 78e0298..fb93c70 100644 --- a/avro/src/reader.rs +++ b/avro/src/reader.rs @@ -17,15 +17,16 @@ //! Logic handling reading from Avro format at user level. use crate::{ + AvroResult, Codec, Error, decode::{decode, decode_internal}, from_value, headers::{HeaderBuilder, RabinFingerprintHeader}, schema::{ - resolve_names, resolve_names_with_schemata, AvroSchema, Names, ResolvedOwnedSchema, - ResolvedSchema, Schema, + AvroSchema, Names, ResolvedOwnedSchema, ResolvedSchema, Schema, resolve_names, + resolve_names_with_schemata, }, types::Value, - util, AvroResult, Codec, Error, + util, }; use log::warn; use serde::de::DeserializeOwned; @@ -86,24 +87,27 @@ impl<'r, R: Read> Block<'r, R> { } let meta_schema = Schema::map(Schema::Bytes); - if let Value::Map(metadata) = decode(&meta_schema, &mut self.reader)? { - self.read_writer_schema(&metadata)?; - self.codec = read_codec(&metadata)?; - - for (key, value) in metadata { - if key == "avro.schema" - || key == "avro.codec" - || key == "avro.codec.compression_level" - { - // already processed - } else if key.starts_with("avro.") { - warn!("Ignoring unknown metadata key: {key}"); - } else { - self.read_user_metadata(key, value); + match decode(&meta_schema, &mut self.reader)? { + Value::Map(metadata) => { + self.read_writer_schema(&metadata)?; + self.codec = read_codec(&metadata)?; + + for (key, value) in metadata { + if key == "avro.schema" + || key == "avro.codec" + || key == "avro.codec.compression_level" + { + // already processed + } else if key.starts_with("avro.") { + warn!("Ignoring unknown metadata key: {key}"); + } else { + self.read_user_metadata(key, value); + } } } - } else { - return Err(Error::GetHeaderMetadata); + _ => { + return Err(Error::GetHeaderMetadata); + } } self.reader diff --git a/avro/src/schema.rs b/avro/src/schema.rs index edb3345..342b605 100644 --- a/avro/src/schema.rs +++ b/avro/src/schema.rs @@ -17,6 +17,7 @@ //! Logic for parsing and interacting with schemas in Avro format. use crate::{ + AvroResult, error::Error, schema_equality, types, util::MapHelper, @@ -24,13 +25,12 @@ use crate::{ validate_enum_symbol_name, validate_namespace, validate_record_field_name, validate_schema_name, }, - AvroResult, }; use digest::Digest; use log::{debug, error, warn}; use serde::{ - ser::{SerializeMap, SerializeSeq}, Deserialize, Serialize, Serializer, + ser::{SerializeMap, SerializeSeq}, }; use serde_json::{Map, Value}; use std::{ @@ -1406,10 +1406,12 @@ impl Parser { enclosing_namespace: &Namespace, ) -> AvroResult<Schema> { fn get_schema_ref(parsed: &Schema) -> Schema { - match &parsed { - Schema::Record(RecordSchema { ref name, .. }) - | Schema::Enum(EnumSchema { ref name, .. }) - | Schema::Fixed(FixedSchema { ref name, .. }) => Schema::Ref { name: name.clone() }, + match parsed { + &Schema::Record(RecordSchema { ref name, .. }) + | &Schema::Enum(EnumSchema { ref name, .. }) + | &Schema::Fixed(FixedSchema { ref name, .. }) => { + Schema::Ref { name: name.clone() } + } _ => parsed.clone(), } } @@ -1577,7 +1579,9 @@ impl Parser { Schema::String => Ok(Schema::Uuid), Schema::Fixed(FixedSchema { size: 16, .. }) => Ok(Schema::Uuid), Schema::Fixed(FixedSchema { size, .. }) => { - warn!("Ignoring uuid logical type for a Fixed schema because its size ({size:?}) is not 16! Schema: {schema:?}"); + warn!( + "Ignoring uuid logical type for a Fixed schema because its size ({size:?}) is not 16! Schema: {schema:?}" + ); Ok(schema) } _ => { @@ -1709,7 +1713,7 @@ impl Parser { let namespace = &name.namespace; - if let Some(ref aliases) = aliases { + if let Some(aliases) = aliases { aliases.iter().for_each(|alias| { let alias_fullname = alias.fully_qualified_name(namespace); self.resolving_schemas @@ -1732,7 +1736,7 @@ impl Parser { let namespace = &fully_qualified_name.namespace; - if let Some(ref aliases) = aliases { + if let Some(aliases) = aliases { aliases.iter().for_each(|alias| { let alias_fullname = alias.fully_qualified_name(namespace); self.resolving_schemas.remove(&alias_fullname); @@ -1748,7 +1752,7 @@ impl Parser { enclosing_namespace: &Namespace, ) -> Option<&Schema> { match complex.get("type") { - Some(Value::String(ref typ)) => { + Some(Value::String(typ)) => { let name = Name::new(typ.as_str()) .unwrap() .fully_qualified_name(enclosing_namespace); @@ -1997,7 +2001,7 @@ impl Parser { } let doc = complex.get("doc").and_then(|v| match &v { - Value::String(ref docstr) => Some(docstr.clone()), + &Value::String(docstr) => Some(docstr.clone()), _ => None, }); @@ -2009,7 +2013,7 @@ impl Parser { }?; let default = complex.get("default").and_then(|v| match &v { - Value::String(ref default) => Some(default.clone()), + &Value::String(default) => Some(default.clone()), _ => None, }); @@ -2050,7 +2054,7 @@ fn fix_aliases_namespace(aliases: Option<Vec<String>>, namespace: &Namespace) -> .map(|alias| { if alias.find('.').is_none() { match namespace { - Some(ref ns) => format!("{ns}.{alias}"), + Some(ns) => format!("{ns}.{alias}"), None => alias.clone(), } } else { @@ -2127,10 +2131,10 @@ impl Serialize for Schema { map.serialize_entry("namespace", n)?; } map.serialize_entry("name", &name.name)?; - if let Some(ref docstr) = doc { + if let Some(docstr) = doc { map.serialize_entry("doc", docstr)?; } - if let Some(ref aliases) = aliases { + if let Some(aliases) = aliases { map.serialize_entry("aliases", aliases)?; } map.serialize_entry("fields", fields)?; @@ -2154,7 +2158,7 @@ impl Serialize for Schema { map.serialize_entry("name", &name.name)?; map.serialize_entry("symbols", symbols)?; - if let Some(ref aliases) = aliases { + if let Some(aliases) = aliases { map.serialize_entry("aliases", aliases)?; } for attr in attributes { @@ -2507,7 +2511,7 @@ pub mod derive { /// ``` pub trait AvroSchemaComponent { fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace: &Namespace) - -> Schema; + -> Schema; } impl<T> AvroSchema for T @@ -2639,10 +2643,10 @@ pub mod derive { #[cfg(test)] mod tests { use super::*; - use crate::{rabin::Rabin, SpecificSingleObjectWriter}; + use crate::{SpecificSingleObjectWriter, rabin::Rabin}; use apache_avro_test_helper::{ - logger::{assert_logged, assert_not_logged}, TestResult, + logger::{assert_logged, assert_not_logged}, }; use serde_json::json; use serial_test::serial; @@ -2792,17 +2796,19 @@ mod tests { let schema_c_expected = Schema::Record( RecordSchema::builder() .name(Name::new("C")?) - .fields(vec![RecordField::builder() - .name("field_one".to_string()) - .schema(Schema::Union(UnionSchema::new(vec![ - Schema::Ref { - name: Name::new("A")?, - }, - Schema::Ref { - name: Name::new("B")?, - }, - ])?)) - .build()]) + .fields(vec![ + RecordField::builder() + .name("field_one".to_string()) + .schema(Schema::Union(UnionSchema::new(vec![ + Schema::Ref { + name: Name::new("A")?, + }, + Schema::Ref { + name: Name::new("B")?, + }, + ])?)) + .build(), + ]) .lookup(BTreeMap::from_iter(vec![("field_one".to_string(), 0)])) .build(), ); @@ -6852,7 +6858,9 @@ mod tests { let scale = attrs .get("scale") .expect("The 'scale' attribute is missing"); - assert_logged(&format!("Ignoring invalid decimal logical type: The decimal precision ({precision}) must be bigger or equal to the scale ({scale})")); + assert_logged(&format!( + "Ignoring invalid decimal logical type: The decimal precision ({precision}) must be bigger or equal to the scale ({scale})" + )); } _ => unreachable!("Expected Schema::Fixed, got {:?}", schema), } @@ -6868,7 +6876,9 @@ mod tests { )?; match schema { Schema::Decimal(_) => { - assert_not_logged("Ignoring invalid decimal logical type: The decimal precision (2) must be bigger or equal to the scale (3)"); + assert_not_logged( + "Ignoring invalid decimal logical type: The decimal precision (2) must be bigger or equal to the scale (3)", + ); } _ => unreachable!("Expected Schema::Decimal, got {:?}", schema), } diff --git a/avro/src/schema_compatibility.rs b/avro/src/schema_compatibility.rs index fa6dc05..3fec12a 100644 --- a/avro/src/schema_compatibility.rs +++ b/avro/src/schema_compatibility.rs @@ -21,7 +21,7 @@ use crate::{ schema::{EnumSchema, FixedSchema, RecordSchema, Schema, SchemaKind}, }; use std::{ - collections::{hash_map::DefaultHasher, HashSet}, + collections::{HashSet, hash_map::DefaultHasher}, hash::Hasher, ptr, }; @@ -460,7 +460,7 @@ impl SchemaCompatibility { _ => { return Err(CompatibilityError::Inconclusive(String::from( "readers_schema", - ))) + ))); } }; } @@ -524,8 +524,8 @@ impl SchemaCompatibility { mod tests { use super::*; use crate::{ - types::{Record, Value}, Codec, Reader, Writer, + types::{Record, Value}, }; use apache_avro_test_helper::TestResult; use rstest::*; @@ -735,9 +735,11 @@ mod tests { (nested_record(), nested_optional_record()), ]; - assert!(incompatible_schemas - .iter() - .any(|(reader, writer)| SchemaCompatibility::can_read(writer, reader).is_err())); + assert!( + incompatible_schemas + .iter() + .any(|(reader, writer)| SchemaCompatibility::can_read(writer, reader).is_err()) + ); } #[rstest] @@ -1068,9 +1070,11 @@ mod tests { (nested_optional_record(), nested_record()), ]; - assert!(compatible_schemas - .iter() - .all(|(reader, writer)| SchemaCompatibility::can_read(writer, reader).is_ok())); + assert!( + compatible_schemas + .iter() + .all(|(reader, writer)| SchemaCompatibility::can_read(writer, reader).is_ok()) + ); } fn writer_schema() -> Schema { @@ -1684,24 +1688,26 @@ mod tests { let schemas = [ ( Schema::parse_str( - r#"{ + r#"{ "type": "record", "name": "StatisticsMap", "fields": [ {"name": "average", "type": "int", "default": 0}, {"name": "success", "type": {"type": "map", "values": "int"}} ] - }"#)?, + }"#, + )?, Schema::parse_str( - r#"{ + r#"{ "type": "record", "name": "StatisticsMap", "fields": [ {"name": "average", "type": "int", "default": 0}, {"name": "success", "type": ["null", {"type": "map", "values": "int"}], "default": null} ] - }"#)?, - "Incompatible schemata! Field 'success' in reader schema does not match the type in the writer schema" + }"#, + )?, + "Incompatible schemata! Field 'success' in reader schema does not match the type in the writer schema", ), ( Schema::parse_str( @@ -1711,17 +1717,19 @@ mod tests { "fields": [ {"name": "max_values", "type": {"type": "array", "items": "int"}} ] - }"#)?, - Schema::parse_str( + }"#, + )?, + Schema::parse_str( r#"{ "type": "record", "name": "StatisticsArray", "fields": [ {"name": "max_values", "type": ["null", {"type": "array", "items": "int"}], "default": null} ] - }"#)?, - "Incompatible schemata! Field 'max_values' in reader schema does not match the type in the writer schema" - ) + }"#, + )?, + "Incompatible schemata! Field 'max_values' in reader schema does not match the type in the writer schema", + ), ]; for (schema_1, schema_2, error) in schemas { diff --git a/avro/src/schema_equality.rs b/avro/src/schema_equality.rs index feb4049..b884a0f 100644 --- a/avro/src/schema_equality.rs +++ b/avro/src/schema_equality.rs @@ -16,11 +16,11 @@ // under the License. use crate::{ + Schema, schema::{ ArraySchema, DecimalSchema, EnumSchema, FixedSchema, MapSchema, RecordField, RecordSchema, UnionSchema, }, - Schema, }; use log::{debug, error}; use std::{fmt::Debug, sync::OnceLock}; diff --git a/avro/src/ser.rs b/avro/src/ser.rs index 2d636fe..8e6e595 100644 --- a/avro/src/ser.rs +++ b/avro/src/ser.rs @@ -17,11 +17,11 @@ //! Logic for serde-compatible serialization. use crate::{ + Error, bytes::{BytesType, SER_BYTES_TYPE}, types::Value, - Error, }; -use serde::{ser, Serialize}; +use serde::{Serialize, ser}; use std::{collections::HashMap, iter::once}; #[derive(Clone, Default)] diff --git a/avro/src/ser_schema.rs b/avro/src/ser_schema.rs index fd273a9..7be43fa 100644 --- a/avro/src/ser_schema.rs +++ b/avro/src/ser_schema.rs @@ -692,7 +692,9 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> { _ => { /* skip */ } } } - Err(create_error(format!("Cannot find a matching Int-like, Long-like or Bytes schema in {union_schema:?}"))) + Err(create_error(format!( + "Cannot find a matching Int-like, Long-like or Bytes schema in {union_schema:?}" + ))) } expected => Err(create_error(format!("Expected: {expected}. Got: Int"))), } @@ -982,14 +984,21 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> { if value.len() == fixed_schema.size { self.writer.write(value).map_err(Error::WriteBytes) } else { - Err(create_error(format!("Fixed schema size ({}) does not match the value length ({})", fixed_schema.size, value.len()))) + Err(create_error(format!( + "Fixed schema size ({}) does not match the value length ({})", + fixed_schema.size, + value.len() + ))) } } Schema::Duration => { if value.len() == 12 { self.writer.write(value).map_err(Error::WriteBytes) } else { - Err(create_error(format!("Duration length must be 12! Got ({})", value.len()))) + Err(create_error(format!( + "Duration length must be 12! Got ({})", + value.len() + ))) } } Schema::Decimal(decimal_schema) => match decimal_schema.inner.as_ref() { @@ -1011,7 +1020,9 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> { n: value.len(), }), }, - unsupported => Err(create_error(format!("Decimal schema's inner should be Bytes or Fixed schema. Got: {unsupported}"))), + unsupported => Err(create_error(format!( + "Decimal schema's inner should be Bytes or Fixed schema. Got: {unsupported}" + ))), }, Schema::Ref { name } => { let ref_schema = self.get_ref_schema(name)?; @@ -1034,9 +1045,13 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> { _ => { /* skip */ } } } - Err(create_error(format!("Cannot find a matching String, Bytes, Uuid, BigDecimal, Fixed, Duration, Decimal or Ref schema in {union_schema:?}"))) + Err(create_error(format!( + "Cannot find a matching String, Bytes, Uuid, BigDecimal, Fixed, Duration, Decimal or Ref schema in {union_schema:?}" + ))) } - unsupported => Err(create_error(format!("Expected String, Bytes, Uuid, BigDecimal, Fixed, Duration, Decimal, Ref or Union schema. Got: {unsupported}"))), + unsupported => Err(create_error(format!( + "Expected String, Bytes, Uuid, BigDecimal, Fixed, Duration, Decimal, Ref or Union schema. Got: {unsupported}" + ))), } } @@ -1748,7 +1763,7 @@ impl<'a, 's, W: Write> ser::Serializer for &'a mut SchemaAwareWriteSerializer<'s #[cfg(test)] mod tests { use super::*; - use crate::{decimal::Decimal, schema::ResolvedSchema, Days, Duration, Millis, Months}; + use crate::{Days, Duration, Millis, Months, decimal::Decimal, schema::ResolvedSchema}; use apache_avro_test_helper::TestResult; use bigdecimal::BigDecimal; use num_bigint::{BigInt, Sign}; @@ -2556,7 +2571,9 @@ mod tests { assert_eq!( buffer.as_slice(), - &[200, 1, 208, 15, 160, 156, 1, 208, 15, 160, 156, 1, 160, 156, 1] + &[ + 200, 1, 208, 15, 160, 156, 1, 208, 15, 160, 156, 1, 160, 156, 1 + ] ); Ok(()) @@ -2608,7 +2625,9 @@ mod tests { assert_eq!( buffer.as_slice(), - &[200, 1, 208, 15, 160, 156, 1, 208, 15, 160, 156, 1, 160, 156, 1] + &[ + 200, 1, 208, 15, 160, 156, 1, 208, 15, 160, 156, 1, 160, 156, 1 + ] ); } diff --git a/avro/src/types.rs b/avro/src/types.rs index fe88bfe..f241661 100644 --- a/avro/src/types.rs +++ b/avro/src/types.rs @@ -17,6 +17,7 @@ //! Logic handling the intermediate representation of Avro values. use crate::{ + AvroResult, Error, bigdecimal::{deserialize_big_decimal, serialize_big_decimal}, decimal::Decimal, duration::Duration, @@ -24,7 +25,6 @@ use crate::{ DecimalSchema, EnumSchema, FixedSchema, Name, Namespace, Precision, RecordField, RecordSchema, ResolvedSchema, Scale, Schema, SchemaKind, UnionSchema, }, - AvroResult, Error, }; use bigdecimal::BigDecimal; use log::{debug, error}; @@ -1184,8 +1184,8 @@ mod tests { schema::RecordFieldOrder, }; use apache_avro_test_helper::{ - logger::{assert_logged, assert_not_logged}, TestResult, + logger::{assert_logged, assert_not_logged}, }; use num_bigint::BigInt; use pretty_assertions::assert_eq; @@ -1274,23 +1274,21 @@ mod tests { ), ( Value::Union(3, Box::new(Value::Int(42))), - Schema::Union( - UnionSchema::new(vec![ - Schema::Null, - Schema::Double, - Schema::String, - Schema::Int, - ]) - ?, - ), + Schema::Union(UnionSchema::new(vec![ + Schema::Null, + Schema::Double, + Schema::String, + Schema::Int, + ])?), true, "", ), ( Value::Union(1, Box::new(Value::Long(42i64))), - Schema::Union( - UnionSchema::new(vec![Schema::Null, Schema::TimestampMillis])?, - ), + Schema::Union(UnionSchema::new(vec![ + Schema::Null, + Schema::TimestampMillis, + ])?), true, "", ), @@ -1312,7 +1310,12 @@ mod tests { false, "Invalid value: Array([Boolean(true)]) for schema: Array(ArraySchema { items: Long, attributes: {} }). Reason: Unsupported value-schema combination! Value: Boolean(true), schema: Long", ), - (Value::Record(vec![]), Schema::Null, false, "Invalid value: Record([]) for schema: Null. Reason: Unsupported value-schema combination! Value: Record([]), schema: Null"), + ( + Value::Record(vec![]), + Schema::Null, + false, + "Invalid value: Record([]) for schema: Null. Reason: Unsupported value-schema combination! Value: Record([]), schema: Null", + ), ( Value::Fixed(12, vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]), Schema::Duration, @@ -1564,11 +1567,13 @@ mod tests { attributes: Default::default(), }); - assert!(Value::Record(vec![ - ("a".to_string(), Value::Long(42i64)), - ("b".to_string(), Value::String("foo".to_string())), - ]) - .validate(&schema)); + assert!( + Value::Record(vec![ + ("a".to_string(), Value::Long(42i64)), + ("b".to_string(), Value::String("foo".to_string())), + ]) + .validate(&schema) + ); let value = Value::Record(vec![ ("b".to_string(), Value::String("foo".to_string())), @@ -1617,22 +1622,26 @@ mod tests { r#"Invalid value: Record([("a", Long(42)), ("b", String("foo")), ("c", Null), ("d", Null)]) for schema: Record(RecordSchema { name: Name { name: "some_record", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: "a", doc: None, aliases: None, default: None, schema: Long, order: Ascending, position: 0, custom_attributes: {} }, RecordField { name: "b", doc: None, aliases: None, default: None, schema: String, order: Ascending, position: 1, custom_attributes [...] ); - assert!(Value::Map( - vec![ - ("a".to_string(), Value::Long(42i64)), - ("b".to_string(), Value::String("foo".to_string())), - ] - .into_iter() - .collect() - ) - .validate(&schema)); - - assert!(!Value::Map( - vec![("d".to_string(), Value::Long(123_i64)),] + assert!( + Value::Map( + vec![ + ("a".to_string(), Value::Long(42i64)), + ("b".to_string(), Value::String("foo".to_string())), + ] .into_iter() .collect() - ) - .validate(&schema)); + ) + .validate(&schema) + ); + + assert!( + !Value::Map( + vec![("d".to_string(), Value::Long(123_i64)),] + .into_iter() + .collect() + ) + .validate(&schema) + ); assert_logged( r#"Invalid value: Map({"d": Long(123)}) for schema: Record(RecordSchema { name: Name { name: "some_record", namespace: None }, aliases: None, doc: None, fields: [RecordField { name: "a", doc: None, aliases: None, default: None, schema: Long, order: Ascending, position: 0, custom_attributes: {} }, RecordField { name: "b", doc: None, aliases: None, default: None, schema: String, order: Ascending, position: 1, custom_attributes: {} }, RecordField { name: "c", doc: None, aliases: [...] Field with name '"b"' is not a member of the map items"#, @@ -1640,27 +1649,31 @@ Field with name '"b"' is not a member of the map items"#, let union_schema = Schema::Union(UnionSchema::new(vec![Schema::Null, schema])?); - assert!(Value::Union( - 1, - Box::new(Value::Record(vec![ - ("a".to_string(), Value::Long(42i64)), - ("b".to_string(), Value::String("foo".to_string())), - ])) - ) - .validate(&union_schema)); - - assert!(Value::Union( - 1, - Box::new(Value::Map( - vec![ + assert!( + Value::Union( + 1, + Box::new(Value::Record(vec![ ("a".to_string(), Value::Long(42i64)), ("b".to_string(), Value::String("foo".to_string())), - ] - .into_iter() - .collect() - )) - ) - .validate(&union_schema)); + ])) + ) + .validate(&union_schema) + ); + + assert!( + Value::Union( + 1, + Box::new(Value::Map( + vec![ + ("a".to_string(), Value::Long(42i64)), + ("b".to_string(), Value::String("foo".to_string())), + ] + .into_iter() + .collect() + )) + ) + .validate(&union_schema) + ); Ok(()) } @@ -1720,45 +1733,51 @@ Field with name '"b"' is not a member of the map items"#, #[test] fn resolve_decimal_invalid_scale() { let value = Value::Decimal(Decimal::from(vec![1, 2])); - assert!(value - .resolve(&Schema::Decimal(DecimalSchema { - precision: 2, - scale: 3, - inner: Box::new(Schema::Bytes), - })) - .is_err()); + assert!( + value + .resolve(&Schema::Decimal(DecimalSchema { + precision: 2, + scale: 3, + inner: Box::new(Schema::Bytes), + })) + .is_err() + ); } #[test] fn resolve_decimal_invalid_precision_for_length() { let value = Value::Decimal(Decimal::from((1u8..=8u8).rev().collect::<Vec<_>>())); - assert!(value - .resolve(&Schema::Decimal(DecimalSchema { - precision: 1, - scale: 0, - inner: Box::new(Schema::Bytes), - })) - .is_ok()); + assert!( + value + .resolve(&Schema::Decimal(DecimalSchema { + precision: 1, + scale: 0, + inner: Box::new(Schema::Bytes), + })) + .is_ok() + ); } #[test] fn resolve_decimal_fixed() { let value = Value::Decimal(Decimal::from(vec![1, 2, 3, 4, 5])); - assert!(value - .clone() - .resolve(&Schema::Decimal(DecimalSchema { - precision: 10, - scale: 1, - inner: Box::new(Schema::Fixed(FixedSchema { - name: Name::new("decimal").unwrap(), - aliases: None, - size: 20, - doc: None, - default: None, - attributes: Default::default(), + assert!( + value + .clone() + .resolve(&Schema::Decimal(DecimalSchema { + precision: 10, + scale: 1, + inner: Box::new(Schema::Fixed(FixedSchema { + name: Name::new("decimal").unwrap(), + aliases: None, + size: 20, + doc: None, + default: None, + attributes: Default::default(), + })) })) - })) - .is_ok()); + .is_ok() + ); assert!(value.resolve(&Schema::String).is_err()); } @@ -2066,7 +2085,10 @@ Field with name '"b"' is not a member of the map items"#, ); assert_eq!( JsonValue::try_from(Value::Duration( - [1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8, 9u8, 10u8, 11u8, 12u8].into() + [ + 1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8, 9u8, 10u8, 11u8, 12u8 + ] + .into() ))?, JsonValue::Array(vec![ JsonValue::Number(1.into()), @@ -3075,28 +3097,32 @@ Field with name '"b"' is not a member of the map items"#, ); let value = Value::Bytes(vec![97, 99]); - assert!(value - .resolve(&Schema::Fixed(FixedSchema { - name: "test".into(), - aliases: None, - doc: None, - size: 3, - default: None, - attributes: Default::default() - })) - .is_err(),); + assert!( + value + .resolve(&Schema::Fixed(FixedSchema { + name: "test".into(), + aliases: None, + doc: None, + size: 3, + default: None, + attributes: Default::default() + })) + .is_err(), + ); let value = Value::Bytes(vec![97, 98, 99, 100]); - assert!(value - .resolve(&Schema::Fixed(FixedSchema { - name: "test".into(), - aliases: None, - doc: None, - size: 3, - default: None, - attributes: Default::default() - })) - .is_err(),); + assert!( + value + .resolve(&Schema::Fixed(FixedSchema { + name: "test".into(), + aliases: None, + doc: None, + size: 3, + default: None, + attributes: Default::default() + })) + .is_err(), + ); Ok(()) } @@ -3184,34 +3210,138 @@ Field with name '"b"' is not a member of the map items"#, #[test] fn avro_4029_resolve_from_unsupported_err() -> TestResult { - let data: Vec<(&str, Value, &str)> = vec!( - (r#"{ "name": "NAME", "type": "int" }"#, Value::Float(123_f32), "Expected Value::Int, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "fixed", "size": 3 }"#, Value::Float(123_f32), "String expected for fixed, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "bytes" }"#, Value::Float(123_f32), "Expected Value::Bytes, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "string", "logicalType": "uuid" }"#, Value::String("abc-1234".into()), "Failed to convert &str to UUID: invalid group count: expected 5, found 2"), - (r#"{ "name": "NAME", "type": "string", "logicalType": "uuid" }"#, Value::Float(123_f32), "Expected Value::Uuid, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "bytes", "logicalType": "big-decimal" }"#, Value::Float(123_f32), "Expected Value::BigDecimal, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "fixed", "size": 12, "logicalType": "duration" }"#, Value::Float(123_f32), "Expected Value::Duration or Value::Fixed(12), got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 3 }"#, Value::Float(123_f32), "Expected Value::Decimal, Value::Bytes or Value::Fixed, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "bytes" }"#, Value::Array(vec!(Value::Long(256_i64))), "Unable to convert to u8, got Int(256)"), - (r#"{ "name": "NAME", "type": "int", "logicalType": "date" }"#, Value::Float(123_f32), "Expected Value::Date or Value::Int, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "int", "logicalType": "time-millis" }"#, Value::Float(123_f32), "Expected Value::TimeMillis or Value::Int, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "long", "logicalType": "time-micros" }"#, Value::Float(123_f32), "Expected Value::TimeMicros, Value::Long or Value::Int, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "long", "logicalType": "timestamp-millis" }"#, Value::Float(123_f32), "Expected Value::TimestampMillis, Value::Long or Value::Int, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "long", "logicalType": "timestamp-micros" }"#, Value::Float(123_f32), "Expected Value::TimestampMicros, Value::Long or Value::Int, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "long", "logicalType": "timestamp-nanos" }"#, Value::Float(123_f32), "Expected Value::TimestampNanos, Value::Long or Value::Int, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "long", "logicalType": "local-timestamp-millis" }"#, Value::Float(123_f32), "Expected Value::LocalTimestampMillis, Value::Long or Value::Int, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "long", "logicalType": "local-timestamp-micros" }"#, Value::Float(123_f32), "Expected Value::LocalTimestampMicros, Value::Long or Value::Int, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "long", "logicalType": "local-timestamp-nanos" }"#, Value::Float(123_f32), "Expected Value::LocalTimestampNanos, Value::Long or Value::Int, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "null" }"#, Value::Float(123_f32), "Expected Value::Null, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "boolean" }"#, Value::Float(123_f32), "Expected Value::Boolean, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "int" }"#, Value::Float(123_f32), "Expected Value::Int, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "long" }"#, Value::Float(123_f32), "Expected Value::Long or Value::Int, got: Float(123.0)"), - (r#"{ "name": "NAME", "type": "float" }"#, Value::Boolean(false), r#"Expected Value::Float, Value::Double, Value::Int, Value::Long or Value::String ("NaN", "INF", "Infinity", "-INF" or "-Infinity"), got: Boolean(false)"#), - (r#"{ "name": "NAME", "type": "double" }"#, Value::Boolean(false), r#"Expected Value::Double, Value::Float, Value::Int, Value::Long or Value::String ("NaN", "INF", "Infinity", "-INF" or "-Infinity"), got: Boolean(false)"#), - (r#"{ "name": "NAME", "type": "string" }"#, Value::Boolean(false), "Expected Value::String, Value::Bytes or Value::Fixed, got: Boolean(false)"), - (r#"{ "name": "NAME", "type": "enum", "symbols": ["one", "two"] }"#, Value::Boolean(false), "Expected Value::Enum, got: Boolean(false)"), - ); + let data: Vec<(&str, Value, &str)> = vec![ + ( + r#"{ "name": "NAME", "type": "int" }"#, + Value::Float(123_f32), + "Expected Value::Int, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "fixed", "size": 3 }"#, + Value::Float(123_f32), + "String expected for fixed, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "bytes" }"#, + Value::Float(123_f32), + "Expected Value::Bytes, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "string", "logicalType": "uuid" }"#, + Value::String("abc-1234".into()), + "Failed to convert &str to UUID: invalid group count: expected 5, found 2", + ), + ( + r#"{ "name": "NAME", "type": "string", "logicalType": "uuid" }"#, + Value::Float(123_f32), + "Expected Value::Uuid, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "bytes", "logicalType": "big-decimal" }"#, + Value::Float(123_f32), + "Expected Value::BigDecimal, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "fixed", "size": 12, "logicalType": "duration" }"#, + Value::Float(123_f32), + "Expected Value::Duration or Value::Fixed(12), got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 3 }"#, + Value::Float(123_f32), + "Expected Value::Decimal, Value::Bytes or Value::Fixed, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "bytes" }"#, + Value::Array(vec![Value::Long(256_i64)]), + "Unable to convert to u8, got Int(256)", + ), + ( + r#"{ "name": "NAME", "type": "int", "logicalType": "date" }"#, + Value::Float(123_f32), + "Expected Value::Date or Value::Int, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "int", "logicalType": "time-millis" }"#, + Value::Float(123_f32), + "Expected Value::TimeMillis or Value::Int, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "long", "logicalType": "time-micros" }"#, + Value::Float(123_f32), + "Expected Value::TimeMicros, Value::Long or Value::Int, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "long", "logicalType": "timestamp-millis" }"#, + Value::Float(123_f32), + "Expected Value::TimestampMillis, Value::Long or Value::Int, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "long", "logicalType": "timestamp-micros" }"#, + Value::Float(123_f32), + "Expected Value::TimestampMicros, Value::Long or Value::Int, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "long", "logicalType": "timestamp-nanos" }"#, + Value::Float(123_f32), + "Expected Value::TimestampNanos, Value::Long or Value::Int, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "long", "logicalType": "local-timestamp-millis" }"#, + Value::Float(123_f32), + "Expected Value::LocalTimestampMillis, Value::Long or Value::Int, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "long", "logicalType": "local-timestamp-micros" }"#, + Value::Float(123_f32), + "Expected Value::LocalTimestampMicros, Value::Long or Value::Int, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "long", "logicalType": "local-timestamp-nanos" }"#, + Value::Float(123_f32), + "Expected Value::LocalTimestampNanos, Value::Long or Value::Int, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "null" }"#, + Value::Float(123_f32), + "Expected Value::Null, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "boolean" }"#, + Value::Float(123_f32), + "Expected Value::Boolean, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "int" }"#, + Value::Float(123_f32), + "Expected Value::Int, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "long" }"#, + Value::Float(123_f32), + "Expected Value::Long or Value::Int, got: Float(123.0)", + ), + ( + r#"{ "name": "NAME", "type": "float" }"#, + Value::Boolean(false), + r#"Expected Value::Float, Value::Double, Value::Int, Value::Long or Value::String ("NaN", "INF", "Infinity", "-INF" or "-Infinity"), got: Boolean(false)"#, + ), + ( + r#"{ "name": "NAME", "type": "double" }"#, + Value::Boolean(false), + r#"Expected Value::Double, Value::Float, Value::Int, Value::Long or Value::String ("NaN", "INF", "Infinity", "-INF" or "-Infinity"), got: Boolean(false)"#, + ), + ( + r#"{ "name": "NAME", "type": "string" }"#, + Value::Boolean(false), + "Expected Value::String, Value::Bytes or Value::Fixed, got: Boolean(false)", + ), + ( + r#"{ "name": "NAME", "type": "enum", "symbols": ["one", "two"] }"#, + Value::Boolean(false), + "Expected Value::Enum, got: Boolean(false)", + ), + ]; for (schema_str, value, expected_error) in data { let schema = Schema::parse_str(schema_str)?; diff --git a/avro/src/util.rs b/avro/src/util.rs index 9247694..1264a8a 100644 --- a/avro/src/util.rs +++ b/avro/src/util.rs @@ -15,13 +15,13 @@ // specific language governing permissions and limitations // under the License. -use crate::{schema::Documentation, AvroResult, Error}; +use crate::{AvroResult, Error, schema::Documentation}; use serde_json::{Map, Value}; use std::{ io::{Read, Write}, sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, Once, + atomic::{AtomicBool, AtomicUsize, Ordering}, }, }; diff --git a/avro/src/validator.rs b/avro/src/validator.rs index f2e178b..5012a4a 100644 --- a/avro/src/validator.rs +++ b/avro/src/validator.rs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -use crate::{schema::Namespace, AvroResult, Error}; +use crate::{AvroResult, Error, schema::Namespace}; use log::debug; use regex_lite::Regex; use std::sync::OnceLock; diff --git a/avro/src/writer.rs b/avro/src/writer.rs index 1950430..413e60a 100644 --- a/avro/src/writer.rs +++ b/avro/src/writer.rs @@ -17,12 +17,12 @@ //! Logic handling writing in Avro format at user level. use crate::{ + AvroResult, Codec, Error, encode::{encode, encode_internal, encode_to_vec}, headers::{HeaderBuilder, RabinFingerprintHeader}, schema::{AvroSchema, Name, ResolvedOwnedSchema, ResolvedSchema, Schema}, ser_schema::SchemaAwareWriteSerializer, types::Value, - AvroResult, Codec, Error, }; use serde::Serialize; use std::{ @@ -757,6 +757,7 @@ mod tests { use super::*; use crate::{ + Reader, decimal::Decimal, duration::{Days, Duration, Millis, Months}, headers::GlueSchemaUuidHeader, @@ -764,7 +765,6 @@ mod tests { schema::{DecimalSchema, FixedSchema, Name}, types::Record, util::zig_i64, - Reader, }; use pretty_assertions::assert_eq; use serde::{Deserialize, Serialize}; @@ -1351,12 +1351,19 @@ mod tests { let key = "avro.stringKey".to_string(); match writer.add_user_metadata(key.clone(), "value") { Err(ref e @ Error::InvalidMetadataKey(_)) => { - assert_eq!(e.to_string(), format!("Metadata keys starting with 'avro.' are reserved for internal usage: {key}.")) + assert_eq!( + e.to_string(), + format!( + "Metadata keys starting with 'avro.' are reserved for internal usage: {key}." + ) + ) } Err(e) => panic!( "Unexpected error occurred while writing user metadata with reserved prefix ('avro.'): {e:?}" ), - Ok(_) => panic!("Expected an error that the metadata key cannot be prefixed with 'avro.'"), + Ok(_) => { + panic!("Expected an error that the metadata key cannot be prefixed with 'avro.'") + } } Ok(()) diff --git a/avro/tests/append_to_existing.rs b/avro/tests/append_to_existing.rs index 28120b6..e9af4b3 100644 --- a/avro/tests/append_to_existing.rs +++ b/avro/tests/append_to_existing.rs @@ -16,9 +16,8 @@ // under the License. use apache_avro::{ - read_marker, + AvroResult, Reader, Schema, Writer, read_marker, types::{Record, Value}, - AvroResult, Reader, Schema, Writer, }; use apache_avro_test_helper::TestResult; diff --git a/avro/tests/avro-3786.rs b/avro/tests/avro-3786.rs index a0e7714..c509a3f 100644 --- a/avro/tests/avro-3786.rs +++ b/avro/tests/avro-3786.rs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -use apache_avro::{from_avro_datum, to_avro_datum, to_value, types, Schema}; +use apache_avro::{Schema, from_avro_datum, to_avro_datum, to_value, types}; use apache_avro_test_helper::TestResult; #[test] @@ -611,8 +611,8 @@ fn deserialize_union_with_different_enum_order_defined_in_record() -> TestResult } #[test] -fn deserialize_union_with_record_with_enum_defined_inline_reader_has_different_indices( -) -> TestResult { +fn deserialize_union_with_record_with_enum_defined_inline_reader_has_different_indices() +-> TestResult { #[derive( Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, serde::Deserialize, serde::Serialize, )] diff --git a/avro/tests/avro-3787.rs b/avro/tests/avro-3787.rs index c08c3c6..1795fca 100644 --- a/avro/tests/avro-3787.rs +++ b/avro/tests/avro-3787.rs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -use apache_avro::{from_avro_datum, to_avro_datum, to_value, types, Schema}; +use apache_avro::{Schema, from_avro_datum, to_avro_datum, to_value, types}; use apache_avro_test_helper::TestResult; #[test] diff --git a/avro/tests/codecs.rs b/avro/tests/codecs.rs index 02137e9..f58f770 100644 --- a/avro/tests/codecs.rs +++ b/avro/tests/codecs.rs @@ -16,8 +16,8 @@ // under the License. use apache_avro::{ - types::{Record, Value}, Codec, DeflateSettings, Reader, Schema, Writer, + types::{Record, Value}, }; use apache_avro_test_helper::TestResult; use miniz_oxide::deflate::CompressionLevel; diff --git a/avro/tests/io.rs b/avro/tests/io.rs index c6589a9..0943eab 100644 --- a/avro/tests/io.rs +++ b/avro/tests/io.rs @@ -16,7 +16,7 @@ // under the License. //! Port of https://github.com/apache/avro/blob/release-1.9.1/lang/py/test/test_io.py -use apache_avro::{from_avro_datum, to_avro_datum, types::Value, Error, Schema}; +use apache_avro::{Error, Schema, from_avro_datum, to_avro_datum, types::Value}; use apache_avro_test_helper::TestResult; use pretty_assertions::assert_eq; use std::{io::Cursor, sync::OnceLock}; diff --git a/avro/tests/schema.rs b/avro/tests/schema.rs index 73967bb..96ba41d 100644 --- a/avro/tests/schema.rs +++ b/avro/tests/schema.rs @@ -21,15 +21,15 @@ use std::{ }; use apache_avro::{ - from_avro_datum, from_value, + Codec, Error, Reader, Schema, Writer, from_avro_datum, from_value, schema::{EnumSchema, FixedSchema, Name, RecordField, RecordSchema}, to_avro_datum, to_value, types::{Record, Value}, - Codec, Error, Reader, Schema, Writer, }; use apache_avro_test_helper::{ - data::{examples, valid_examples, DOC_EXAMPLES}, - init, TestResult, + TestResult, + data::{DOC_EXAMPLES, examples, valid_examples}, + init, }; #[test] @@ -366,11 +366,11 @@ fn test_parse_reused_record_schema_by_fullname() -> TestResult { assert_eq!(fields.len(), 3, "The number of the fields is not correct!"); let RecordField { - ref name, + name, doc: _, default: _, aliases: _, - ref schema, + schema, order: _, position: _, custom_attributes: _, @@ -379,7 +379,7 @@ fn test_parse_reused_record_schema_by_fullname() -> TestResult { assert_eq!(name, "min_temp"); match schema { - Schema::Ref { ref name } => { + Schema::Ref { name } => { assert_eq!(name.fullname(None), "prefix.Temp", "Name does not match!"); } unexpected => unreachable!("Unexpected schema type: {:?}", unexpected), diff --git a/avro/tests/shared.rs b/avro/tests/shared.rs index df12129..c9397df 100644 --- a/avro/tests/shared.rs +++ b/avro/tests/shared.rs @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -use apache_avro::{types::Value, Codec, Reader, Schema, Writer}; +use apache_avro::{Codec, Reader, Schema, Writer, types::Value}; use apache_avro_test_helper::TestResult; use std::{ fmt, diff --git a/avro/tests/to_from_avro_datum_schemata.rs b/avro/tests/to_from_avro_datum_schemata.rs index 9986cbb..c300c0f 100644 --- a/avro/tests/to_from_avro_datum_schemata.rs +++ b/avro/tests/to_from_avro_datum_schemata.rs @@ -16,10 +16,10 @@ // under the License. use apache_avro::{ - from_avro_datum_reader_schemata, from_avro_datum_schemata, to_avro_datum_schemata, - types::Value, Codec, Reader, Schema, Writer, + Codec, Reader, Schema, Writer, from_avro_datum_reader_schemata, from_avro_datum_schemata, + to_avro_datum_schemata, types::Value, }; -use apache_avro_test_helper::{init, TestResult}; +use apache_avro_test_helper::{TestResult, init}; static SCHEMA_A_STR: &str = r#"{ "name": "A", diff --git a/avro/tests/union_schema.rs b/avro/tests/union_schema.rs index db0f356..b9c22ff 100644 --- a/avro/tests/union_schema.rs +++ b/avro/tests/union_schema.rs @@ -15,8 +15,8 @@ // specific language governing permissions and limitations // under the License. -use apache_avro::{from_value, AvroResult, Codec, Reader, Schema, Writer}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use apache_avro::{AvroResult, Codec, Reader, Schema, Writer, from_value}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; static SCHEMA_A_STR: &str = r#"{ "name": "A", diff --git a/avro/tests/validators.rs b/avro/tests/validators.rs index fc45353..ab37248 100644 --- a/avro/tests/validators.rs +++ b/avro/tests/validators.rs @@ -16,13 +16,13 @@ // under the License. use apache_avro::{ + AvroResult, schema::Namespace, validator::{ - set_enum_symbol_name_validator, set_record_field_name_validator, set_schema_name_validator, - set_schema_namespace_validator, EnumSymbolNameValidator, RecordFieldNameValidator, - SchemaNameValidator, SchemaNamespaceValidator, + EnumSymbolNameValidator, RecordFieldNameValidator, SchemaNameValidator, + SchemaNamespaceValidator, set_enum_symbol_name_validator, set_record_field_name_validator, + set_schema_name_validator, set_schema_namespace_validator, }, - AvroResult, }; use apache_avro_test_helper::TestResult; diff --git a/avro_derive/src/lib.rs b/avro_derive/src/lib.rs index 767a46a..6e84658 100644 --- a/avro_derive/src/lib.rs +++ b/avro_derive/src/lib.rs @@ -22,8 +22,8 @@ use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::{ - parse_macro_input, spanned::Spanned, AttrStyle, Attribute, DeriveInput, Ident, Meta, Type, - TypePath, + AttrStyle, Attribute, DeriveInput, Ident, Meta, Type, TypePath, parse_macro_input, + spanned::Spanned, }; #[derive(darling::FromAttributes)] @@ -106,7 +106,7 @@ fn derive_avro_schema(input: &mut DeriveInput) -> Result<TokenStream, Vec<syn::E return Err(vec![syn::Error::new( input.ident.span(), "AvroSchema derive only works for structs and simple enums ", - )]) + )]); } }; let ident = &input.ident; @@ -198,13 +198,13 @@ fn get_data_struct_schema_def( return Err(vec![syn::Error::new( error_span, "AvroSchema derive does not work for tuple structs", - )]) + )]); } syn::Fields::Unit => { return Err(vec![syn::Error::new( error_span, "AvroSchema derive does not work for unit structs", - )]) + )]); } } let record_doc = preserve_optional(record_doc); @@ -287,13 +287,13 @@ fn type_to_schema_expr(ty: &Type) -> Result<TokenStream, Vec<syn::Error>> { return Err(vec![syn::Error::new_spanned( ty, "AvroSchema: Cannot guarantee successful deserialization of this type", - )]) + )]); } "u64" => { return Err(vec![syn::Error::new_spanned( - ty, - "Cannot guarantee successful serialization of this type due to overflow concerns", - )]) + ty, + "Cannot guarantee successful serialization of this type due to overflow concerns", + )]); } // Can't guarantee serialization type _ => { // Fails when the type does not implement AvroSchemaComponent directly @@ -376,11 +376,7 @@ fn extract_outer_doc(attributes: &[Attribute]) -> Option<String> { }) .collect::<Vec<String>>() .join("\n"); - if doc.is_empty() { - None - } else { - Some(doc) - } + if doc.is_empty() { None } else { Some(doc) } } fn preserve_optional(op: Option<impl quote::ToTokens>) -> TokenStream { @@ -633,10 +629,12 @@ mod tests { Ok(mut input) => { let schema_token_stream = derive_avro_schema(&mut input); assert!(&schema_token_stream.is_ok()); - assert!(schema_token_stream - .unwrap() - .to_string() - .contains("namespace.testing")) + assert!( + schema_token_stream + .unwrap() + .to_string() + .contains("namespace.testing") + ) } Err(error) => panic!( "Failed to parse as derive input when it should be able to. Error: {error:?}" diff --git a/avro_derive/tests/derive.rs b/avro_derive/tests/derive.rs index 721b6d4..a379b7b 100644 --- a/avro_derive/tests/derive.rs +++ b/avro_derive/tests/derive.rs @@ -16,13 +16,12 @@ // under the License. use apache_avro::{ - from_value, - schema::{derive::AvroSchemaComponent, AvroSchema}, - Reader, Schema, Writer, + Reader, Schema, Writer, from_value, + schema::{AvroSchema, derive::AvroSchemaComponent}, }; use apache_avro_derive::*; use proptest::prelude::*; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; use std::collections::HashMap; #[cfg(test)] diff --git a/wasm-demo/tests/demos.rs b/wasm-demo/tests/demos.rs index d92a97c..30bbe9c 100644 --- a/wasm-demo/tests/demos.rs +++ b/wasm-demo/tests/demos.rs @@ -20,7 +20,7 @@ use std::io::BufWriter; use wasm_bindgen_test::*; -use apache_avro::{from_value, to_value, types::Record, Codec, Reader, Schema, Writer}; +use apache_avro::{Codec, Reader, Schema, Writer, from_value, to_value, types::Record}; use serde::{Deserialize, Serialize}; wasm_bindgen_test_configure!(run_in_browser);
