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

Kriskras99 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 784294d  fix: Allow serializing an enum inside a union (#591)
784294d is described below

commit 784294dd2cf7a6d6290bd08dda2b31a5ac4fa34f
Author: Kriskras99 <[email protected]>
AuthorDate: Sat Jul 25 15:52:51 2026 +0200

    fix: Allow serializing an enum inside a union (#591)
    
    * fix: Allow serializing an enum inside a union
    
    * fix: Add more tests and resolve references
    
    * fix: Require exact name for enum in union
---
 avro/src/serde/ser_schema/mod.rs   | 271 ++++++++++++++++++++++++++++++++++++-
 avro/src/serde/ser_schema/union.rs |  36 ++++-
 2 files changed, 300 insertions(+), 7 deletions(-)

diff --git a/avro/src/serde/ser_schema/mod.rs b/avro/src/serde/ser_schema/mod.rs
index 5aba68b..4369235 100644
--- a/avro/src/serde/ser_schema/mod.rs
+++ b/avro/src/serde/ser_schema/mod.rs
@@ -428,7 +428,7 @@ impl<'s, 'w, W: Write, S: Borrow<Schema>> Serializer for 
SchemaAwareSerializer<'
 
     fn serialize_unit_variant(
         self,
-        _name: &'static str,
+        name: &'static str,
         variant_index: u32,
         variant: &'static str,
     ) -> Result<Self::Ok, Self::Error> {
@@ -445,15 +445,17 @@ impl<'s, 'w, W: Write, S: Borrow<Schema>> Serializer for 
SchemaAwareSerializer<'
                     Err(self.error("unit variant", format!(r#"Expected symbol 
"{variant}" at index {variant_index} in enum"#)))
                 }
             }
-            Schema::Union(union) => match 
self.get_resolved_union_variant(union, variant_index)? {
+            Schema::Union(union) if (variant_index as usize) < 
union.variants().len() => match self.get_resolved_union_variant(union, 
variant_index)? {
                 // Bare union
                 Schema::Null => zig_i32(variant_index as i32, &mut 
*self.writer),
                 Schema::Record(record) if record.fields.is_empty() && 
record.name.name() == variant => {
                     // Union of records
                     zig_i32(variant_index as i32, &mut *self.writer)
                 }
-                _ => Err(self.error("unit variant", format!("Expected 
Schema::Null | Schema::Record(name: {variant}, fields: []) at index 
{variant_index} in the union"))),
+                _ => UnionSerializer::new(self.writer, union, 
self.config).serialize_unit_variant(name, variant_index, variant)
             }
+            // This branch happens if the enum has more variants than the 
union, which is valid if the target schema is a enum schema in the union
+            Schema::Union(union) => UnionSerializer::new(self.writer, union, 
self.config).serialize_unit_variant(name, variant_index, variant),
             _ => Err(self.error("unit variant", format!("Expected 
Schema::Enum(symbols[{variant_index}] == {variant}) | 
Schema::Union(variants[{variant_index}] == Schema::Null | Schema::Record(name: 
{variant}, fields: []))"))),
         }
     }
@@ -2216,4 +2218,267 @@ mod tests {
 
         Ok(())
     }
+
+    #[test]
+    fn avro_rs_529_enum_larger_than_union_schema() -> TestResult {
+        #[derive(Debug, Serialize)]
+        enum TestEnum {
+            A,
+            B,
+            C,
+        }
+
+        let schema = Schema::parse_str(
+            r#"
+            [
+                "int",
+                {
+                    "type": "enum",
+                    "name": "TestEnum",
+                    "symbols": ["A", "B", "C"]
+                }
+            ]
+            "#,
+        )?;
+        let names = HashMap::new();
+        assert_serialize(TestEnum::A, &schema, &names, &[2, 0]);
+        assert_serialize(TestEnum::B, &schema, &names, &[2, 2]);
+        assert_serialize(TestEnum::C, &schema, &names, &[2, 4]);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_529_enum_equal_to_union_schema() -> TestResult {
+        #[derive(Debug, Serialize)]
+        enum TestEnum {
+            A,
+            B,
+        }
+
+        let schema = Schema::parse_str(
+            r#"
+            [
+                "int",
+                {
+                    "type": "enum",
+                    "name": "TestEnum",
+                    "symbols": ["A", "B"]
+                }
+            ]
+            "#,
+        )?;
+        let names = HashMap::new();
+        assert_serialize(TestEnum::A, &schema, &names, &[2, 0]);
+        assert_serialize(TestEnum::B, &schema, &names, &[2, 2]);
+
+        Ok(())
+    }
+
+    #[test]
+    #[should_panic(expected = "assertion failed: `(left == right)`")]
+    fn avro_rs_529_enum_with_union_schema_with_null() {
+        #[derive(Debug, Serialize)]
+        enum TestEnum {
+            A,
+            B,
+        }
+
+        let schema = Schema::parse_str(
+            r#"
+            [
+                "null",
+                {
+                    "type": "enum",
+                    "name": "TestEnum",
+                    "symbols": ["A", "B"]
+                }
+            ]
+            "#,
+        )
+        .unwrap();
+        let names = HashMap::new();
+        // This works because the value index does not contain a null
+        assert_serialize(TestEnum::B, &schema, &names, &[2, 2]);
+        // This doesn't work because the value index contains a null, so the 
serializer uses that and
+        // writes [0, 0]
+        assert_serialize(TestEnum::A, &schema, &names, &[2, 0]);
+    }
+
+    #[test]
+    #[should_panic(expected = "assertion failed: `(left == right)`")]
+    fn avro_rs_529_enum_with_union_schema_with_empty_record_matching_variant() 
{
+        #[derive(Debug, Serialize)]
+        enum TestEnum {
+            A,
+            B,
+        }
+
+        let schema = Schema::parse_str(
+            r#"
+            [
+                {
+                    "type": "record",
+                    "name": "A",
+                    "fields": []
+                },
+                {
+                    "type": "enum",
+                    "name": "TestEnum",
+                    "symbols": ["A", "B"]
+                }
+            ]
+            "#,
+        )
+        .unwrap();
+        let names = HashMap::new();
+        // This works because the value index does not contain a null
+        assert_serialize(TestEnum::B, &schema, &names, &[2, 2]);
+        // This doesn't work because the value index contains a null, so the 
serializer uses that and
+        // writes [0, 0]
+        assert_serialize(TestEnum::A, &schema, &names, &[2, 0]);
+    }
+
+    #[test]
+    fn avro_rs_529_enum_with_union_schema_with_two_enums() {
+        #[derive(Debug, Serialize)]
+        enum TestEnum {
+            A,
+            B,
+        }
+
+        #[derive(Debug, Serialize)]
+        enum OtherEnum {
+            A,
+            B,
+        }
+
+        let schema = Schema::parse_str(
+            r#"
+            [
+                {
+                    "type": "enum",
+                    "name": "TestEnum",
+                    "symbols": ["A", "B"]
+                },
+                {
+                    "type": "enum",
+                    "name": "OtherEnum",
+                    "symbols": ["A", "B"]
+                }
+            ]
+            "#,
+        )
+        .unwrap();
+        let names = HashMap::new();
+        assert_serialize(TestEnum::A, &schema, &names, &[0, 0]);
+        assert_serialize(TestEnum::B, &schema, &names, &[0, 2]);
+
+        assert_serialize(OtherEnum::A, &schema, &names, &[2, 0]);
+        assert_serialize(OtherEnum::B, &schema, &names, &[2, 2]);
+    }
+
+    #[test]
+    fn avro_rs_529_enum_with_union_schema_with_reference() {
+        #[derive(Debug, Serialize)]
+        enum TestEnum {
+            A,
+            B,
+        }
+
+        let (schema, mut schemata) = Schema::parse_str_with_list(
+            r#"
+            [
+                {
+                    "type": "TestEnum"
+                }
+            ]
+            "#,
+            [r#"
+            {
+                "type": "enum",
+                "name": "TestEnum",
+                "symbols": ["A", "B"]
+            }
+        "#],
+        )
+        .unwrap();
+        let mut names = HashMap::new();
+        let reference = schemata.pop().unwrap();
+        names.insert(reference.name().cloned().unwrap(), &reference);
+
+        assert_serialize(TestEnum::A, &schema, &names, &[0, 0]);
+        assert_serialize(TestEnum::B, &schema, &names, &[0, 2]);
+    }
+
+    #[test]
+    fn avro_rs_529_wrong_name() -> TestResult {
+        #[derive(Debug, Serialize)]
+        enum WrongName {
+            A,
+            B,
+        }
+
+        let schema = Schema::parse_str(
+            r#"
+            [
+                "int",
+                {
+                    "type": "enum",
+                    "name": "TestEnum",
+                    "symbols": ["A", "B"]
+                }
+            ]
+            "#,
+        )?;
+        let names = HashMap::new();
+        assert_serialize_err(
+            WrongName::A,
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `unit variant` using 
Schema::Union(UnionSchema { schemas: [Int, Enum(EnumSchema { name: Name { name: 
"TestEnum", .. }, symbols: ["A", "B"], .. })] }): Expected Schema::Enum(name: 
"WrongName", symbols: [.., "A", ..]) in variants"#,
+        );
+        assert_serialize_err(
+            WrongName::B,
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `unit variant` using 
Schema::Union(UnionSchema { schemas: [Int, Enum(EnumSchema { name: Name { name: 
"TestEnum", .. }, symbols: ["A", "B"], .. })] }): Expected Schema::Enum(name: 
"WrongName", symbols: [.., "B", ..]) in variants"#,
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_529_unknown_variant() -> TestResult {
+        #[derive(Debug, Serialize)]
+        enum TestEnum {
+            A,
+            B,
+            C,
+        }
+
+        let schema = Schema::parse_str(
+            r#"
+            [
+                "int",
+                {
+                    "type": "enum",
+                    "name": "TestEnum",
+                    "symbols": ["A", "B"]
+                }
+            ]
+            "#,
+        )?;
+        let names = HashMap::new();
+        assert_serialize(TestEnum::A, &schema, &names, &[2, 0]);
+        assert_serialize(TestEnum::B, &schema, &names, &[2, 2]);
+        assert_serialize_err(
+            TestEnum::C,
+            &schema,
+            &names,
+            r#"Failed to serialize value of type `unit variant` using 
Schema::Union(UnionSchema { schemas: [Int, Enum(EnumSchema { name: Name { name: 
"TestEnum", .. }, symbols: ["A", "B"], .. })] }): No variant named "C" in enum 
schema"#,
+        );
+
+        Ok(())
+    }
 }
diff --git a/avro/src/serde/ser_schema/union.rs 
b/avro/src/serde/ser_schema/union.rs
index 4c02011..37c46b5 100644
--- a/avro/src/serde/ser_schema/union.rs
+++ b/avro/src/serde/ser_schema/union.rs
@@ -356,11 +356,39 @@ impl<'s, 'w, W: Write, S: Borrow<Schema>> Serializer for 
UnionSerializer<'s, 'w,
 
     fn serialize_unit_variant(
         self,
-        _: &'static str,
-        _: u32,
-        _: &'static str,
+        name: &'static str,
+        variant_index: u32,
+        variant: &'static str,
     ) -> Result<Self::Ok, Self::Error> {
-        Err(self.error("unit variant", "Nested unions are not supported"))
+        if let Some((index, Schema::Enum(enum_schema))) =
+            self.union.find_named_schema(name, self.config.names)?
+        {
+            let variant_index = if enum_schema
+                .symbols
+                .get(variant_index as usize)
+                .map(String::as_str)
+                == Some(variant)
+            {
+                variant_index as i32
+            } else if let Some(symbol) = 
enum_schema.symbols.iter().position(|s| s == variant) {
+                symbol as i32
+            } else {
+                return Err(self.error(
+                    "unit variant",
+                    format!(r#"No variant named "{variant}" in enum schema"#),
+                ));
+            };
+            let mut bytes_written = zig_i32(index as i32, &mut *self.writer)?;
+            bytes_written += zig_i32(variant_index, &mut *self.writer)?;
+            Ok(bytes_written)
+        } else {
+            Err(self.error(
+                "unit variant",
+                format!(
+                    r#"Expected Schema::Enum(name: "{name}", symbols: [.., 
"{variant}", ..]) in variants"#
+                ),
+            ))
+        }
     }
 
     fn serialize_newtype_struct<T>(

Reply via email to