This is an automated email from the ASF dual-hosted git repository.

mgrigorov pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/avro-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 8397883  fix: Calculate the RecordSchema's `lookup` table on build 
(#411)
8397883 is described below

commit 839788386ae458241947c1b47b7c97809232190b
Author: Martin Grigorov <[email protected]>
AuthorDate: Mon Jan 19 13:43:45 2026 +0200

    fix: Calculate the RecordSchema's `lookup` table on build (#411)
    
    * fix: Calculate the RecordSchema's `lookup` table on build
    
    Fixes #403
    
    * Add a unit test for RecordSchema's custom attributes
    
    * Allow to build a RecordSchema without any fields
    
    Co-authored-by: Kriskras99 <[email protected]>
    
    * Do not call `.fields(vec![])`, since it is the default anyway
    
    ---------
    
    Co-authored-by: Kriskras99 <[email protected]>
---
 avro/src/schema/mod.rs           |   3 -
 avro/src/schema/record/schema.rs | 135 ++++++++++++++++++++++++++++++++++++++-
 2 files changed, 134 insertions(+), 4 deletions(-)

diff --git a/avro/src/schema/mod.rs b/avro/src/schema/mod.rs
index 25319f7..bc7832b 100644
--- a/avro/src/schema/mod.rs
+++ b/avro/src/schema/mod.rs
@@ -2702,8 +2702,6 @@ mod tests {
     // AVRO-3248
     #[test]
     fn test_union_of_records() -> TestResult {
-        use std::iter::FromIterator;
-
         // A and B are the same except the name.
         let schema_str_a = r#"{
             "name": "A",
@@ -2751,7 +2749,6 @@ mod tests {
                         ])?))
                         .build(),
                 ])
-                .lookup(BTreeMap::from_iter(vec![("field_one".to_string(), 
0)]))
                 .build(),
         );
 
diff --git a/avro/src/schema/record/schema.rs b/avro/src/schema/record/schema.rs
index 28a30a6..e02a23e 100644
--- a/avro/src/schema/record/schema.rs
+++ b/avro/src/schema/record/schema.rs
@@ -31,15 +31,26 @@ pub struct RecordSchema {
     #[builder(default)]
     pub doc: Documentation,
     /// The set of fields of the schema
+    #[builder(default)]
     pub fields: Vec<RecordField>,
     /// The `lookup` table maps field names to their position in the `Vec`
     /// of `fields`.
+    #[builder(skip = calculate_lookup_table(&fields))]
     pub lookup: BTreeMap<String, usize>,
     /// The custom attributes of the schema
-    #[builder(default = BTreeMap::new())]
+    #[builder(default)]
     pub attributes: BTreeMap<String, Value>,
 }
 
+/// Calculate the lookup table for the given fields.
+fn calculate_lookup_table(fields: &[RecordField]) -> BTreeMap<String, usize> {
+    fields
+        .iter()
+        .enumerate()
+        .map(|(i, field)| (field.name.clone(), i))
+        .collect()
+}
+
 #[derive(Debug, Default)]
 pub(crate) enum RecordSchemaParseLocation {
     /// When the parse is happening at root level
@@ -49,3 +60,125 @@ pub(crate) enum RecordSchemaParseLocation {
     /// When the parse is happening inside a record field
     FromField,
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::Schema;
+    use apache_avro_test_helper::TestResult;
+    use pretty_assertions::assert_eq;
+
+    #[test]
+    fn avro_rs_403_record_schema_builder_no_fields() -> TestResult {
+        let name = Name::new("TestRecord")?;
+
+        let record_schema = RecordSchema::builder().name(name.clone()).build();
+
+        assert_eq!(record_schema.name, name);
+        assert_eq!(record_schema.aliases, None);
+        assert_eq!(record_schema.doc, None);
+        assert_eq!(record_schema.fields.len(), 0);
+        assert_eq!(record_schema.lookup.len(), 0);
+        assert_eq!(record_schema.attributes.len(), 0);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_403_record_schema_builder_no_fields_with_aliases() -> 
TestResult {
+        let name = Name::new("TestRecord")?;
+
+        let record_schema = RecordSchema::builder()
+            .name(name.clone())
+            .aliases(Some(vec!["alias_1".into()]))
+            .build();
+
+        assert_eq!(record_schema.name, name);
+        assert_eq!(record_schema.aliases, Some(vec!["alias_1".into()]));
+        assert_eq!(record_schema.doc, None);
+        assert_eq!(record_schema.fields.len(), 0);
+        assert_eq!(record_schema.lookup.len(), 0);
+        assert_eq!(record_schema.attributes.len(), 0);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_403_record_schema_builder_no_fields_with_doc() -> TestResult {
+        let name = Name::new("TestRecord")?;
+
+        let record_schema = RecordSchema::builder()
+            .name(name.clone())
+            .doc(Some("some_doc".into()))
+            .build();
+
+        assert_eq!(record_schema.name, name);
+        assert_eq!(record_schema.aliases, None);
+        assert_eq!(record_schema.doc, Some("some_doc".into()));
+        assert_eq!(record_schema.fields.len(), 0);
+        assert_eq!(record_schema.lookup.len(), 0);
+        assert_eq!(record_schema.attributes.len(), 0);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_403_record_schema_builder_no_fields_with_attributes() -> 
TestResult {
+        let name = Name::new("TestRecord")?;
+        let attrs: BTreeMap<String, Value> = [
+            ("bool_key".into(), Value::Bool(true)),
+            ("key_2".into(), Value::String("value_2".into())),
+        ]
+        .into_iter()
+        .collect();
+
+        let record_schema = RecordSchema::builder()
+            .name(name.clone())
+            .attributes(attrs.clone())
+            .build();
+
+        assert_eq!(record_schema.name, name);
+        assert_eq!(record_schema.aliases, None);
+        assert_eq!(record_schema.doc, None);
+        assert_eq!(record_schema.fields.len(), 0);
+        assert_eq!(record_schema.lookup.len(), 0);
+        assert_eq!(record_schema.attributes, attrs);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_403_record_schema_builder_with_fields() -> TestResult {
+        let name = Name::new("TestRecord")?;
+        let fields = vec![
+            RecordField::builder()
+                .name("field1_null".into())
+                .schema(Schema::Null)
+                .build(),
+            RecordField::builder()
+                .name("field2_bool".into())
+                .schema(Schema::Boolean)
+                .build(),
+        ];
+
+        let record_schema = RecordSchema::builder()
+            .name(name.clone())
+            .fields(fields.clone())
+            .build();
+
+        let expected_lookup: BTreeMap<String, usize> =
+            [("field1_null".into(), 0), ("field2_bool".into(), 1)]
+                .iter()
+                .cloned()
+                .collect();
+
+        assert_eq!(record_schema.name, name);
+        assert_eq!(record_schema.aliases, None);
+        assert_eq!(record_schema.doc, None);
+        assert_eq!(record_schema.fields, fields);
+        assert_eq!(record_schema.lookup, expected_lookup);
+        assert_eq!(record_schema.attributes.len(), 0);
+
+        Ok(())
+    }
+}

Reply via email to