This is an automated email from the ASF dual-hosted git repository. Kriskras99 pushed a commit to branch fix/enum_in_union in repository https://gitbox.apache.org/repos/asf/avro-rs.git
commit 9382632cdb43081513a6a7e4245b361eeed80265 Author: Kriskras99 <[email protected]> AuthorDate: Wed Jul 15 12:52:45 2026 +0200 fix: Allow serializing an enum inside a union --- avro/src/serde/ser_schema/mod.rs | 37 ++++++++++++++++++++++++++++++++++--- avro/src/serde/ser_schema/union.rs | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 7 deletions(-) diff --git a/avro/src/serde/ser_schema/mod.rs b/avro/src/serde/ser_schema/mod.rs index 5aba68b..a33702e 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,33 @@ mod tests { Ok(()) } + + #[test] + fn avro_rs_529_enum_with_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(()) + } } diff --git a/avro/src/serde/ser_schema/union.rs b/avro/src/serde/ser_schema/union.rs index 4c02011..fde485c 100644 --- a/avro/src/serde/ser_schema/union.rs +++ b/avro/src/serde/ser_schema/union.rs @@ -356,11 +356,37 @@ 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)? + && enum_schema + .symbols + .get(variant_index as usize) + .map(String::as_str) + == Some(variant) + { + // Fast path for if the name and variant match (no #[serde(skip)] or other trickery) + let mut bytes_written = zig_i32(index as i32, &mut *self.writer)?; + bytes_written += zig_i32(variant_index as i32, &mut *self.writer)?; + Ok(bytes_written) + } else { + for (index, schema) in self.union.variants().iter().enumerate() { + if let Schema::Enum(enum_schema) = schema + && let Some(symbol) = enum_schema.symbols.iter().position(|s| s == variant) + { + let mut bytes_written = zig_i32(index as i32, &mut *self.writer)?; + bytes_written += zig_i32(symbol as i32, &mut *self.writer)?; + return Ok(bytes_written); + } + } + Err(self.error( + "unit variant", + format!("Expected Schema::Enum(symbols: [.., {variant}, ..]) in variants"), + )) + } } fn serialize_newtype_struct<T>(
