This is an automated email from the ASF dual-hosted git repository. mgrigorov pushed a commit to branch more-builder-with-bon in repository https://gitbox.apache.org/repos/asf/avro-rs.git
commit 1a228019fb1069da17df9b2dc75b04357716836c Author: Martin Tzvetanov Grigorov <[email protected]> AuthorDate: Mon Jan 20 11:42:40 2025 +0200 feat: Use builder pattern for creating RecordSchema, EnumSchema, FixedSchema and RecordField Replace typed-builder with bon (https://bon-rs.com) because it provides more features, e.g. named parameters via Function Builder Signed-off-by: Martin Tzvetanov Grigorov <[email protected]> --- Cargo.lock | 64 +++++++++++++++++++---------- avro/Cargo.toml | 2 +- avro/src/decode.rs | 14 +++---- avro/src/schema.rs | 118 ++++++++++++++++++++++++++--------------------------- avro/src/writer.rs | 10 ++--- 5 files changed, 113 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 605ae3e..e570099 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -76,6 +76,7 @@ dependencies = [ "apache-avro-derive", "apache-avro-test-helper", "bigdecimal", + "bon", "bzip2", "crc32fast", "criterion", @@ -100,7 +101,6 @@ dependencies = [ "strum", "strum_macros", "thiserror", - "typed-builder", "uuid", "xz2", "zstd", @@ -191,6 +191,31 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bon" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe7acc34ff59877422326db7d6f2d845a582b16396b6b08194942bf34c6528ab" +dependencies = [ + "bon-macros", + "rustversion", +] + +[[package]] +name = "bon-macros" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4159dd617a7fbc9be6a692fe69dc2954f8e6bb6bb5e4d7578467441390d77fd0" +dependencies = [ + "darling", + "ident_case", + "prettyplease", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -428,6 +453,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", + "strsim", "syn", ] @@ -931,6 +957,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettyplease" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.93" @@ -1260,6 +1296,12 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.26.3" @@ -1320,26 +1362,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "typed-builder" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e14ed59dc8b7b26cacb2a92bad2e8b1f098806063898ab42a3bd121d7d45e75" -dependencies = [ - "typed-builder-macro", -] - -[[package]] -name = "typed-builder-macro" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "560b82d656506509d43abe30e0ba64c56b1953ab3d4fe7ba5902747a7a3cedd5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "typenum" version = "1.17.0" diff --git a/avro/Cargo.toml b/avro/Cargo.toml index 8e513ce..d7b0e8f 100644 --- a/avro/Cargo.toml +++ b/avro/Cargo.toml @@ -55,6 +55,7 @@ name = "single" [dependencies] apache-avro-derive = { default-features = false, version = "0.18.0", path = "../avro_derive", optional = true } bigdecimal = { default-features = false, version = "0.4.7", features = ["std", "serde"] } +bon = { default-features = false, version = "3.3.2" } bzip2 = { version = "0.5.0", optional = true } crc32fast = { default-features = false, version = "1.4.2", optional = true } digest = { default-features = false, version = "0.10.7", features = ["core-api"] } @@ -69,7 +70,6 @@ snap = { default-features = false, version = "1.1.0", optional = true } strum = { default-features = false, version = "0.26.3" } strum_macros = { default-features = false, version = "0.26.4" } thiserror = { default-features = false, version = "2.0.11" } -typed-builder = { default-features = false, version = "0.20.0" } uuid = { default-features = false, version = "1.12.0", features = ["serde", "std"] } xz2 = { default-features = false, version = "0.1.7", optional = true } zstd = { default-features = false, version = "0.13.2", optional = true } diff --git a/avro/src/decode.rs b/avro/src/decode.rs index 46e3381..7f5737d 100644 --- a/avro/src/decode.rs +++ b/avro/src/decode.rs @@ -415,14 +415,12 @@ mod tests { fn test_negative_decimal_value() -> TestResult { use crate::{encode::encode, schema::Name}; use num_bigint::ToBigInt; - let inner = Box::new(Schema::Fixed(FixedSchema { - size: 2, - doc: None, - name: Name::new("decimal")?, - aliases: None, - default: None, - attributes: Default::default(), - })); + let inner = Box::new(Schema::Fixed( + FixedSchema::builder() + .name(Name::new("decimal")?) + .size(2) + .build(), + )); let schema = Schema::Decimal(DecimalSchema { inner, precision: 4, diff --git a/avro/src/schema.rs b/avro/src/schema.rs index 93dbe18..13fc905 100644 --- a/avro/src/schema.rs +++ b/avro/src/schema.rs @@ -623,11 +623,12 @@ pub(crate) fn resolve_names_with_schemata( } /// Represents a `field` in a `record` Avro schema. -#[derive(Clone, Debug, PartialEq)] +#[derive(bon::Builder, Clone, Debug, PartialEq)] pub struct RecordField { /// Name of the field. pub name: String, /// Documentation of the field. + #[builder(default)] pub doc: Documentation, /// Aliases of the field's name. They have no namespace. pub aliases: Option<Vec<String>>, @@ -640,10 +641,13 @@ pub struct RecordField { /// Order of the field. /// /// **NOTE** This currently has no effect. + #[builder(default = RecordFieldOrder::Ignore)] pub order: RecordFieldOrder, /// Position of the field in the list of `field` of its parent `Schema` + #[builder(default)] pub position: usize, /// A collection of all unknown fields in the record field. + #[builder(default = BTreeMap::new())] pub custom_attributes: BTreeMap<String, Value>, } @@ -788,13 +792,15 @@ impl RecordField { } /// A description of an Enum schema. -#[derive(Debug, Clone)] +#[derive(bon::Builder, Debug, Clone)] pub struct RecordSchema { /// The name of the schema pub name: Name, /// The aliases of the schema + #[builder(default)] pub aliases: Aliases, /// The documentation of the schema + #[builder(default)] pub doc: Documentation, /// The set of fields of the schema pub fields: Vec<RecordField>, @@ -802,40 +808,47 @@ pub struct RecordSchema { /// of `fields`. pub lookup: BTreeMap<String, usize>, /// The custom attributes of the schema + #[builder(default = BTreeMap::new())] pub attributes: BTreeMap<String, Value>, } /// A description of an Enum schema. -#[derive(Debug, Clone)] +#[derive(bon::Builder, Debug, Clone)] pub struct EnumSchema { /// The name of the schema pub name: Name, /// The aliases of the schema + #[builder(default)] pub aliases: Aliases, /// The documentation of the schema + #[builder(default)] pub doc: Documentation, /// The set of symbols of the schema pub symbols: Vec<String>, /// An optional default symbol used for compatibility pub default: Option<String>, /// The custom attributes of the schema + #[builder(default = BTreeMap::new())] pub attributes: BTreeMap<String, Value>, } /// A description of a Union schema. -#[derive(Debug, Clone)] +#[derive(bon::Builder, Debug, Clone)] pub struct FixedSchema { /// The name of the schema pub name: Name, /// The aliases of the schema + #[builder(default)] pub aliases: Aliases, /// The documentation of the schema + #[builder(default)] pub doc: Documentation, /// The size of the fixed schema pub size: usize, /// An optional default symbol used for compatibility pub default: Option<String>, /// The custom attributes of the schema + #[builder(default = BTreeMap::new())] pub attributes: BTreeMap<String, Value>, } @@ -2644,12 +2657,9 @@ mod tests { #[test] fn test_avro_3621_nullable_record_field() -> TestResult { - let nullable_record_field = RecordField { - name: "next".to_string(), - doc: None, - default: None, - aliases: None, - schema: Schema::Union(UnionSchema::new(vec![ + let nullable_record_field = RecordField::builder() + .name("next".to_string()) + .schema(Schema::Union(UnionSchema::new(vec![ Schema::Null, Schema::Ref { name: Name { @@ -2657,24 +2667,20 @@ mod tests { namespace: None, }, }, - ])?), - order: RecordFieldOrder::Ascending, - position: 1, - custom_attributes: Default::default(), - }; + ])?)) + .order(RecordFieldOrder::Ascending) + .position(1) + .build(); assert!(nullable_record_field.is_nullable()); - let non_nullable_record_field = RecordField { - name: "next".to_string(), - doc: None, - default: Some(json!(2)), - aliases: None, - schema: Schema::Long, - order: RecordFieldOrder::Ascending, - position: 1, - custom_attributes: Default::default(), - }; + let non_nullable_record_field = RecordField::builder() + .name("next".to_string()) + .default(json!(2)) + .schema(Schema::Long) + .order(RecordFieldOrder::Ascending) + .position(1) + .build(); assert!(!non_nullable_record_field.is_nullable()); Ok(()) @@ -2716,30 +2722,23 @@ mod tests { .unwrap() .clone(); - let schema_c_expected = Schema::Record(RecordSchema { - name: Name::new("C")?, - aliases: None, - doc: None, - fields: vec![RecordField { - name: "field_one".to_string(), - doc: None, - default: None, - aliases: None, - schema: Schema::Union(UnionSchema::new(vec![ - Schema::Ref { - name: Name::new("A")?, - }, - Schema::Ref { - name: Name::new("B")?, - }, - ])?), - order: RecordFieldOrder::Ignore, - position: 0, - custom_attributes: Default::default(), - }], - lookup: BTreeMap::from_iter(vec![("field_one".to_string(), 0)]), - attributes: Default::default(), - }); + 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()]) + .lookup(BTreeMap::from_iter(vec![("field_one".to_string(), 0)])) + .build(), + ); assert_eq!(schema_c, schema_c_expected); Ok(()) @@ -3382,17 +3381,16 @@ mod tests { doc: None, default: None, aliases: None, - schema: Schema::Enum(EnumSchema { - name: Name { - name: "enum".to_owned(), - namespace: None, - }, - aliases: None, - doc: None, - symbols: vec!["one".to_string(), "two".to_string(), "three".to_string()], - default: None, - attributes: Default::default(), - }), + schema: Schema::Enum( + EnumSchema::builder() + .name(Name::new("enum")?) + .symbols(vec![ + "one".to_string(), + "two".to_string(), + "three".to_string(), + ]) + .build(), + ), order: RecordFieldOrder::Ascending, position: 0, custom_attributes: Default::default(), diff --git a/avro/src/writer.rs b/avro/src/writer.rs index f1fdae4..0f824b3 100644 --- a/avro/src/writer.rs +++ b/avro/src/writer.rs @@ -31,21 +31,21 @@ const DEFAULT_BLOCK_SIZE: usize = 16000; const AVRO_OBJECT_HEADER: &[u8] = b"Obj\x01"; /// Main interface for writing Avro formatted values. -#[derive(typed_builder::TypedBuilder)] +#[derive(bon::Builder)] pub struct Writer<'a, W> { schema: &'a Schema, writer: W, - #[builder(default, setter(skip))] + #[builder(skip)] resolved_schema: Option<ResolvedSchema<'a>>, #[builder(default = Codec::Null)] codec: Codec, #[builder(default = DEFAULT_BLOCK_SIZE)] block_size: usize, - #[builder(default = Vec::with_capacity(block_size), setter(skip))] + #[builder(skip = Vec::with_capacity(block_size))] buffer: Vec<u8>, - #[builder(default, setter(skip))] + #[builder(skip)] serializer: Serializer, - #[builder(default = 0, setter(skip))] + #[builder(skip)] num_values: usize, #[builder(default = generate_sync_marker())] marker: [u8; 16],
