You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by kr...@apache.org on 2026/03/11 14:01:42 UTC
(avro-rs) 07/08: finished?
This is an automated email from the ASF dual-hosted git repository.
kriskras99 pushed a commit to branch feat/enums
in repository https://gitbox.apache.org/repos/asf/avro-rs.git
commit ecdd9aabe6908dd5bb3e28ca9474c40520a54634
Author: default <ad...@kriskras99.nl>
AuthorDate: Mon Mar 9 13:13:24 2026 +0000
finished?
---
avro/src/schema/mod.rs | 1 -
avro/src/serde/deser_schema/enums/union.rs | 1 -
avro/src/serde/deser_schema/mod.rs | 1 -
avro/src/serde/deser_schema/record.rs | 3 -
avro/src/serde/ser_schema/mod.rs | 47 +--
avro/tests/avro-rs-219.rs | 1 -
avro_derive/src/attributes/mod.rs | 68 ++-
avro_derive/src/enums/bare_union.rs | 44 +-
avro_derive/src/enums/mod.rs | 23 +-
avro_derive/src/enums/plain.rs | 12 +-
avro_derive/src/enums/record_internally_tagged.rs | 35 +-
avro_derive/src/enums/record_tag_content.rs | 25 +-
avro_derive/src/enums/union_of_records.rs | 16 +-
avro_derive/src/fields.rs | 129 +++---
avro_derive/src/lib.rs | 122 +++---
avro_derive/src/tuple.rs | 20 +-
avro_derive/src/utils.rs | 91 ++++
avro_derive/tests/derive.rs | 2 +
avro_derive/tests/enum.rs | 465 ++++++++++++++++++++-
.../avro_3709_record_field_attributes.expanded.rs | 4 +-
.../expanded/avro_rs_xxx_bare_union.expanded.rs | 135 ------
.../tests/expanded/avro_rs_xxx_bare_union.rs | 15 +-
.../avro_rs_xxx_internally_tagged.expanded.rs | 207 ++++-----
.../expanded/avro_rs_xxx_internally_tagged.rs | 27 +-
.../expanded/avro_rs_xxx_tag_content.expanded.rs | 340 +++++++++------
.../tests/expanded/avro_rs_xxx_tag_content.rs | 27 +-
.../avro_rs_xxx_union_of_records.expanded.rs | 369 +++++++++-------
.../tests/expanded/avro_rs_xxx_union_of_records.rs | 26 +-
avro_derive/tests/expanded/mod.rs | 4 +-
avro_derive/tests/ui/avro_rs_401_tuple.stderr | 5 -
...rs => avro_rs_xxx_bare_union_struct_variant.rs} | 5 +-
.../avro_rs_xxx_bare_union_struct_variant.stderr | 5 +
...ple.rs => avro_rs_xxx_untagged_newtype_docs.rs} | 8 +-
.../ui/avro_rs_xxx_untagged_newtype_docs.stderr | 6 +
34 files changed, 1398 insertions(+), 891 deletions(-)
diff --git a/avro/src/schema/mod.rs b/avro/src/schema/mod.rs
index 4bffaca..b9a75f5 100644
--- a/avro/src/schema/mod.rs
+++ b/avro/src/schema/mod.rs
@@ -4866,7 +4866,6 @@ mod tests {
_ => panic!("Expected ArraySchema. got: {}", &schema1),
}
let canonical_form1 = schema1.canonical_form();
- println!("Canonical Form 1: {}", &canonical_form1);
let schema2 = Schema::parse_str(&canonical_form1)?;
let canonical_form2 = schema2.canonical_form();
diff --git a/avro/src/serde/deser_schema/enums/union.rs b/avro/src/serde/deser_schema/enums/union.rs
index 8712128..aa1c13b 100644
--- a/avro/src/serde/deser_schema/enums/union.rs
+++ b/avro/src/serde/deser_schema/enums/union.rs
@@ -100,7 +100,6 @@ impl<'de, 's, 'r, R: Read, S: Borrow<Schema>> VariantAccess<'de>
{
if let Schema::Record(record) = self.schema
&& record.fields.len() == 1
- && record.fields[0].name == "field_0"
&& record
.attributes
.get("org.apache.avro.rust.union_of_records")
diff --git a/avro/src/serde/deser_schema/mod.rs b/avro/src/serde/deser_schema/mod.rs
index 8fb8bb5..cf9d0d7 100644
--- a/avro/src/serde/deser_schema/mod.rs
+++ b/avro/src/serde/deser_schema/mod.rs
@@ -576,7 +576,6 @@ impl<'de, 's, 'r, R: Read, S: Borrow<Schema>> Deserializer<'de>
where
V: serde::de::Visitor<'de>,
{
- println!("deserialize_tuple(len: {len}): {self:?}");
match self.schema {
Schema::Null if len == 0 => visitor.visit_seq(UnitTupleDeserializer),
schema if len == 1 => {
diff --git a/avro/src/serde/deser_schema/record.rs b/avro/src/serde/deser_schema/record.rs
index f2771f3..b2114dc 100644
--- a/avro/src/serde/deser_schema/record.rs
+++ b/avro/src/serde/deser_schema/record.rs
@@ -65,7 +65,6 @@ impl<'de, 's, 'r, R: Read, S: Borrow<Schema>> MapAccess<'de> for RecordDeseriali
where
K: serde::de::DeserializeSeed<'de>,
{
- println!("next_key_seed: {self:?}");
let State::Key(index) = self.current_field else {
panic!("`next_key_seed` and `next_value_seed` where called in the wrong error")
};
@@ -85,14 +84,12 @@ impl<'de, 's, 'r, R: Read, S: Borrow<Schema>> MapAccess<'de> for RecordDeseriali
where
V: serde::de::DeserializeSeed<'de>,
{
- println!("next_value_seed: {self:?}");
let State::Value(index) = self.current_field else {
panic!("`next_key_seed` and `next_value_seed` where called in the wrong error")
};
let schema = &self.schema.fields[index].schema;
let v = if let Schema::Record(record) = schema
&& record.fields.len() == 1
- && record.fields[0].name == "field_0"
&& record
.attributes
.get("org.apache.avro.rust.union_of_records")
diff --git a/avro/src/serde/ser_schema/mod.rs b/avro/src/serde/ser_schema/mod.rs
index 870aa14..0387097 100644
--- a/avro/src/serde/ser_schema/mod.rs
+++ b/avro/src/serde/ser_schema/mod.rs
@@ -217,7 +217,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
type SerializeStructVariant = RecordSerializer<'s, 'w, W>;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
- println!("serialize_bool({v}): {self:?}");
match self.schema {
Schema::Boolean => {
self.writer
@@ -233,27 +232,22 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
- println!("serialize_i8({v}): {self:?}");
self.serialize_int("i8", i32::from(v))
}
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
- println!("serialize_i16({v}): {self:?}");
self.serialize_int("i16", i32::from(v))
}
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
- println!("serialize_i32({v}): {self:?}");
self.serialize_int("i32", v)
}
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
- println!("serialize_i64({v}): {self:?}");
self.serialize_long("i64", v)
}
fn serialize_i128(mut self, v: i128) -> Result<Self::Ok, Self::Error> {
- println!("serialize_i128({v}): {self:?}");
match self.schema {
Schema::Fixed(fixed) if fixed.name.name() == "i128" && fixed.size == 16 => {
let bytes = v.to_le_bytes();
@@ -267,22 +261,18 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
- println!("serialize_u8({v}): {self:?}");
self.serialize_int("u8", i32::from(v))
}
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
- println!("serialize_u16({v}): {self:?}");
self.serialize_int("u16", i32::from(v))
}
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
- println!("serialize_u32({v}): {self:?}");
self.serialize_long("u32", i64::from(v))
}
fn serialize_u64(mut self, v: u64) -> Result<Self::Ok, Self::Error> {
- println!("serialize_u64({v}): {self:?}");
match self.schema {
Schema::Fixed(fixed) if fixed.name.name() == "u64" && fixed.size == 8 => {
let bytes = v.to_le_bytes();
@@ -296,7 +286,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_u128(mut self, v: u128) -> Result<Self::Ok, Self::Error> {
- println!("serialize_u128({v}): {self:?}");
match self.schema {
Schema::Fixed(fixed) if fixed.name.name() == "u128" && fixed.size == 16 => {
let bytes = v.to_le_bytes();
@@ -310,7 +299,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_f32(mut self, v: f32) -> Result<Self::Ok, Self::Error> {
- println!("serialize_f32({v}): {self:?}");
match self.schema {
Schema::Float => {
let bytes = v.to_le_bytes();
@@ -324,7 +312,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_f64(mut self, v: f64) -> Result<Self::Ok, Self::Error> {
- println!("serialize_f64({v}): {self:?}");
match self.schema {
Schema::Double => {
let bytes = v.to_le_bytes();
@@ -338,7 +325,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_char(mut self, v: char) -> Result<Self::Ok, Self::Error> {
- println!("serialize_char({v}): {self:?}");
match self.schema {
Schema::String => {
let bytes = v.to_string().into_bytes();
@@ -352,7 +338,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_str(mut self, v: &str) -> Result<Self::Ok, Self::Error> {
- println!("serialize_str({v}): {self:?}");
match self.schema {
Schema::String | Schema::Uuid(UuidSchema::String) => {
self.write_bytes_with_len(v.as_bytes())
@@ -365,7 +350,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_bytes(mut self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
- println!("serialize_bytes({v:?}): {self:?}");
match (SER_BYTES_TYPE.get(), self.schema) {
(BytesType::Unset | BytesType::Bytes, Schema::Bytes | Schema::BigDecimal | Schema::Decimal(DecimalSchema { inner: InnerDecimalSchema::Bytes, .. })) => {
self.write_bytes_with_len(v)
@@ -385,7 +369,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
- println!("serialize_none: {self:?}");
if let Schema::Union(union) = self.schema
&& union.variants().len() == 2
&& let Some(index) = union.index().get(&SchemaKind::Null).copied()
@@ -403,7 +386,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
where
T: ?Sized + Serialize,
{
- println!("serialize_some: {self:?}");
if let Schema::Union(union) = self.schema
&& union.variants().len() == 2
&& let Some(index) = union.index().get(&SchemaKind::Null).copied()
@@ -423,7 +405,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
- println!("serialize_unit: {self:?}");
match self.schema {
Schema::Null => Ok(0),
Schema::Union(union) => {
@@ -434,7 +415,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
- println!("serialize_unit_struct(name: {name}): {self:?}");
match self.schema {
Schema::Record(record) if record.name.name() == name && record.fields.is_empty() => {
Ok(0)
@@ -454,9 +434,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
variant_index: u32,
variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
- println!(
- "serialize_unit_variant(name: {name}, index: {variant_index}, variant: {variant}): {self:?}"
- );
match self.schema {
Schema::Enum(enum_schema) => {
if name.as_ptr() == SERIALIZING_SCHEMA_DEFAULT.as_ptr() {
@@ -504,7 +481,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
where
T: ?Sized + Serialize,
{
- println!("serialize_newtype_struct(name: {name}): {self:?}");
match self.schema {
Schema::Record(record) if record.name.name() == name && record.fields.len() == 1 => {
let schema = &record.fields[0].schema;
@@ -530,15 +506,11 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
where
T: ?Sized + Serialize,
{
- println!(
- "serialize_newtype_variant(name: {_name}, index: {variant_index}, variant: {variant}): {self:?}"
- );
match self.schema {
Schema::Union(union) => {
if let Some(Schema::Record(record)) = union.variants().get(variant_index as usize)
&& record.name.name() == variant
&& record.fields.len() == 1
- && record.fields[0].name == "field_0"
&& record
.attributes
.get("org.apache.avro.rust.union_of_records")
@@ -562,7 +534,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
- println!("serialize_seq(len: {len:?}): {self:?}");
match self.schema {
Schema::Array(array) => {
ArraySerializer::new(self.writer, array, self.config, len, None)
@@ -575,7 +546,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
- println!("serialize_tuple(len: {len}): {self:?}");
match self.schema {
Schema::Union(union) => {
UnionAwareSerializer::new(self.writer, union, self.config).serialize_tuple(len)
@@ -601,7 +571,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
- println!("serialize_tuple_struct(name: {name}, len: {len}): {self:?}");
match self.schema {
Schema::Record(record) if record.name.name() == name && record.fields.len() == len => {
Ok(ManyTupleSerializer::new(
@@ -627,9 +596,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
- println!(
- "serialize_tuple_variant(name: {_name}, index: {variant_index}, variant: {variant}, len: {len}): {self:?}"
- );
match self.schema {
Schema::Union(union) => {
if let Some(Schema::Record(record)) = union.variants().get(variant_index as usize)
@@ -673,7 +639,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
}
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
- println!("serialize_map(len: {len:?}): {self:?}");
match self.schema {
Schema::Map(map) => Ok(MapOrRecordSerializer::Map(MapSerializer::new(
self.writer,
@@ -703,10 +668,8 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
- println!("serialize_struct(name: {name}, len: {len}): {self:?}");
match self.schema {
- // For unit variants with tag,content `len` will be 1 but we expect 2.
- Schema::Record(record) if record.name.name() == name => Ok(RecordSerializer::new(
+ Schema::Record(record) => Ok(RecordSerializer::new(
self.writer,
record,
self.config,
@@ -714,10 +677,7 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
)),
Schema::Union(union) => UnionAwareSerializer::new(self.writer, union, self.config)
.serialize_struct(name, len),
- _ => Err(self.error(
- "struct",
- format!("Expected Schema::Record(name: {name}, fields.len() == {len})"),
- )),
+ _ => Err(self.error("struct", "Expected Schema::Record")),
}
}
@@ -728,9 +688,6 @@ impl<'s, 'w, W: Write> Serializer for SchemaAwareSerializer<'s, 'w, W> {
variant: &'static str,
len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
- println!(
- "serialize_struct_variant(name: {_name}, index: {variant_index}, variant: {variant}, len: {len}): {self:?}"
- );
match self.schema {
Schema::Union(union) => {
if let Some(Schema::Record(record)) = union.variants().get(variant_index as usize)
diff --git a/avro/tests/avro-rs-219.rs b/avro/tests/avro-rs-219.rs
index 34d503f..5e96a3e 100644
--- a/avro/tests/avro-rs-219.rs
+++ b/avro/tests/avro-rs-219.rs
@@ -38,6 +38,5 @@ fn avro_rs_219_failing_deserialization_due_to_bigdecimal_dependency() {
}"#;
let result = serde_json::from_str::<S3>(test);
- println!("result : {result:#?}");
result.unwrap();
}
diff --git a/avro_derive/src/attributes/mod.rs b/avro_derive/src/attributes/mod.rs
index ab66911..396e6fa 100644
--- a/avro_derive/src/attributes/mod.rs
+++ b/avro_derive/src/attributes/mod.rs
@@ -15,11 +15,11 @@
// specific language governing permissions and limitations
// under the License.
-use crate::case::RenameRule;
+use crate::{TypedTokenStream, case::RenameRule};
use darling::{FromAttributes, FromMeta};
-use proc_macro2::{Span, TokenStream};
+use proc_macro2::Span;
use quote::quote;
-use syn::{AttrStyle, Attribute, Expr, Ident, Path, Type, spanned::Spanned};
+use syn::{AttrStyle, Attribute, Expr, Ident, Path, spanned::Spanned};
mod avro;
mod serde;
@@ -39,7 +39,6 @@ pub enum EnumRepr {
RecordInternallyTagged { tag: String },
}
-#[derive(Default)]
pub struct NamedTypeOptions {
pub name: String,
pub doc: Option<String>,
@@ -47,7 +46,7 @@ pub struct NamedTypeOptions {
pub rename_all: RenameRule,
pub rename_all_fields: RenameRule,
pub transparent: bool,
- pub default: TokenStream,
+ pub default: TypedTokenStream<Option<serde_json::Value>>,
pub repr: Option<EnumRepr>,
pub with: Option<Path>,
}
@@ -230,18 +229,18 @@ impl NamedTypeOptions {
};
let default = match avro.default {
- None => quote! { ::std::option::Option::None },
+ None => TypedTokenStream::none(),
Some(default_value) => {
if let Err(err) = serde_json::from_str::<serde_json::Value>(&default_value[..]) {
errors.push(syn::Error::new(
ident.span(),
format!("Invalid Avro `default` JSON: \n{err}"),
));
- quote! { ::std::option::Option::None }
+ TypedTokenStream::none()
} else {
- quote! {
+ TypedTokenStream::<Option<serde_json::Value>>::new(quote! {
::std::option::Option::Some(serde_json::from_str(#default_value).expect("Unreachable! This was checked at compile time"))
- }
+ })
}
}
};
@@ -377,6 +376,7 @@ impl With {
}
}
}
+
/// How to get the default value for a value.
#[derive(Debug, PartialEq, Default)]
pub enum FieldDefault {
@@ -500,6 +500,56 @@ impl FieldOptions {
with,
})
}
+
+ /// Parse and check the attributes for the anonymous field of newtype variants.
+ ///
+ /// This function is not used by `union_of_records`.
+ pub fn new_for_newtype(attributes: &[Attribute], span: Span) -> Result<Self, Vec<syn::Error>> {
+ let options = Self::new(attributes, span)?;
+ let mut errors = Vec::new();
+ if options.doc.is_some() {
+ errors.push(syn::Error::new(
+ span,
+ r#"AvroSchema: `#[avro(doc = "..")]` only works on newtype variants when the enum uses `#[avro(repr = "union_of_records")`"#
+ ));
+ }
+ if matches!(options.default, FieldDefault::Value(_)) {
+ errors.push(syn::Error::new(
+ span,
+ r#"AvroSchema: `#[avro(default = "..")]` only works on newtype variants when the enum uses `#[avro(repr = "union_of_records")`"#
+ ));
+ }
+ if !options.alias.is_empty() {
+ errors.push(syn::Error::new(
+ span,
+ r#"AvroSchema: `#[avro(alias = "..")]` only works on newtype variants when the enum uses `#[avro(repr = "union_of_records")`"#
+ ));
+ }
+ if options.rename.is_some() {
+ errors.push(syn::Error::new(
+ span,
+ r#"AvroSchema: `#[avro(alias = "..")]` only works on newtype variants when the enum uses `#[avro(repr = "union_of_records")`"#
+ ));
+ }
+ if options.skip {
+ errors.push(syn::Error::new(
+ span,
+ r#"AvroSchema: `#[avro(skip)]` is not supported for newtype variants"#,
+ ));
+ }
+ if options.flatten {
+ errors.push(syn::Error::new(
+ span,
+ r#"AvroSchema: `#[avro(flatten)]` is not supported for newtype variants"#,
+ ));
+ }
+
+ if !errors.is_empty() {
+ return Err(errors);
+ }
+
+ Ok(options)
+ }
}
fn extract_rustdoc(attributes: &[Attribute]) -> Option<String> {
diff --git a/avro_derive/src/enums/bare_union.rs b/avro_derive/src/enums/bare_union.rs
index 7752b81..6439c81 100644
--- a/avro_derive/src/enums/bare_union.rs
+++ b/avro_derive/src/enums/bare_union.rs
@@ -1,7 +1,8 @@
-use crate::attributes::{FieldOptions, NamedTypeOptions, VariantOptions};
+use crate::attributes::{NamedTypeOptions, VariantOptions};
use crate::tuple::tuple_struct_variant_to_record_schema;
-use crate::{fields, named_to_record_fields};
-use proc_macro2::{Span, TokenStream};
+use crate::utils::{Schema, TypedTokenStream};
+use crate::{FieldOptions, fields};
+use proc_macro2::Span;
use quote::quote;
use syn::spanned::Spanned;
use syn::{DataEnum, Fields};
@@ -10,7 +11,7 @@ pub fn get_data_enum_schema_def(
container_attrs: &NamedTypeOptions,
data_enum: DataEnum,
ident_span: Span,
-) -> Result<TokenStream, Vec<syn::Error>> {
+) -> Result<TypedTokenStream<Schema>, Vec<syn::Error>> {
let mut variant_expr = Vec::new();
let mut have_null = false;
for variant in data_enum.variants {
@@ -22,22 +23,12 @@ pub fn get_data_enum_schema_def(
});
match variant.fields {
Fields::Named(named) => {
- let fields = named_to_record_fields(
- named,
- variant_attrs
- .rename_all
- .or(container_attrs.rename_all_fields),
- )?;
-
- let schema_expr = quote! {
- ::apache_avro::schema::Schema::Record(
- ::apache_avro::schema::RecordSchema::builder()
- .name(::apache_avro::schema::Name::new_with_enclosing_namespace(#name, enclosing_namespace).expect(&format!("Unable to parse variant record name for schema {}", #name)[..]))
- .fields(#fields)
- .build()
- )
- };
- variant_expr.push(schema_expr);
+ // TODO: Support struct variants for untagged enums. All fields of all variants need to be in one record with
+ // defaults for every field and the record named as the enum.
+ return Err(vec![syn::Error::new(
+ named.span(),
+ "AvroSchema: Struct variants are not supported for untagged structs",
+ )]);
}
Fields::Unnamed(unnamed) => {
if unnamed.unnamed.is_empty() {
@@ -47,8 +38,9 @@ pub fn get_data_enum_schema_def(
)]);
} else if unnamed.unnamed.len() == 1 {
let only_one = unnamed.unnamed.iter().next().expect("There is one");
- let field_attrs = FieldOptions::new(&only_one.attrs, only_one.span())?;
- let schema_expr = fields::to_schema(&only_one, field_attrs.with)?;
+ let field_attrs =
+ FieldOptions::new_for_newtype(&only_one.attrs, only_one.span())?;
+ let schema_expr = fields::to_schema(only_one, field_attrs.with)?;
variant_expr.push(schema_expr);
} else if unnamed.unnamed.len() > 1 {
let schema_expr = tuple_struct_variant_to_record_schema(unnamed, &name, &[])?;
@@ -63,16 +55,18 @@ pub fn get_data_enum_schema_def(
"More than one variant maps to Schema::Null, this is not supported for bare unions",
)]);
}
- variant_expr.push(quote! { ::apache_avro::schema::Schema::Null });
+ variant_expr.push(TypedTokenStream::<Schema>::new(
+ quote! { ::apache_avro::schema::Schema::Null },
+ ));
have_null = true;
}
}
}
- Ok(quote! {
+ Ok(TypedTokenStream::new(quote! {{
let mut builder = ::apache_avro::schema::UnionSchema::builder();
#(builder.variant(#variant_expr).expect("Duplicate Schema found");)*
::apache_avro::schema::Schema::Union(builder.build())
- })
+ }}))
}
diff --git a/avro_derive/src/enums/mod.rs b/avro_derive/src/enums/mod.rs
index e9c2418..d83033d 100644
--- a/avro_derive/src/enums/mod.rs
+++ b/avro_derive/src/enums/mod.rs
@@ -21,8 +21,12 @@ mod record_internally_tagged;
mod record_tag_content;
mod union_of_records;
-use crate::attributes::{EnumRepr, NamedTypeOptions};
-use proc_macro2::{Ident, Span, TokenStream};
+use crate::{
+ attributes::{EnumRepr, NamedTypeOptions},
+ utils::{Schema, TypedTokenStream},
+};
+use proc_macro2::{Ident, Span};
+use quote::quote;
use syn::{Attribute, DataEnum, Fields, Meta};
/// Generate a schema definition for a enum.
@@ -30,7 +34,7 @@ pub fn get_data_enum_schema_def(
container_attrs: &NamedTypeOptions,
data_enum: DataEnum,
ident_span: Span,
-) -> Result<TokenStream, Vec<syn::Error>> {
+) -> Result<TypedTokenStream<Schema>, Vec<syn::Error>> {
match &container_attrs.repr {
None => {
if data_enum.variants.iter().all(|v| Fields::Unit == v.fields) {
@@ -58,15 +62,22 @@ pub fn get_data_enum_schema_def(
fn default_enum_variant(
data_enum: &DataEnum,
error_span: Span,
-) -> Result<Option<String>, Vec<syn::Error>> {
+) -> Result<TypedTokenStream<Option<String>>, Vec<syn::Error>> {
match data_enum
.variants
.iter()
.filter(|v| v.attrs.iter().any(is_default_attr))
.collect::<Vec<_>>()
{
- variants if variants.is_empty() => Ok(None),
- single if single.len() == 1 => Ok(Some(single[0].ident.to_string())),
+ variants if variants.is_empty() => Ok(TypedTokenStream::new(
+ quote! { ::std::option::Option::None },
+ )),
+ single if single.len() == 1 => {
+ let ident = single[0].ident.to_string();
+ Ok(TypedTokenStream::new(
+ quote! { ::std::option::Option::Some(#ident.to_string()) },
+ ))
+ }
multiple => Err(vec![syn::Error::new(
error_span,
format!(
diff --git a/avro_derive/src/enums/plain.rs b/avro_derive/src/enums/plain.rs
index 648ebd4..29bbe90 100644
--- a/avro_derive/src/enums/plain.rs
+++ b/avro_derive/src/enums/plain.rs
@@ -18,8 +18,9 @@
use crate::attributes::{NamedTypeOptions, VariantOptions};
use crate::case::RenameRule;
use crate::enums::default_enum_variant;
+use crate::utils::{Schema, TypedTokenStream};
use crate::{aliases, preserve_optional};
-use proc_macro2::{Span, TokenStream};
+use proc_macro2::Span;
use quote::quote;
use syn::spanned::Spanned;
use syn::{DataEnum, Fields};
@@ -28,12 +29,11 @@ pub fn schema_def(
container_attrs: &NamedTypeOptions,
data_enum: DataEnum,
ident_span: Span,
-) -> Result<TokenStream, Vec<syn::Error>> {
+) -> Result<TypedTokenStream<Schema>, Vec<syn::Error>> {
let doc = preserve_optional(container_attrs.doc.as_ref());
let enum_aliases = aliases(&container_attrs.aliases);
if data_enum.variants.iter().all(|v| Fields::Unit == v.fields) {
- let default_value = default_enum_variant(&data_enum, ident_span)?;
- let default = preserve_optional(default_value);
+ let default = default_enum_variant(&data_enum, ident_span)?;
let mut symbols = Vec::new();
for variant in &data_enum.variants {
let field_attrs = VariantOptions::new(&variant.attrs, variant.span())?;
@@ -46,7 +46,7 @@ pub fn schema_def(
};
symbols.push(name);
}
- Ok(quote! {
+ Ok(TypedTokenStream::new(quote! {
::apache_avro::schema::Schema::Enum(::apache_avro::schema::EnumSchema {
name,
aliases: #enum_aliases,
@@ -55,7 +55,7 @@ pub fn schema_def(
default: #default,
attributes: ::std::collections::BTreeMap::new(),
})
- })
+ }))
} else {
Err(vec![syn::Error::new(
ident_span,
diff --git a/avro_derive/src/enums/record_internally_tagged.rs b/avro_derive/src/enums/record_internally_tagged.rs
index e9173cd..4387e42 100644
--- a/avro_derive/src/enums/record_internally_tagged.rs
+++ b/avro_derive/src/enums/record_internally_tagged.rs
@@ -1,6 +1,6 @@
-use crate::attributes::{FieldOptions, NamedTypeOptions, VariantOptions};
-use crate::{aliases, fields, named_to_record_fields, preserve_optional};
-use proc_macro2::TokenStream;
+use crate::attributes::{NamedTypeOptions, VariantOptions};
+use crate::utils::{Schema, TypedTokenStream};
+use crate::{FieldOptions, aliases, fields, named_to_record_fields, preserve_optional};
use quote::quote;
use syn::spanned::Spanned;
use syn::{DataEnum, Fields};
@@ -9,18 +9,12 @@ pub fn get_data_enum_schema_def(
container_attrs: &NamedTypeOptions,
data_enum: DataEnum,
tag: &str,
-) -> Result<TokenStream, Vec<syn::Error>> {
+) -> Result<TypedTokenStream<Schema>, Vec<syn::Error>> {
let doc = preserve_optional(container_attrs.doc.as_ref());
let enum_aliases = aliases(&container_attrs.aliases);
- let mut symbols = Vec::new();
let mut field_additions = Vec::new();
for variant in data_enum.variants {
let variant_attrs = VariantOptions::new(&variant.attrs, variant.span())?;
- let name = variant_attrs.rename.unwrap_or_else(|| {
- container_attrs
- .rename_all
- .apply_to_variant(&variant.ident.to_string())
- });
match variant.fields {
Fields::Named(named) => {
let named_fields = named_to_record_fields(
@@ -36,10 +30,15 @@ pub fn get_data_enum_schema_def(
Fields::Unnamed(unnamed) => {
if unnamed.unnamed.len() == 1 {
let only_one = unnamed.unnamed.iter().next().expect("There is one");
- let field_attrs = FieldOptions::new(&only_one.attrs, only_one.span())?;
- let schema_expr = fields::to_schema(&only_one, field_attrs.with)?;
+ let field_attrs =
+ FieldOptions::new_for_newtype(&only_one.attrs, only_one.span())?;
+ let schema_expr = fields::to_record_fields(only_one, field_attrs.with)?;
field_additions.push(quote! {
- fields.push(#schema_expr);
+ if let Some(record_fields) = #schema_expr {
+ fields.extend(record_fields);
+ } else {
+ panic!("Newtype variant type must implement `get_record_fields` for internally tagged enums");
+ }
});
} else if unnamed.unnamed.len() > 1 {
return Err(vec![syn::Error::new(
@@ -50,18 +49,14 @@ pub fn get_data_enum_schema_def(
}
Fields::Unit => {}
}
- symbols.push(name);
}
- Ok(quote! {
+ Ok(TypedTokenStream::new(quote! {{
let mut builder = ::apache_avro::schema::UnionSchema::builder();
- let tag_name = ::apache_avro::schema::Name::new_with_enclosing_namespace(#tag, enclosing_namespace).expect(&format!("Unable to parse name for schema tag {}", #tag)[..]);
- let tag_schema = ::apache_avro::schema::Schema::r#enum(tag_name, vec![#(#symbols.to_owned()),*]).build();
-
let mut fields = ::std::vec::Vec::new();
fields.push(::apache_avro::schema::RecordField::builder()
.name(#tag)
- .schema(tag_schema)
+ .schema(::apache_avro::schema::Schema::String)
.build()
);
#(
@@ -74,5 +69,5 @@ pub fn get_data_enum_schema_def(
.fields(fields)
.build()
)
- })
+ }}))
}
diff --git a/avro_derive/src/enums/record_tag_content.rs b/avro_derive/src/enums/record_tag_content.rs
index a1aebf5..81ac0a7 100644
--- a/avro_derive/src/enums/record_tag_content.rs
+++ b/avro_derive/src/enums/record_tag_content.rs
@@ -1,7 +1,7 @@
use crate::attributes::{FieldOptions, NamedTypeOptions, VariantOptions};
use crate::tuple::tuple_struct_variant_to_record_schema;
+use crate::utils::{Schema, TypedTokenStream};
use crate::{aliases, fields, named_to_record_fields, preserve_optional};
-use proc_macro2::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
use syn::{DataEnum, Fields};
@@ -11,7 +11,7 @@ pub fn get_data_enum_schema_def(
data_enum: DataEnum,
tag: &str,
content: &str,
-) -> Result<TokenStream, Vec<syn::Error>> {
+) -> Result<TypedTokenStream<Schema>, Vec<syn::Error>> {
let doc = preserve_optional(container_attrs.doc.as_ref());
let enum_aliases = aliases(&container_attrs.aliases);
let mut symbols = Vec::new();
@@ -32,26 +32,27 @@ pub fn get_data_enum_schema_def(
.or(container_attrs.rename_all_fields),
)?;
- let schema_expr = quote! {
+ let schema_expr = TypedTokenStream::<Schema>::new(quote! {
::apache_avro::schema::Schema::Record(
::apache_avro::schema::RecordSchema::builder()
.name(::apache_avro::schema::Name::new_with_enclosing_namespace(#name, enclosing_namespace).expect(&format!("Unable to parse variant record name for schema {}", #name)[..]))
.fields(#fields)
.build()
)
- };
+ });
schema_definitions.push(schema_expr);
}
Fields::Unnamed(unnamed) => {
if unnamed.unnamed.is_empty() {
- let schema_expr = quote! {
+ let schema_expr = TypedTokenStream::<Schema>::new(quote! {
::apache_avro::schema::Schema::Null
- };
+ });
schema_definitions.push(schema_expr);
} else if unnamed.unnamed.len() == 1 {
let only_one = unnamed.unnamed.iter().next().expect("There is one");
- let field_attrs = FieldOptions::new(&only_one.attrs, only_one.span())?;
- let schema_expr = fields::to_schema(&only_one, field_attrs.with)?;
+ let field_attrs =
+ FieldOptions::new_for_newtype(&only_one.attrs, only_one.span())?;
+ let schema_expr = fields::to_schema(only_one, field_attrs.with)?;
schema_definitions.push(schema_expr);
} else if unnamed.unnamed.len() > 1 {
let schema_expr = tuple_struct_variant_to_record_schema(unnamed, &name, &[])?;
@@ -59,11 +60,13 @@ pub fn get_data_enum_schema_def(
schema_definitions.push(schema_expr);
}
}
- Fields::Unit => schema_definitions.push(quote! { ::apache_avro::schema::Schema::Null }),
+ Fields::Unit => schema_definitions.push(TypedTokenStream::<Schema>::new(
+ quote! { ::apache_avro::schema::Schema::Null },
+ )),
}
symbols.push(name);
}
- Ok(quote! {
+ Ok(TypedTokenStream::new(quote! {{
let mut builder = ::apache_avro::schema::UnionSchema::builder();
#(
@@ -96,5 +99,5 @@ pub fn get_data_enum_schema_def(
.fields(fields)
.build()
)
- })
+ }}))
}
diff --git a/avro_derive/src/enums/union_of_records.rs b/avro_derive/src/enums/union_of_records.rs
index c0f8902..d7cfd59 100644
--- a/avro_derive/src/enums/union_of_records.rs
+++ b/avro_derive/src/enums/union_of_records.rs
@@ -1,7 +1,7 @@
use crate::attributes::{NamedTypeOptions, VariantOptions};
use crate::named_to_record_fields;
use crate::tuple::tuple_struct_variant_to_record_schema;
-use proc_macro2::TokenStream;
+use crate::utils::{Schema, TypedTokenStream};
use quote::quote;
use syn::spanned::Spanned;
use syn::{DataEnum, Fields};
@@ -9,7 +9,7 @@ use syn::{DataEnum, Fields};
pub fn get_data_enum_schema_def(
container_attrs: &NamedTypeOptions,
data_enum: DataEnum,
-) -> Result<TokenStream, Vec<syn::Error>> {
+) -> Result<TypedTokenStream<Schema>, Vec<syn::Error>> {
let mut variant_expr = Vec::new();
for variant in data_enum.variants {
let variant_attrs = VariantOptions::new(&variant.attrs, variant.span())?;
@@ -27,14 +27,14 @@ pub fn get_data_enum_schema_def(
.or(container_attrs.rename_all_fields),
)?;
- let schema_expr = quote! {
+ let schema_expr = TypedTokenStream::<Schema>::new(quote! {
::apache_avro::schema::Schema::Record(
::apache_avro::schema::RecordSchema::builder()
.name(::apache_avro::schema::Name::new_with_enclosing_namespace(#name, enclosing_namespace).expect(&format!("Unable to parse variant record name for schema {}", #name)[..]))
.fields(#fields)
.build()
)
- };
+ });
variant_expr.push(schema_expr);
}
Fields::Unnamed(unnamed) => {
@@ -51,22 +51,22 @@ pub fn get_data_enum_schema_def(
variant_expr.push(schema_expr);
}
Fields::Unit => {
- let schema_expr = quote! {
+ let schema_expr = TypedTokenStream::<Schema>::new(quote! {
::apache_avro::schema::Schema::Record(
::apache_avro::schema::RecordSchema::builder()
.name(::apache_avro::schema::Name::new_with_enclosing_namespace(#name, enclosing_namespace).expect(&format!("Unable to parse variant record name for schema {}", #name)[..]))
.build()
)
- };
+ });
variant_expr.push(schema_expr);
}
}
}
- Ok(quote! {
+ Ok(TypedTokenStream::new(quote! {{
let mut builder = ::apache_avro::schema::UnionSchema::builder();
#(builder.variant(#variant_expr).expect("Duplicate Schema found");)*
::apache_avro::schema::Schema::Union(builder.build())
- })
+ }}))
}
diff --git a/avro_derive/src/fields.rs b/avro_derive/src/fields.rs
index af5b3aa..8e4702d 100644
--- a/avro_derive/src/fields.rs
+++ b/avro_derive/src/fields.rs
@@ -1,18 +1,19 @@
+use crate::RecordField;
use crate::attributes::{FieldDefault, With};
-use proc_macro2::TokenStream;
+use crate::utils::{Schema, TypedTokenStream};
use quote::{ToTokens, quote};
use syn::spanned::Spanned;
use syn::{Expr, ExprLit, Field, Lit, Type, TypeArray, TypeTuple};
-pub fn to_schema(field: &Field, with: With) -> Result<TokenStream, Vec<syn::Error>> {
+pub fn to_schema(field: &Field, with: With) -> Result<TypedTokenStream<Schema>, Vec<syn::Error>> {
match with {
With::Trait => Ok(type_to_schema_expr(&field.ty)?),
- With::Serde(path) => {
- Ok(quote! { #path::get_schema_in_ctxt(named_schemas, enclosing_namespace) })
- }
+ With::Serde(path) => Ok(TypedTokenStream::<Schema>::new(
+ quote! { #path::get_schema_in_ctxt(named_schemas, enclosing_namespace) },
+ )),
With::Expr(Expr::Closure(closure)) => {
if closure.inputs.is_empty() {
- Ok(quote! { (#closure)() })
+ Ok(TypedTokenStream::<Schema>::new(quote! { (#closure)() }))
} else {
Err(vec![syn::Error::new(
field.span(),
@@ -20,7 +21,9 @@ pub fn to_schema(field: &Field, with: With) -> Result<TokenStream, Vec<syn::Erro
)])
}
}
- With::Expr(Expr::Path(path)) => Ok(quote! { #path(named_schemas, enclosing_namespace) }),
+ With::Expr(Expr::Path(path)) => Ok(TypedTokenStream::<Schema>::new(
+ quote! { #path(named_schemas, enclosing_namespace) },
+ )),
With::Expr(_expr) => Err(vec![syn::Error::new(
field.span(),
"Invalid expression, expected function or closure",
@@ -37,21 +40,24 @@ pub fn to_schema(field: &Field, with: With) -> Result<TokenStream, Vec<syn::Erro
/// - `enclosing_namespace`: `Option<&str>`
/// ## Returns
/// An `Expr` that resolves to an instance of `Option<Vec<RecordField>>`.
-pub fn to_record_fields(field: &Field, with: With) -> Result<TokenStream, Vec<syn::Error>> {
+pub fn to_record_fields(
+ field: &Field,
+ with: With,
+) -> Result<TypedTokenStream<Option<Vec<RecordField>>>, Vec<syn::Error>> {
match with {
With::Trait => Ok(type_to_record_fields(&field.ty)?),
- With::Serde(path) => {
- Ok(quote! { #path::get_record_fields_in_ctxt(named_schemas, enclosing_namespace) })
- }
+ With::Serde(path) => Ok(TypedTokenStream::<Option<Vec<RecordField>>>::new(
+ quote! { #path::get_record_fields_in_ctxt(named_schemas, enclosing_namespace) },
+ )),
With::Expr(Expr::Closure(closure)) => {
if closure.inputs.is_empty() {
- Ok(quote! {
+ Ok(TypedTokenStream::<Option<Vec<RecordField>>>::new(quote! {
::apache_avro::serde::get_record_fields_in_ctxt(
named_schemas,
enclosing_namespace,
|_, _| (#closure)(),
)
- })
+ }))
} else {
Err(vec![syn::Error::new(
field.span(),
@@ -59,9 +65,11 @@ pub fn to_record_fields(field: &Field, with: With) -> Result<TokenStream, Vec<sy
)])
}
}
- With::Expr(Expr::Path(path)) => Ok(quote! {
- ::apache_avro::serde::get_record_fields_in_ctxt(named_schemas, enclosing_namespace, #path)
- }),
+ With::Expr(Expr::Path(path)) => {
+ Ok(TypedTokenStream::<Option<Vec<RecordField>>>::new(quote! {
+ ::apache_avro::serde::get_record_fields_in_ctxt(named_schemas, enclosing_namespace, #path)
+ }))
+ }
With::Expr(_expr) => Err(vec![syn::Error::new(
field.span(),
"Invalid expression, expected function or closure",
@@ -69,9 +77,12 @@ pub fn to_record_fields(field: &Field, with: With) -> Result<TokenStream, Vec<sy
}
}
-pub fn to_default(field: &Field, default: FieldDefault) -> Result<TokenStream, Vec<syn::Error>> {
+pub fn to_default(
+ field: &Field,
+ default: FieldDefault,
+) -> Result<TypedTokenStream<Option<serde_json::Value>>, Vec<syn::Error>> {
match default {
- FieldDefault::Disabled => Ok(quote! { ::std::option::Option::None }),
+ FieldDefault::Disabled => Ok(TypedTokenStream::none()),
FieldDefault::Trait => type_to_field_default(&field.ty),
FieldDefault::Value(default_value) => {
let _: serde_json::Value = serde_json::from_str(&default_value[..]).map_err(|e| {
@@ -80,9 +91,9 @@ pub fn to_default(field: &Field, default: FieldDefault) -> Result<TokenStream, V
format!("Invalid avro default json: \n{e}"),
)]
})?;
- Ok(quote! {
+ Ok(TypedTokenStream::<Option<serde_json::Value>>::new(quote! {
::std::option::Option::Some(::serde_json::from_str(#default_value).expect("Unreachable! Checked at compile time"))
- })
+ }))
}
}
}
@@ -96,11 +107,11 @@ pub fn to_default(field: &Field, default: FieldDefault) -> Result<TokenStream, V
/// - `enclosing_namespace`: `Option<&str>`
/// ## Returns
/// An `Expr` that resolves to an instance of `Schema`.
-fn type_to_schema_expr(ty: &Type) -> Result<TokenStream, Vec<syn::Error>> {
+fn type_to_schema_expr(ty: &Type) -> Result<TypedTokenStream<Schema>, Vec<syn::Error>> {
match ty {
- Type::Slice(_) | Type::Path(_) | Type::Reference(_) => Ok(
+ Type::Slice(_) | Type::Path(_) | Type::Reference(_) => Ok(TypedTokenStream::<Schema>::new(
quote! {<#ty as :: apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace)},
- ),
+ )),
Type::Tuple(tuple) => tuple_to_schema(tuple),
Type::Array(array) => array_to_schema(array),
Type::Ptr(_) => Err(vec![syn::Error::new_spanned(
@@ -130,11 +141,13 @@ fn type_to_schema_expr(ty: &Type) -> Result<TokenStream, Vec<syn::Error>> {
/// - `enclosing_namespace`: `Option<&str>`
/// ## Returns
/// An `Expr` that resolves to an instance of `Schema`.
-fn tuple_to_schema(tuple: &TypeTuple) -> Result<TokenStream, Vec<syn::Error>> {
+fn tuple_to_schema(tuple: &TypeTuple) -> Result<TypedTokenStream<Schema>, Vec<syn::Error>> {
if tuple.elems.is_empty() {
- Ok(quote! {::apache_avro::schema::Schema::Null})
+ Ok(TypedTokenStream::<Schema>::new(
+ quote! {::apache_avro::schema::Schema::Null},
+ ))
} else if tuple.elems.len() == 1 {
- type_to_schema_expr(&tuple.elems.iter().next().unwrap())
+ type_to_schema_expr(tuple.elems.iter().next().unwrap())
} else {
let mut fields = Vec::with_capacity(tuple.elems.len());
@@ -161,7 +174,7 @@ fn tuple_to_schema(tuple: &TypeTuple) -> Result<TokenStream, Vec<syn::Error>> {
let name = format!("tuple_{}{tuple_as_valid_name}", tuple.elems.len());
- Ok(quote! {
+ Ok(TypedTokenStream::<Schema>::new(quote! {
::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema::builder()
.name(::apache_avro::schema::Name::new_with_enclosing_namespace(#name, enclosing_namespace).expect(&format!("Unable to parse variant record name for schema {}", #name)[..]))
.fields(vec![
@@ -174,7 +187,7 @@ fn tuple_to_schema(tuple: &TypeTuple) -> Result<TokenStream, Vec<syn::Error>> {
)
.build()
)
- })
+ }))
}
}
@@ -192,7 +205,7 @@ fn tuple_to_schema(tuple: &TypeTuple) -> Result<TokenStream, Vec<syn::Error>> {
/// - `enclosing_namespace`: `Option<&str>`
/// ## Returns
/// An `Expr` that resolves to an instance of `Schema`.
-fn array_to_schema(array: &TypeArray) -> Result<TokenStream, Vec<syn::Error>> {
+fn array_to_schema(array: &TypeArray) -> Result<TypedTokenStream<Schema>, Vec<syn::Error>> {
let Expr::Lit(ExprLit {
lit: Lit::Int(lit), ..
}) = &array.len
@@ -206,7 +219,9 @@ fn array_to_schema(array: &TypeArray) -> Result<TokenStream, Vec<syn::Error>> {
let len: usize = lit.base10_parse().map_err(|e| vec![e])?;
if len == 0 {
- Ok(quote! {::apache_avro::schema::Schema::Null})
+ Ok(TypedTokenStream::<Schema>::new(
+ quote! {::apache_avro::schema::Schema::Null},
+ ))
} else if len == 1 {
type_to_schema_expr(&array.elem)
} else {
@@ -233,7 +248,7 @@ fn array_to_schema(array: &TypeArray) -> Result<TokenStream, Vec<syn::Error>> {
let name = format!("array_{len}_{array_elem_as_valid_name}");
- Ok(quote! {
+ Ok(TypedTokenStream::<Schema>::new(quote! {
::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema::builder()
.name(::apache_avro::schema::Name::new_with_enclosing_namespace(#name, enclosing_namespace).expect(&format!("Unable to parse variant record name for schema {}", #name)[..]))
.fields(vec![
@@ -246,15 +261,19 @@ fn array_to_schema(array: &TypeArray) -> Result<TokenStream, Vec<syn::Error>> {
)
.build()
)
- })
+ }))
}
}
-fn type_to_record_fields(ty: &Type) -> Result<TokenStream, Vec<syn::Error>> {
+fn type_to_record_fields(
+ ty: &Type,
+) -> Result<TypedTokenStream<Option<Vec<RecordField>>>, Vec<syn::Error>> {
match ty {
- Type::Slice(_) | Type::Path(_) | Type::Reference(_) => Ok(
- quote! {<#ty as :: apache_avro::AvroSchemaComponent>::get_record_fields_in_ctxt(named_schemas, enclosing_namespace)},
- ),
+ Type::Slice(_) | Type::Path(_) | Type::Reference(_) => {
+ Ok(TypedTokenStream::<Option<Vec<RecordField>>>::new(
+ quote! {<#ty as :: apache_avro::AvroSchemaComponent>::get_record_fields_in_ctxt(named_schemas, enclosing_namespace)},
+ ))
+ }
Type::Array(array) => array_to_record_fields(array),
Type::Tuple(tuple) => tuple_to_record_fields(tuple),
Type::Ptr(_) => Err(vec![syn::Error::new_spanned(
@@ -284,11 +303,13 @@ fn type_to_record_fields(ty: &Type) -> Result<TokenStream, Vec<syn::Error>> {
/// - `enclosing_namespace`: `Option<&str>`
/// ## Returns
/// An `Expr` that resolves to an instance of `Schema`.
-fn tuple_to_record_fields(tuple: &TypeTuple) -> Result<TokenStream, Vec<syn::Error>> {
+fn tuple_to_record_fields(
+ tuple: &TypeTuple,
+) -> Result<TypedTokenStream<Option<Vec<RecordField>>>, Vec<syn::Error>> {
if tuple.elems.is_empty() {
- Ok(quote! {::std::option::Option::None})
+ Ok(TypedTokenStream::none())
} else if tuple.elems.len() == 1 {
- type_to_record_fields(&tuple.elems.iter().next().unwrap())
+ type_to_record_fields(tuple.elems.iter().next().unwrap())
} else {
let mut fields = Vec::with_capacity(tuple.elems.len());
@@ -303,9 +324,9 @@ fn tuple_to_record_fields(tuple: &TypeTuple) -> Result<TokenStream, Vec<syn::Err
});
}
- Ok(quote! {
+ Ok(TypedTokenStream::new(quote! {
::std::option::Option::Some(vec![#(#fields, )*])
- })
+ }))
}
}
/// Create a schema definition for an array.
@@ -322,10 +343,12 @@ fn tuple_to_record_fields(tuple: &TypeTuple) -> Result<TokenStream, Vec<syn::Err
/// - `enclosing_namespace`: `Option<&str>`
/// ## Returns
/// An `Expr` that resolves to an instance of `Schema`.
-fn array_to_record_fields(array: &TypeArray) -> Result<TokenStream, Vec<syn::Error>> {
+fn array_to_record_fields(
+ array: &TypeArray,
+) -> Result<TypedTokenStream<Option<Vec<RecordField>>>, Vec<syn::Error>> {
let Expr::Lit(ExprLit {
- lit: Lit::Int(lit), ..
- }) = &array.len
+ lit: Lit::Int(lit), ..
+ }) = &array.len
else {
return Err(vec![syn::Error::new(
array.span(),
@@ -336,7 +359,9 @@ fn array_to_record_fields(array: &TypeArray) -> Result<TokenStream, Vec<syn::Err
let len: usize = lit.base10_parse().map_err(|e| vec![e])?;
if len == 0 {
- Ok(quote! {::std::option::Option::None})
+ Ok(TypedTokenStream::<Option<_>>::new(
+ quote! {::std::option::Option::None},
+ ))
} else if len == 1 {
type_to_record_fields(&array.elem)
} else {
@@ -351,22 +376,26 @@ fn array_to_record_fields(array: &TypeArray) -> Result<TokenStream, Vec<syn::Err
}
});
- Ok(quote! {
+ Ok(TypedTokenStream::<Option<Vec<RecordField>>>::new(quote! {
::std::option::Option::Some(vec![#(#fields, )*])
- })
+ }))
}
}
-fn type_to_field_default(ty: &Type) -> Result<TokenStream, Vec<syn::Error>> {
+fn type_to_field_default(
+ ty: &Type,
+) -> Result<TypedTokenStream<Option<serde_json::Value>>, Vec<syn::Error>> {
match ty {
Type::Slice(_) | Type::Path(_) | Type::Reference(_) => {
- Ok(quote! {<#ty as :: apache_avro::AvroSchemaComponent>::field_default()})
+ Ok(TypedTokenStream::<Option<serde_json::Value>>::new(
+ quote! {<#ty as :: apache_avro::AvroSchemaComponent>::field_default()},
+ ))
}
Type::Ptr(_) => Err(vec![syn::Error::new_spanned(
ty,
"AvroSchema: derive does not support raw pointers",
)]),
- Type::Tuple(_) | Type::Array(_) => Ok(quote! { ::std::option::Option::None }),
+ Type::Tuple(_) | Type::Array(_) => Ok(TypedTokenStream::none()),
_ => Err(vec![syn::Error::new_spanned(
ty,
format!(
diff --git a/avro_derive/src/lib.rs b/avro_derive/src/lib.rs
index ab3e8c2..57fb3a0 100644
--- a/avro_derive/src/lib.rs
+++ b/avro_derive/src/lib.rs
@@ -34,11 +34,12 @@ mod case;
mod enums;
mod fields;
mod tuple;
+mod utils;
-use proc_macro2::{Span, TokenStream};
+use proc_macro2::Span;
use quote::quote;
use syn::{
- DataStruct, DeriveInput, Fields, FieldsNamed, Generics, Ident, Type, parse_macro_input,
+ DataStruct, DeriveInput, Fields, FieldsNamed, Generics, Ident, parse_macro_input,
spanned::Spanned,
};
@@ -46,6 +47,10 @@ use crate::{
attributes::{FieldOptions, NamedTypeOptions},
case::RenameRule,
tuple::unnamed_to_record_fields,
+ utils::{
+ RecordField, Schema, TypedTokenStream, aliases, doc_into_tokenstream, field_aliases,
+ preserve_optional, to_compile_errors,
+ },
};
#[proc_macro_derive(AvroSchema, attributes(avro, serde))]
@@ -57,7 +62,7 @@ pub fn proc_macro_derive_avro_schema(input: proc_macro::TokenStream) -> proc_mac
.into()
}
-fn derive_avro_schema(input: DeriveInput) -> Result<TokenStream, Vec<syn::Error>> {
+fn derive_avro_schema(input: DeriveInput) -> Result<proc_macro2::TokenStream, Vec<syn::Error>> {
// It would be nice to parse the attributes before the `match`, but we first need to validate that `input` is not a union.
// Otherwise a user could get errors related to the attributes and after fixing those get an error because the attributes were on a union.
let input_span = input.span();
@@ -68,9 +73,15 @@ fn derive_avro_schema(input: DeriveInput) -> Result<TokenStream, Vec<syn::Error>
Ok(create_trait_definition(
input.ident,
&input.generics,
- quote! { <#path as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace) },
- quote! { <#path as ::apache_avro::AvroSchemaComponent>::get_record_fields_in_ctxt(named_schemas, enclosing_namespace) },
- quote! { <#path as ::apache_avro::AvroSchemaComponent>::field_default() },
+ TypedTokenStream::<Schema>::new(
+ quote! { <#path as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(named_schemas, enclosing_namespace) },
+ ),
+ TypedTokenStream::<Option<Vec<RecordField>>>::new(
+ quote! { <#path as ::apache_avro::AvroSchemaComponent>::get_record_fields_in_ctxt(named_schemas, enclosing_namespace) },
+ ),
+ TypedTokenStream::<Option<serde_json::Value>>::new(
+ quote! { <#path as ::apache_avro::AvroSchemaComponent>::field_default() },
+ ),
))
} else {
match input.data {
@@ -116,7 +127,7 @@ fn derive_avro_schema(input: DeriveInput) -> Result<TokenStream, Vec<syn::Error>
input.ident,
&input.generics,
inner,
- quote! { None },
+ TypedTokenStream::none(),
named_type_options.default,
))
}
@@ -132,10 +143,10 @@ fn derive_avro_schema(input: DeriveInput) -> Result<TokenStream, Vec<syn::Error>
fn create_trait_definition(
ident: Ident,
generics: &Generics,
- get_schema_impl: TokenStream,
- get_record_fields_impl: TokenStream,
- field_default_impl: TokenStream,
-) -> TokenStream {
+ get_schema_impl: TypedTokenStream<Schema>,
+ get_record_fields_impl: TypedTokenStream<Option<Vec<RecordField>>>,
+ field_default_impl: TypedTokenStream<Option<serde_json::Value>>,
+) -> proc_macro2::TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
quote! {
#[automatically_derived]
@@ -156,8 +167,11 @@ fn create_trait_definition(
}
/// Generate the code to check `named_schemas` if this schema already exist
-fn handle_named_schemas(full_schema_name: String, schema_def: TokenStream) -> TokenStream {
- quote! {
+fn handle_named_schemas(
+ full_schema_name: String,
+ schema_def: TypedTokenStream<Schema>,
+) -> TypedTokenStream<Schema> {
+ TypedTokenStream::<Schema>::new(quote! {{
let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(#full_schema_name, enclosing_namespace).expect(concat!("Unable to parse schema name ", #full_schema_name));
if named_schemas.contains(&name) {
::apache_avro::schema::Schema::Ref{name}
@@ -166,30 +180,36 @@ fn handle_named_schemas(full_schema_name: String, schema_def: TokenStream) -> To
named_schemas.insert(name.clone());
#schema_def
}
- }
+ }})
}
/// Generate a schema definition for a struct.
fn get_struct_schema_def(
container_attrs: &NamedTypeOptions,
data_struct: DataStruct,
-) -> Result<(TokenStream, TokenStream), Vec<syn::Error>> {
+) -> Result<
+ (
+ TypedTokenStream<Schema>,
+ TypedTokenStream<Option<Vec<RecordField>>>,
+ ),
+ Vec<syn::Error>,
+> {
let record_fields = match data_struct.fields {
Fields::Named(a) => named_to_record_fields(a, container_attrs.rename_all)?,
Fields::Unnamed(unnamed) => unnamed_to_record_fields(unnamed)?,
- Fields::Unit => quote! { std::vec::Vec::<::apache_avro::schema::RecordField>::new() },
+ Fields::Unit => TypedTokenStream::<Vec<RecordField>>::new(
+ quote! { std::vec::Vec::<::apache_avro::schema::RecordField>::new() },
+ ),
};
let record_doc = preserve_optional(container_attrs.doc.as_ref());
let record_aliases = aliases(&container_attrs.aliases);
- let full_schema_name = &container_attrs.name;
- let schema_def = quote! {
+ let schema_def = TypedTokenStream::<Schema>::new(quote! {
{
let schema_fields = #record_fields;
let schema_field_set: ::std::collections::HashSet<_> = schema_fields.iter().map(|rf| &rf.name).collect();
assert_eq!(schema_fields.len(), schema_field_set.len(), "Duplicate field names found: {schema_fields:?}");
- let name = ::apache_avro::schema::Name::new(#full_schema_name).expect(&format!("Unable to parse struct name for schema {}", #full_schema_name)[..]);
let lookup: ::std::collections::BTreeMap<String, usize> = schema_fields
.iter()
.enumerate()
@@ -204,10 +224,10 @@ fn get_struct_schema_def(
attributes: ::std::collections::BTreeMap::new(),
})
}
- };
- let record_fields = quote! {
+ });
+ let record_fields = TypedTokenStream::<Option<Vec<RecordField>>>::new(quote! {
::std::option::Option::Some(#record_fields)
- };
+ });
Ok((schema_def, record_fields))
}
@@ -216,7 +236,13 @@ fn get_struct_schema_def(
fn get_transparent_struct_schema_def(
fields: Fields,
input_span: Span,
-) -> Result<(TokenStream, TokenStream), Vec<syn::Error>> {
+) -> Result<
+ (
+ TypedTokenStream<Schema>,
+ TypedTokenStream<Option<Vec<RecordField>>>,
+ ),
+ Vec<syn::Error>,
+> {
match fields {
Fields::Named(fields_named) => {
let mut found = None;
@@ -283,7 +309,7 @@ fn get_transparent_struct_schema_def(
fn named_to_record_fields(
named: FieldsNamed,
rename_all: RenameRule,
-) -> Result<TokenStream, Vec<syn::Error>> {
+) -> Result<TypedTokenStream<Vec<RecordField>>, Vec<syn::Error>> {
let mut fields = Vec::with_capacity(named.named.len());
for field in named.named {
let field_attrs = FieldOptions::new(&field.attrs, field.span())?;
@@ -338,7 +364,7 @@ fn named_to_record_fields(
// When fields are flattened there might be more fields, but this is good for the regular case
let minimum_length = fields.len();
- Ok(quote! {
+ Ok(TypedTokenStream::new(quote! {
{
let mut fields = ::std::vec::Vec::with_capacity(#minimum_length);
#(
@@ -346,49 +372,5 @@ fn named_to_record_fields(
)*
fields
}
- })
-}
-
-/// Stolen from serde
-fn to_compile_errors(errors: Vec<syn::Error>) -> TokenStream {
- let compile_errors = errors.iter().map(syn::Error::to_compile_error);
- quote!(#(#compile_errors)*)
-}
-
-fn preserve_optional(op: Option<impl quote::ToTokens>) -> TokenStream {
- match op {
- Some(tt) => quote! {::std::option::Option::Some(#tt.into())},
- None => quote! {::std::option::Option::None},
- }
-}
-
-fn doc_into_tokenstream(doc: Option<String>) -> TokenStream {
- match doc {
- Some(doc) => quote! {::std::option::Option::Some(#doc.to_string())},
- None => quote! {::std::option::Option::None},
- }
-}
-
-fn aliases(op: &[impl quote::ToTokens]) -> TokenStream {
- let items: Vec<TokenStream> = op
- .iter()
- .map(|tt| quote! {#tt.try_into().expect("Alias is invalid")})
- .collect();
- if items.is_empty() {
- quote! {::std::option::Option::None}
- } else {
- quote! {::std::option::Option::Some(vec![#(#items),*])}
- }
-}
-
-fn field_aliases(op: &[impl quote::ToTokens]) -> TokenStream {
- let items: Vec<TokenStream> = op
- .iter()
- .map(|tt| quote! {#tt.try_into().expect("Alias is invalid")})
- .collect();
- if items.is_empty() {
- quote! {::std::vec::Vec::new()}
- } else {
- quote! {vec![#(#items),*]}
- }
+ }))
}
diff --git a/avro_derive/src/tuple.rs b/avro_derive/src/tuple.rs
index 8253332..1f669d1 100644
--- a/avro_derive/src/tuple.rs
+++ b/avro_derive/src/tuple.rs
@@ -1,8 +1,10 @@
-use proc_macro2::TokenStream;
use quote::quote;
use syn::{FieldsUnnamed, spanned::Spanned};
-use crate::{FieldOptions, doc_into_tokenstream, field_aliases, fields};
+use crate::{
+ FieldOptions, RecordField, Schema, TypedTokenStream, fields,
+ utils::{doc_into_tokenstream, field_aliases},
+};
/// Create a `Schema::Record` from this tuple definition.
///
@@ -22,10 +24,10 @@ pub fn tuple_struct_variant_to_record_schema(
unnamed: FieldsUnnamed,
name: &str,
extra_attributes: &[&str],
-) -> Result<TokenStream, Vec<syn::Error>> {
+) -> Result<TypedTokenStream<Schema>, Vec<syn::Error>> {
let fields = unnamed_to_record_fields(unnamed)?;
- Ok(quote! {
+ Ok(TypedTokenStream::<Schema>::new(quote! {
::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema::builder()
.name(::apache_avro::schema::Name::new_with_enclosing_namespace(#name, enclosing_namespace).expect(&format!("Unable to parse variant record name for schema {}", #name)[..]))
.fields(#fields)
@@ -37,7 +39,7 @@ pub fn tuple_struct_variant_to_record_schema(
)
.build()
)
- })
+ }))
}
/// Create a vector of `RecordField`s named `field_{field_index}`.
@@ -49,7 +51,9 @@ pub fn tuple_struct_variant_to_record_schema(
/// - `enclosing_namespace`: `Option<&str>`
/// ## Returns
/// An `Expr` that resolves to an instance of `Vec<RecordField>`.
-pub fn unnamed_to_record_fields(unnamed: FieldsUnnamed) -> Result<TokenStream, Vec<syn::Error>> {
+pub fn unnamed_to_record_fields(
+ unnamed: FieldsUnnamed,
+) -> Result<TypedTokenStream<Vec<RecordField>>, Vec<syn::Error>> {
let mut fields = Vec::with_capacity(unnamed.unnamed.len());
for (index, field) in unnamed.unnamed.into_iter().enumerate() {
let field_attrs = FieldOptions::new(&field.attrs, field.span())?;
@@ -78,9 +82,9 @@ pub fn unnamed_to_record_fields(unnamed: FieldsUnnamed) -> Result<TokenStream, V
.build()
});
}
- Ok(quote! {
+ Ok(TypedTokenStream::new(quote! {
vec![
#(#fields, )*
]
- })
+ }))
}
diff --git a/avro_derive/src/utils.rs b/avro_derive/src/utils.rs
new file mode 100644
index 0000000..ecfa339
--- /dev/null
+++ b/avro_derive/src/utils.rs
@@ -0,0 +1,91 @@
+use std::{fmt::Display, marker::PhantomData};
+
+use quote::{ToTokens, quote};
+
+/// A [`TokenStream`] that is a expression that evaluates to `Output`
+///
+/// This token stream can define and expect any variables names.
+pub struct TypedTokenStream<Output> {
+ output: PhantomData<Output>,
+ stream: proc_macro2::TokenStream,
+}
+
+/// Stand-in for `apache_avro::Schema` for [`TypedTokenStream`]
+pub struct Schema;
+
+/// Stand-in for `apache_avro::schema::RecordField` for [`TypedTokenStream`]
+pub struct RecordField;
+
+impl<Output> TypedTokenStream<Output> {
+ pub fn new(stream: proc_macro2::TokenStream) -> Self {
+ Self {
+ output: PhantomData,
+ stream,
+ }
+ }
+}
+
+impl<Output> TypedTokenStream<Option<Output>> {
+ pub fn none() -> Self {
+ Self {
+ output: PhantomData,
+ stream: quote! { ::std::option::Option::None },
+ }
+ }
+}
+
+impl<Output> ToTokens for TypedTokenStream<Output> {
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
+ self.stream.to_tokens(tokens);
+ }
+}
+
+impl<Output> Display for TypedTokenStream<Output> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.stream.fmt(f)
+ }
+}
+
+/// Stolen from serde
+pub fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
+ let compile_errors = errors.iter().map(syn::Error::to_compile_error);
+ quote!(#(#compile_errors)*)
+}
+
+pub fn preserve_optional(op: Option<&String>) -> TypedTokenStream<Option<String>> {
+ match op {
+ Some(tt) => TypedTokenStream::new(quote! {::std::option::Option::Some(#tt.into())}),
+ None => TypedTokenStream::new(quote! {::std::option::Option::None}),
+ }
+}
+
+pub fn doc_into_tokenstream(doc: Option<String>) -> TypedTokenStream<Option<String>> {
+ match doc {
+ Some(doc) => TypedTokenStream::new(quote! {::std::option::Option::Some(#doc.to_string())}),
+ None => TypedTokenStream::new(quote! {::std::option::Option::None}),
+ }
+}
+
+pub fn aliases(op: &[String]) -> TypedTokenStream<Option<Vec<String>>> {
+ let items: Vec<proc_macro2::TokenStream> = op
+ .iter()
+ .map(|tt| quote! {#tt.try_into().expect("Alias is invalid")})
+ .collect();
+ if items.is_empty() {
+ TypedTokenStream::new(quote! {::std::option::Option::None})
+ } else {
+ TypedTokenStream::new(quote! {::std::option::Option::Some(vec![#(#items),*])})
+ }
+}
+
+pub fn field_aliases(op: &[impl quote::ToTokens]) -> TypedTokenStream<Vec<String>> {
+ let items: Vec<proc_macro2::TokenStream> = op
+ .iter()
+ .map(|tt| quote! {#tt.try_into().expect("Alias is invalid")})
+ .collect();
+ if items.is_empty() {
+ TypedTokenStream::new(quote! {::std::vec::Vec::new()})
+ } else {
+ TypedTokenStream::new(quote! {vec![#(#items),*]})
+ }
+}
diff --git a/avro_derive/tests/derive.rs b/avro_derive/tests/derive.rs
index 268e175..74428b6 100644
--- a/avro_derive/tests/derive.rs
+++ b/avro_derive/tests/derive.rs
@@ -2102,6 +2102,8 @@ fn avro_rs_401_supported_type_variants() {
three: &'static i32,
four: &'a str,
five: &'a mut f64,
+ #[avro(with)]
+ #[serde(with = "apache_avro::serde::array")]
six: [u8; 5],
seven: [u8],
}
diff --git a/avro_derive/tests/enum.rs b/avro_derive/tests/enum.rs
index ff79088..e4e272c 100644
--- a/avro_derive/tests/enum.rs
+++ b/avro_derive/tests/enum.rs
@@ -16,6 +16,7 @@
// under the License.
use apache_avro::{AvroSchema, Error, Reader, Schema, Writer};
+use pretty_assertions::assert_eq;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
/// Takes in a type that implements the right combination of traits and runs it through a Serde
@@ -76,11 +77,23 @@ fn avro_rs_xxx_enum_repr_default() {
enum Foo {
A,
B,
+ #[serde(rename = "D")]
C,
}
- assert!(matches!(Foo::get_schema(), Schema::Enum(_)));
+ let schema = Schema::parse_str(
+ r#"{
+ "type": "enum",
+ "name": "Foo",
+ "symbols": ["A", "B", "D"]
+ }"#,
+ )
+ .unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
serde_assert(Foo::A);
+ serde_assert(Foo::B);
+ serde_assert(Foo::C);
}
#[test]
@@ -90,45 +103,186 @@ fn avro_rs_xxx_enum_repr_enum() {
enum Foo {
A,
B,
+ #[serde(rename = "D")]
C,
}
- assert!(matches!(Foo::get_schema(), Schema::Enum(_)));
+ let schema = Schema::parse_str(
+ r#"{
+ "type": "enum",
+ "name": "Foo",
+ "symbols": ["A", "B", "D"]
+ }"#,
+ )
+ .unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
serde_assert(Foo::A);
+ serde_assert(Foo::B);
+ serde_assert(Foo::C);
}
#[test]
-fn avro_rs_xxx_enum_repr_discriminator_value_plain() {
+fn avro_rs_xxx_enum_repr_record_tag_content_plain() {
#[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
#[avro(repr = "record_tag_content")]
#[serde(tag = "type", content = "value")]
enum Foo {
A,
B,
+ #[serde(rename = "D")]
C,
}
- assert!(matches!(Foo::get_schema(), Schema::Record(_)));
+ let schema = Schema::parse_str(
+ r#"{
+ "type": "record",
+ "name": "Foo",
+ "fields": [
+ {
+ "name": "type",
+ "type": {
+ "type": "enum",
+ "name": "type",
+ "symbols": ["A", "B", "D"]
+ }
+ },
+ {
+ "name": "value",
+ "type": [
+ "null"
+ ]
+ }
+ ]
+ }"#,
+ )
+ .unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
serde_assert(Foo::A);
+ serde_assert(Foo::B);
+ serde_assert(Foo::C);
}
#[test]
-fn avro_rs_xxx_enum_repr_discriminator_value_tuple() {
+fn avro_rs_xxx_enum_repr_record_tag_content_tuple() {
#[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
#[avro(repr = "record_tag_content")]
#[serde(tag = "type", content = "value")]
enum Foo {
A(),
B(String),
- C(String, bool),
+ #[serde(rename = "D")]
+ C(
+ String,
+ #[serde(rename = "is_it_true", alias = "is_it_false")] bool,
+ ),
}
- assert!(matches!(Foo::get_schema(), Schema::Record(_)));
+ let schema = Schema::parse_str(
+ r#"{
+ "type": "record",
+ "name": "Foo",
+ "fields": [
+ {
+ "name": "type",
+ "type": {
+ "type": "enum",
+ "name": "type",
+ "symbols": ["A", "B", "D"]
+ }
+ },
+ {
+ "name": "value",
+ "type": [
+ "null",
+ "string",
+ {
+ "type": "record",
+ "name": "D",
+ "default": "null",
+ "fields": [
+ { "name": "field_0", "type": "string" },
+ { "name": "is_it_true", "aliases": ["is_it_false"], "type": "boolean" }
+ ],
+ "org.apache.avro.rust.tuple": true
+ }
+ ]
+ }
+ ]
+ }"#,
+ )
+ .unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
serde_assert(Foo::A());
serde_assert(Foo::B("Something".to_string()));
serde_assert(Foo::C("Something".to_string(), true));
}
+#[test]
+fn avro_rs_xxx_enum_repr_record_tag_content_struct() {
+ #[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
+ #[avro(repr = "record_tag_content")]
+ #[serde(tag = "type", content = "value")]
+ enum Foo {
+ A {},
+ B {
+ spam: String,
+ },
+ #[serde(rename = "D")]
+ C {
+ bar: String,
+ #[serde(rename = "is_it_true", alias = "is_it_false")]
+ other: bool,
+ },
+ }
+
+ let schema = Schema::parse_str(
+ r#"{
+ "type": "record",
+ "name": "Foo",
+ "fields": [
+ {
+ "name": "type",
+ "type": {
+ "type": "enum",
+ "name": "type",
+ "symbols": ["A", "B", "D"]
+ }
+ },
+ {
+ "name": "value",
+ "type": [
+ {"type": "record", "name": "A", "fields": []},
+ {"type": "record", "name": "B", "fields": [{"name": "spam", "type": "string"}]},
+ {
+ "type": "record",
+ "name": "D",
+ "default": "null",
+ "fields": [
+ { "name": "bar", "type": "string" },
+ { "name": "is_it_true", "aliases": ["is_it_false"], "type": "boolean" }
+ ]
+ }
+ ]
+ }
+ ]
+ }"#,
+ )
+ .unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
+ serde_assert(Foo::A {});
+ serde_assert(Foo::B {
+ spam: "Something".to_string(),
+ });
+ serde_assert(Foo::C {
+ bar: "Something".to_string(),
+ other: true,
+ });
+}
+
#[test]
fn avro_rs_xxx_enum_repr_bare_union_plain() {
#[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
@@ -138,7 +292,9 @@ fn avro_rs_xxx_enum_repr_bare_union_plain() {
A,
}
- assert!(matches!(Foo::get_schema(), Schema::Union(_)));
+ let schema = Schema::parse_str(r#"["null"]"#).unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
serde_assert(Foo::A);
}
@@ -149,10 +305,299 @@ fn avro_rs_xxx_enum_repr_bare_union_tuple() {
#[serde(untagged)]
enum Foo {
B(String),
- C(String, bool),
+ #[serde(rename = "D")]
+ C(
+ String,
+ #[serde(rename = "is_it_true", alias = "is_it_false")] bool,
+ ),
}
- assert!(matches!(Foo::get_schema(), Schema::Union(_)));
+ let schema = Schema::parse_str(
+ r#"[
+ "string",
+ {
+ "type": "record",
+ "name": "D",
+ "default": "null",
+ "fields": [
+ { "name": "field_0", "type": "string" },
+ { "name": "is_it_true", "aliases": ["is_it_false"], "type": "boolean" }
+ ],
+ "org.apache.avro.rust.tuple": true
+ }
+ ]"#,
+ )
+ .unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
serde_assert(Foo::B("Something".to_string()));
serde_assert(Foo::C("Something".to_string(), true));
}
+
+#[test]
+fn avro_rs_xxx_enum_repr_record_internally_tagged_plain() {
+ #[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
+ #[avro(repr = "record_internally_tagged")]
+ #[serde(tag = "type")]
+ enum Foo {
+ A,
+ B,
+ #[serde(rename = "D")]
+ C,
+ }
+
+ let schema = Schema::parse_str(
+ r#"{
+ "type": "record",
+ "name": "Foo",
+ "fields": [
+ {
+ "name": "type",
+ "type": "string"
+ }
+ ]
+ }"#,
+ )
+ .unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
+ serde_assert(Foo::A);
+ serde_assert(Foo::B);
+ serde_assert(Foo::C);
+}
+
+#[test]
+#[should_panic(
+ expected = "Newtype variant type must implement `get_record_fields` for internally tagged enums"
+)]
+fn avro_rs_xxx_enum_repr_record_internally_tagged_tuple_no_get_record_fields() {
+ #[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
+ #[avro(repr = "record_internally_tagged")]
+ #[serde(tag = "type")]
+ enum Foo {
+ B(String),
+ }
+
+ let _ = Foo::get_schema();
+}
+
+#[test]
+fn avro_rs_xxx_enum_repr_record_internally_tagged_tuple() {
+ #[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
+ #[avro(repr = "record_internally_tagged")]
+ #[serde(tag = "type")]
+ enum Foo {
+ B(Bar),
+ }
+
+ #[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
+ struct Bar {
+ spam: String,
+ }
+
+ let schema = Schema::parse_str(
+ r#"{
+ "type": "record",
+ "name": "Foo",
+ "fields": [
+ {
+ "name": "type",
+ "type": "string"
+ },
+ {
+ "name": "spam",
+ "type": "string"
+ }
+ ]
+ }"#,
+ )
+ .unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
+ serde_assert(Foo::B(Bar {
+ spam: "Something".to_string(),
+ }));
+}
+
+#[test]
+fn avro_rs_xxx_enum_repr_record_internally_tagged_struct() {
+ #[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
+ #[avro(repr = "record_internally_tagged")]
+ #[serde(tag = "type")]
+ enum Foo {
+ A {},
+ B {
+ #[avro(default = r#""spam""#)]
+ spam: String,
+ },
+ #[serde(rename = "D")]
+ C {
+ #[avro(default = r#""bar""#)]
+ bar: String,
+ #[serde(rename = "is_it_true", alias = "is_it_false")]
+ #[avro(default = "true")]
+ other: bool,
+ },
+ }
+
+ let schema = Schema::parse_str(
+ r#"{
+ "type": "record",
+ "name": "Foo",
+ "fields": [
+ {
+ "name": "type",
+ "type": "string"
+ },
+ {
+ "name": "spam",
+ "type": "string"
+ },
+ {
+ "name": "bar",
+ "type": "string"
+ },
+ {
+ "name": "is_it_true",
+ "aliases": ["is_it_false"],
+ "type": "boolean"
+ }
+ ]
+ }"#,
+ )
+ .unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
+ serde_assert(Foo::A {});
+ serde_assert(Foo::B {
+ spam: "Something".to_string(),
+ });
+ serde_assert(Foo::C {
+ bar: "Something".to_string(),
+ other: true,
+ });
+}
+
+#[test]
+#[should_panic(expected = "Missing default for skipped field 'spam' for schema")]
+fn avro_rs_xxx_enum_repr_record_internally_tagged_struct_no_defaults() {
+ #[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
+ #[avro(repr = "record_internally_tagged")]
+ #[serde(tag = "type")]
+ enum Foo {
+ A {},
+ B {
+ spam: String,
+ },
+ #[serde(rename = "D")]
+ C {
+ bar: String,
+ #[serde(rename = "is_it_true", alias = "is_it_false")]
+ other: bool,
+ },
+ }
+
+ serde_assert(Foo::A {});
+}
+
+#[test]
+fn avro_rs_xxx_enum_repr_union_of_records_plain() {
+ #[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
+ #[avro(repr = "union_of_records")]
+ enum Foo {
+ A,
+ B,
+ #[serde(rename = "D")]
+ C,
+ }
+
+ let schema = Schema::parse_str(
+ r#"[
+ {"type": "record", "name": "A", "fields": []},
+ {"type": "record", "name": "B", "fields": []},
+ {"type": "record", "name": "D", "fields": []}
+ ]"#,
+ )
+ .unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
+ serde_assert(Foo::A);
+ serde_assert(Foo::B);
+ serde_assert(Foo::C);
+}
+
+#[test]
+fn avro_rs_xxx_enum_repr_union_of_records_tuple() {
+ #[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
+ #[avro(repr = "union_of_records")]
+ enum Foo {
+ A(),
+ B(String),
+ #[serde(rename = "D")]
+ C(
+ String,
+ #[serde(rename = "is_it_true", alias = "is_it_false")] bool,
+ ),
+ }
+
+ let schema = Schema::parse_str(
+ r#"[
+ {"type": "record", "name": "A", "fields": [], "org.apache.avro.rust.tuple": true},
+ {"type": "record", "name": "B", "fields": [{
+ "name": "field_0", "type": "string"
+ }], "org.apache.avro.rust.tuple": true, "org.apache.avro.rust.union_of_records": true },
+ {"type": "record", "name": "D", "fields": [
+ {"name": "field_0", "type": "string"},
+ {"name": "is_it_true", "aliases": ["is_it_false"], "type": "boolean"}
+ ], "org.apache.avro.rust.tuple": true}
+ ]"#,
+ )
+ .unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
+ serde_assert(Foo::A());
+ serde_assert(Foo::B("Something".to_string()));
+ serde_assert(Foo::C("Something".to_string(), true));
+}
+
+#[test]
+fn avro_rs_xxx_enum_repr_union_of_records_struct() {
+ #[derive(AvroSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
+ #[avro(repr = "union_of_records")]
+ enum Foo {
+ A {},
+ B {
+ spam: String,
+ },
+ #[serde(rename = "D")]
+ C {
+ bar: String,
+ #[serde(rename = "is_it_true", alias = "is_it_false")]
+ other: bool,
+ },
+ }
+
+ let schema = Schema::parse_str(
+ r#"[
+ {"type": "record", "name": "A", "fields": []},
+ {"type": "record", "name": "B", "fields": [{
+ "name": "spam", "type": "string"
+ }], "org.apache.avro.rust.union_of_records": true },
+ {"type": "record", "name": "D", "fields": [
+ {"name": "bar", "type": "string"},
+ {"name": "is_it_true", "aliases": ["is_it_false"], "type": "boolean"}
+ ]}
+ ]"#,
+ )
+ .unwrap();
+
+ assert_eq!(Foo::get_schema(), schema);
+ serde_assert(Foo::A {});
+ serde_assert(Foo::B {
+ spam: "Something".to_string(),
+ });
+ serde_assert(Foo::C {
+ bar: "Something".to_string(),
+ other: true,
+ });
+}
diff --git a/avro_derive/tests/expanded/avro_3709_record_field_attributes.expanded.rs b/avro_derive/tests/expanded/avro_3709_record_field_attributes.expanded.rs
index 78f8992..7b5abf2 100644
--- a/avro_derive/tests/expanded/avro_3709_record_field_attributes.expanded.rs
+++ b/avro_derive/tests/expanded/avro_3709_record_field_attributes.expanded.rs
@@ -32,7 +32,7 @@ impl ::apache_avro::AvroSchemaComponent for A {
.name("a3".to_string())
.doc(::std::option::Option::Some("a doc".to_string()))
.maybe_default(
- Some(
+ ::std::option::Option::Some(
::serde_json::from_str("123")
.expect(
::alloc::__export::must_use({
@@ -124,7 +124,7 @@ impl ::apache_avro::AvroSchemaComponent for A {
.name("a3".to_string())
.doc(::std::option::Option::Some("a doc".to_string()))
.maybe_default(
- Some(
+ ::std::option::Option::Some(
::serde_json::from_str("123")
.expect(
::alloc::__export::must_use({
diff --git a/avro_derive/tests/expanded/avro_rs_xxx_bare_union.expanded.rs b/avro_derive/tests/expanded/avro_rs_xxx_bare_union.expanded.rs
index e37481e..99ec83e 100644
--- a/avro_derive/tests/expanded/avro_rs_xxx_bare_union.expanded.rs
+++ b/avro_derive/tests/expanded/avro_rs_xxx_bare_union.expanded.rs
@@ -4,9 +4,6 @@ enum Abc {
A,
B(bool),
C(#[avro(doc = "This is an int")] i32, i64),
- D {},
- E { is_it_true: bool },
- F { #[avro(doc = "This is X")] x: f64, y: f32 },
}
#[automatically_derived]
impl ::apache_avro::AvroSchemaComponent for Abc {
@@ -109,138 +106,6 @@ impl ::apache_avro::AvroSchemaComponent for Abc {
),
)
.expect("Duplicate Schema found");
- builder
- .variant(
- ::apache_avro::schema::Schema::Record(
- ::apache_avro::schema::RecordSchema::builder()
- .name(
- ::apache_avro::schema::Name::new_with_enclosing_namespace(
- "D",
- enclosing_namespace,
- )
- .expect(
- &::alloc::__export::must_use({
- ::alloc::fmt::format(
- format_args!(
- "Unable to parse variant record name for schema {0}", "D",
- ),
- )
- })[..],
- ),
- )
- .fields({
- let mut fields = ::std::vec::Vec::with_capacity(0usize);
- fields
- })
- .build(),
- ),
- )
- .expect("Duplicate Schema found");
- builder
- .variant(
- ::apache_avro::schema::Schema::Record(
- ::apache_avro::schema::RecordSchema::builder()
- .name(
- ::apache_avro::schema::Name::new_with_enclosing_namespace(
- "E",
- enclosing_namespace,
- )
- .expect(
- &::alloc::__export::must_use({
- ::alloc::fmt::format(
- format_args!(
- "Unable to parse variant record name for schema {0}", "E",
- ),
- )
- })[..],
- ),
- )
- .fields({
- let mut fields = ::std::vec::Vec::with_capacity(1usize);
- fields
- .push(
- ::apache_avro::schema::RecordField::builder()
- .name("is_it_true".to_string())
- .doc(::std::option::Option::None)
- .maybe_default(
- <bool as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <bool as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
- enclosing_namespace,
- ),
- )
- .build(),
- );
- fields
- })
- .build(),
- ),
- )
- .expect("Duplicate Schema found");
- builder
- .variant(
- ::apache_avro::schema::Schema::Record(
- ::apache_avro::schema::RecordSchema::builder()
- .name(
- ::apache_avro::schema::Name::new_with_enclosing_namespace(
- "F",
- enclosing_namespace,
- )
- .expect(
- &::alloc::__export::must_use({
- ::alloc::fmt::format(
- format_args!(
- "Unable to parse variant record name for schema {0}", "F",
- ),
- )
- })[..],
- ),
- )
- .fields({
- let mut fields = ::std::vec::Vec::with_capacity(2usize);
- fields
- .push(
- ::apache_avro::schema::RecordField::builder()
- .name("x".to_string())
- .doc(::std::option::Option::Some("This is X".to_string()))
- .maybe_default(
- <f64 as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <f64 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
- enclosing_namespace,
- ),
- )
- .build(),
- );
- fields
- .push(
- ::apache_avro::schema::RecordField::builder()
- .name("y".to_string())
- .doc(::std::option::Option::None)
- .maybe_default(
- <f32 as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <f32 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
- enclosing_namespace,
- ),
- )
- .build(),
- );
- fields
- })
- .build(),
- ),
- )
- .expect("Duplicate Schema found");
::apache_avro::schema::Schema::Union(builder.build())
}
}
diff --git a/avro_derive/tests/expanded/avro_rs_xxx_bare_union.rs b/avro_derive/tests/expanded/avro_rs_xxx_bare_union.rs
index 34a3c51..163c1a4 100644
--- a/avro_derive/tests/expanded/avro_rs_xxx_bare_union.rs
+++ b/avro_derive/tests/expanded/avro_rs_xxx_bare_union.rs
@@ -22,18 +22,5 @@ use apache_avro::AvroSchema;
enum Abc {
A,
B(bool),
- C(
- #[avro(doc = "This is an int")]
- i32,
- i64
- ),
- D {},
- E {
- is_it_true: bool,
- },
- F {
- #[avro(doc = "This is X")]
- x: f64,
- y: f32,
- }
+ C(#[avro(doc = "This is an int")] i32, i64),
}
diff --git a/avro_derive/tests/expanded/avro_rs_xxx_internally_tagged.expanded.rs b/avro_derive/tests/expanded/avro_rs_xxx_internally_tagged.expanded.rs
index d75f321..4cf9142 100644
--- a/avro_derive/tests/expanded/avro_rs_xxx_internally_tagged.expanded.rs
+++ b/avro_derive/tests/expanded/avro_rs_xxx_internally_tagged.expanded.rs
@@ -1,19 +1,24 @@
use apache_avro::AvroSchema;
-struct A {
- a: i32,
- b: String,
+#[avro(repr = "record_internally_tagged")]
+#[serde(tag = "type")]
+enum Abc {
+ A,
+ B(bool),
+ D {},
+ E { is_it_true: bool },
+ F { #[avro(doc = "This is X")] x: f64, y: f32 },
}
#[automatically_derived]
-impl ::apache_avro::AvroSchemaComponent for A {
+impl ::apache_avro::AvroSchemaComponent for Abc {
fn get_schema_in_ctxt(
named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>,
enclosing_namespace: ::apache_avro::schema::NamespaceRef,
) -> ::apache_avro::schema::Schema {
let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
- "A",
+ "Abc",
enclosing_namespace,
)
- .expect("Unable to parse schema name A");
+ .expect("Unable to parse schema name Abc");
if named_schemas.contains(&name) {
::apache_avro::schema::Schema::Ref {
name,
@@ -21,20 +26,48 @@ impl ::apache_avro::AvroSchemaComponent for A {
} else {
let enclosing_namespace = name.namespace();
named_schemas.insert(name.clone());
- {
- let schema_fields = {
- let mut fields = ::std::vec::Vec::with_capacity(2usize);
+ let mut builder = ::apache_avro::schema::UnionSchema::builder();
+ let mut fields = ::std::vec::Vec::new();
+ fields
+ .push(
+ ::apache_avro::schema::RecordField::builder()
+ .name("type")
+ .schema(::apache_avro::schema::Schema::String)
+ .build(),
+ );
+ if let Some(record_fields) = <bool as ::apache_avro::AvroSchemaComponent>::get_record_fields_in_ctxt(
+ named_schemas,
+ enclosing_namespace,
+ ) {
+ fields.extend(record_fields);
+ } else {
+ {
+ ::core::panicking::panic_fmt(
+ format_args!(
+ "Newtype variant type must implement `get_record_fields` for internally tagged enums",
+ ),
+ );
+ };
+ }
+ fields
+ .extend({
+ let mut fields = ::std::vec::Vec::with_capacity(0usize);
+ fields
+ });
+ fields
+ .extend({
+ let mut fields = ::std::vec::Vec::with_capacity(1usize);
fields
.push(
::apache_avro::schema::RecordField::builder()
- .name("a".to_string())
+ .name("is_it_true".to_string())
.doc(::std::option::Option::None)
.maybe_default(
- <i32 as ::apache_avro::AvroSchemaComponent>::field_default(),
+ <bool as ::apache_avro::AvroSchemaComponent>::field_default(),
)
.aliases(::std::vec::Vec::new())
.schema(
- <i32 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+ <bool as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
named_schemas,
enclosing_namespace,
),
@@ -42,16 +75,21 @@ impl ::apache_avro::AvroSchemaComponent for A {
.build(),
);
fields
+ });
+ fields
+ .extend({
+ let mut fields = ::std::vec::Vec::with_capacity(2usize);
+ fields
.push(
::apache_avro::schema::RecordField::builder()
- .name("b".to_string())
- .doc(::std::option::Option::None)
+ .name("x".to_string())
+ .doc(::std::option::Option::Some("This is X".to_string()))
.maybe_default(
- <String as ::apache_avro::AvroSchemaComponent>::field_default(),
+ <f64 as ::apache_avro::AvroSchemaComponent>::field_default(),
)
.aliases(::std::vec::Vec::new())
.schema(
- <String as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+ <f64 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
named_schemas,
enclosing_namespace,
),
@@ -59,124 +97,39 @@ impl ::apache_avro::AvroSchemaComponent for A {
.build(),
);
fields
- };
- let schema_field_set: ::std::collections::HashSet<_> = schema_fields
- .iter()
- .map(|rf| &rf.name)
- .collect();
- match (&schema_fields.len(), &schema_field_set.len()) {
- (left_val, right_val) => {
- if !(*left_val == *right_val) {
- let kind = ::core::panicking::AssertKind::Eq;
- ::core::panicking::assert_failed(
- kind,
- &*left_val,
- &*right_val,
- ::core::option::Option::Some(
- format_args!(
- "Duplicate field names found: {0:?}", schema_fields,
+ .push(
+ ::apache_avro::schema::RecordField::builder()
+ .name("y".to_string())
+ .doc(::std::option::Option::None)
+ .maybe_default(
+ <f32 as ::apache_avro::AvroSchemaComponent>::field_default(),
+ )
+ .aliases(::std::vec::Vec::new())
+ .schema(
+ <f32 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+ named_schemas,
+ enclosing_namespace,
),
- ),
- );
- }
- }
- };
- let name = ::apache_avro::schema::Name::new("A")
- .expect(
- &::alloc::__export::must_use({
- ::alloc::fmt::format(
- format_args!(
- "Unable to parse struct name for schema {0}", "A",
- ),
- )
- })[..],
- );
- let lookup: ::std::collections::BTreeMap<String, usize> = schema_fields
- .iter()
- .enumerate()
- .map(|(position, field)| (field.name.to_owned(), position))
- .collect();
- ::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema {
- name,
- aliases: ::std::option::Option::None,
- doc: ::std::option::Option::None,
- fields: schema_fields,
- lookup,
- attributes: ::std::collections::BTreeMap::new(),
- })
- }
+ )
+ .build(),
+ );
+ fields
+ });
+ ::apache_avro::schema::Schema::Record(
+ ::apache_avro::schema::RecordSchema::builder()
+ .name(name)
+ .maybe_aliases(::std::option::Option::None)
+ .doc(::std::option::Option::None)
+ .fields(fields)
+ .build(),
+ )
}
}
fn get_record_fields_in_ctxt(
named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>,
enclosing_namespace: ::apache_avro::schema::NamespaceRef,
) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
- ::std::option::Option::Some({
- let mut fields = ::std::vec::Vec::with_capacity(2usize);
- fields
- .push(
- ::apache_avro::schema::RecordField::builder()
- .name("a".to_string())
- .doc(::std::option::Option::None)
- .maybe_default(
- <i32 as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <i32 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
- enclosing_namespace,
- ),
- )
- .build(),
- );
- fields
- .push(
- ::apache_avro::schema::RecordField::builder()
- .name("b".to_string())
- .doc(::std::option::Option::None)
- .maybe_default(
- <String as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <String as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
- enclosing_namespace,
- ),
- )
- .build(),
- );
- fields
- })
- }
- fn field_default() -> ::std::option::Option<::serde_json::Value> {
- ::std::option::Option::None
- }
-}
-#[serde(transparent)]
-struct B {
- a: A,
-}
-#[automatically_derived]
-impl ::apache_avro::AvroSchemaComponent for B {
- fn get_schema_in_ctxt(
- named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>,
- enclosing_namespace: ::apache_avro::schema::NamespaceRef,
- ) -> ::apache_avro::schema::Schema {
- <A as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
- enclosing_namespace,
- )
- }
- fn get_record_fields_in_ctxt(
- named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>,
- enclosing_namespace: ::apache_avro::schema::NamespaceRef,
- ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
- <A as ::apache_avro::AvroSchemaComponent>::get_record_fields_in_ctxt(
- named_schemas,
- enclosing_namespace,
- )
+ None
}
fn field_default() -> ::std::option::Option<::serde_json::Value> {
::std::option::Option::None
diff --git a/avro_derive/tests/expanded/avro_rs_xxx_internally_tagged.rs b/avro_derive/tests/expanded/avro_rs_xxx_internally_tagged.rs
index b23413b..85b18fd 100644
--- a/avro_derive/tests/expanded/avro_rs_xxx_internally_tagged.rs
+++ b/avro_derive/tests/expanded/avro_rs_xxx_internally_tagged.rs
@@ -18,17 +18,18 @@
use apache_avro::AvroSchema;
#[derive(AvroSchema)]
-struct A {
- a: i32,
- b: String,
+#[avro(repr = "record_internally_tagged")]
+#[serde(tag = "type")]
+enum Abc {
+ A,
+ B(bool),
+ D {},
+ E {
+ is_it_true: bool,
+ },
+ F {
+ #[avro(doc = "This is X")]
+ x: f64,
+ y: f32,
+ },
}
-
-#[derive(AvroSchema)]
-#[serde(transparent)]
-struct B {
- a: A,
-}
-
-// #[derive(AvroSchema)]
-// #[serde(transparent)]
-// struct C(A);
diff --git a/avro_derive/tests/expanded/avro_rs_xxx_tag_content.expanded.rs b/avro_derive/tests/expanded/avro_rs_xxx_tag_content.expanded.rs
index d75f321..49dc6d1 100644
--- a/avro_derive/tests/expanded/avro_rs_xxx_tag_content.expanded.rs
+++ b/avro_derive/tests/expanded/avro_rs_xxx_tag_content.expanded.rs
@@ -1,19 +1,24 @@
use apache_avro::AvroSchema;
-struct A {
- a: i32,
- b: String,
+#[avro(repr = "record_tag_content")]
+#[serde(tag = "type", content = "value")]
+enum Abc {
+ A,
+ B(bool),
+ D {},
+ E { is_it_true: bool },
+ F { #[avro(doc = "This is X")] x: f64, y: f32 },
}
#[automatically_derived]
-impl ::apache_avro::AvroSchemaComponent for A {
+impl ::apache_avro::AvroSchemaComponent for Abc {
fn get_schema_in_ctxt(
named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>,
enclosing_namespace: ::apache_avro::schema::NamespaceRef,
) -> ::apache_avro::schema::Schema {
let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
- "A",
+ "Abc",
enclosing_namespace,
)
- .expect("Unable to parse schema name A");
+ .expect("Unable to parse schema name Abc");
if named_schemas.contains(&name) {
::apache_avro::schema::Schema::Ref {
name,
@@ -21,162 +26,219 @@ impl ::apache_avro::AvroSchemaComponent for A {
} else {
let enclosing_namespace = name.namespace();
named_schemas.insert(name.clone());
- {
- let schema_fields = {
- let mut fields = ::std::vec::Vec::with_capacity(2usize);
- fields
- .push(
- ::apache_avro::schema::RecordField::builder()
- .name("a".to_string())
- .doc(::std::option::Option::None)
- .maybe_default(
- <i32 as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <i32 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
+ let mut builder = ::apache_avro::schema::UnionSchema::builder();
+ builder
+ .variant_ignore_duplicates(::apache_avro::schema::Schema::Null)
+ .expect("Unions cannot have duplicates");
+ builder
+ .variant_ignore_duplicates(
+ <bool as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+ named_schemas,
+ enclosing_namespace,
+ ),
+ )
+ .expect("Unions cannot have duplicates");
+ builder
+ .variant_ignore_duplicates(
+ ::apache_avro::schema::Schema::Record(
+ ::apache_avro::schema::RecordSchema::builder()
+ .name(
+ ::apache_avro::schema::Name::new_with_enclosing_namespace(
+ "D",
enclosing_namespace,
+ )
+ .expect(
+ &::alloc::__export::must_use({
+ ::alloc::fmt::format(
+ format_args!(
+ "Unable to parse variant record name for schema {0}", "D",
+ ),
+ )
+ })[..],
),
- )
- .build(),
- );
- fields
- .push(
- ::apache_avro::schema::RecordField::builder()
- .name("b".to_string())
- .doc(::std::option::Option::None)
- .maybe_default(
- <String as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <String as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
+ )
+ .fields({
+ let mut fields = ::std::vec::Vec::with_capacity(0usize);
+ fields
+ })
+ .build(),
+ ),
+ )
+ .expect("Unions cannot have duplicates");
+ builder
+ .variant_ignore_duplicates(
+ ::apache_avro::schema::Schema::Record(
+ ::apache_avro::schema::RecordSchema::builder()
+ .name(
+ ::apache_avro::schema::Name::new_with_enclosing_namespace(
+ "E",
enclosing_namespace,
+ )
+ .expect(
+ &::alloc::__export::must_use({
+ ::alloc::fmt::format(
+ format_args!(
+ "Unable to parse variant record name for schema {0}", "E",
+ ),
+ )
+ })[..],
),
- )
- .build(),
- );
- fields
- };
- let schema_field_set: ::std::collections::HashSet<_> = schema_fields
- .iter()
- .map(|rf| &rf.name)
- .collect();
- match (&schema_fields.len(), &schema_field_set.len()) {
- (left_val, right_val) => {
- if !(*left_val == *right_val) {
- let kind = ::core::panicking::AssertKind::Eq;
- ::core::panicking::assert_failed(
- kind,
- &*left_val,
- &*right_val,
- ::core::option::Option::Some(
- format_args!(
- "Duplicate field names found: {0:?}", schema_fields,
+ )
+ .fields({
+ let mut fields = ::std::vec::Vec::with_capacity(1usize);
+ fields
+ .push(
+ ::apache_avro::schema::RecordField::builder()
+ .name("is_it_true".to_string())
+ .doc(::std::option::Option::None)
+ .maybe_default(
+ <bool as ::apache_avro::AvroSchemaComponent>::field_default(),
+ )
+ .aliases(::std::vec::Vec::new())
+ .schema(
+ <bool as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+ named_schemas,
+ enclosing_namespace,
+ ),
+ )
+ .build(),
+ );
+ fields
+ })
+ .build(),
+ ),
+ )
+ .expect("Unions cannot have duplicates");
+ builder
+ .variant_ignore_duplicates(
+ ::apache_avro::schema::Schema::Record(
+ ::apache_avro::schema::RecordSchema::builder()
+ .name(
+ ::apache_avro::schema::Name::new_with_enclosing_namespace(
+ "F",
+ enclosing_namespace,
+ )
+ .expect(
+ &::alloc::__export::must_use({
+ ::alloc::fmt::format(
+ format_args!(
+ "Unable to parse variant record name for schema {0}", "F",
+ ),
+ )
+ })[..],
),
- ),
- );
- }
- }
- };
- let name = ::apache_avro::schema::Name::new("A")
- .expect(
- &::alloc::__export::must_use({
- ::alloc::fmt::format(
- format_args!(
- "Unable to parse struct name for schema {0}", "A",
- ),
)
- })[..],
- );
- let lookup: ::std::collections::BTreeMap<String, usize> = schema_fields
- .iter()
- .enumerate()
- .map(|(position, field)| (field.name.to_owned(), position))
- .collect();
- ::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema {
- name,
- aliases: ::std::option::Option::None,
- doc: ::std::option::Option::None,
- fields: schema_fields,
- lookup,
- attributes: ::std::collections::BTreeMap::new(),
- })
- }
- }
- }
- fn get_record_fields_in_ctxt(
- named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>,
- enclosing_namespace: ::apache_avro::schema::NamespaceRef,
- ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
- ::std::option::Option::Some({
- let mut fields = ::std::vec::Vec::with_capacity(2usize);
+ .fields({
+ let mut fields = ::std::vec::Vec::with_capacity(2usize);
+ fields
+ .push(
+ ::apache_avro::schema::RecordField::builder()
+ .name("x".to_string())
+ .doc(::std::option::Option::Some("This is X".to_string()))
+ .maybe_default(
+ <f64 as ::apache_avro::AvroSchemaComponent>::field_default(),
+ )
+ .aliases(::std::vec::Vec::new())
+ .schema(
+ <f64 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+ named_schemas,
+ enclosing_namespace,
+ ),
+ )
+ .build(),
+ );
+ fields
+ .push(
+ ::apache_avro::schema::RecordField::builder()
+ .name("y".to_string())
+ .doc(::std::option::Option::None)
+ .maybe_default(
+ <f32 as ::apache_avro::AvroSchemaComponent>::field_default(),
+ )
+ .aliases(::std::vec::Vec::new())
+ .schema(
+ <f32 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+ named_schemas,
+ enclosing_namespace,
+ ),
+ )
+ .build(),
+ );
+ fields
+ })
+ .build(),
+ ),
+ )
+ .expect("Unions cannot have duplicates");
+ let content_contains_null = builder
+ .contains(&::apache_avro::schema::Schema::Null);
+ let content_schema = ::apache_avro::schema::Schema::Union(builder.build());
+ let tag_name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
+ "type",
+ enclosing_namespace,
+ )
+ .expect(
+ &::alloc::__export::must_use({
+ ::alloc::fmt::format(
+ format_args!(
+ "Unable to parse name for schema tag {0}", "type",
+ ),
+ )
+ })[..],
+ );
+ let tag_schema = ::apache_avro::schema::Schema::r#enum(
+ tag_name,
+ ::alloc::boxed::box_assume_init_into_vec_unsafe(
+ ::alloc::intrinsics::write_box_via_move(
+ ::alloc::boxed::Box::new_uninit(),
+ [
+ "A".to_owned(),
+ "B".to_owned(),
+ "D".to_owned(),
+ "E".to_owned(),
+ "F".to_owned(),
+ ],
+ ),
+ ),
+ )
+ .build();
+ let mut fields = ::std::vec::Vec::with_capacity(2);
fields
.push(
::apache_avro::schema::RecordField::builder()
- .name("a".to_string())
- .doc(::std::option::Option::None)
- .maybe_default(
- <i32 as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <i32 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
- enclosing_namespace,
- ),
- )
+ .name("type")
+ .schema(tag_schema)
.build(),
);
fields
.push(
::apache_avro::schema::RecordField::builder()
- .name("b".to_string())
- .doc(::std::option::Option::None)
+ .name("value")
+ .schema(content_schema)
.maybe_default(
- <String as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <String as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
- enclosing_namespace,
- ),
+ if content_contains_null {
+ Some(::serde_json::Value::Null)
+ } else {
+ None
+ },
)
.build(),
);
- fields
- })
- }
- fn field_default() -> ::std::option::Option<::serde_json::Value> {
- ::std::option::Option::None
- }
-}
-#[serde(transparent)]
-struct B {
- a: A,
-}
-#[automatically_derived]
-impl ::apache_avro::AvroSchemaComponent for B {
- fn get_schema_in_ctxt(
- named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>,
- enclosing_namespace: ::apache_avro::schema::NamespaceRef,
- ) -> ::apache_avro::schema::Schema {
- <A as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
- enclosing_namespace,
- )
+ ::apache_avro::schema::Schema::Record(
+ ::apache_avro::schema::RecordSchema::builder()
+ .name(name)
+ .maybe_aliases(::std::option::Option::None)
+ .doc(::std::option::Option::None)
+ .fields(fields)
+ .build(),
+ )
+ }
}
fn get_record_fields_in_ctxt(
named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>,
enclosing_namespace: ::apache_avro::schema::NamespaceRef,
) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
- <A as ::apache_avro::AvroSchemaComponent>::get_record_fields_in_ctxt(
- named_schemas,
- enclosing_namespace,
- )
+ None
}
fn field_default() -> ::std::option::Option<::serde_json::Value> {
::std::option::Option::None
diff --git a/avro_derive/tests/expanded/avro_rs_xxx_tag_content.rs b/avro_derive/tests/expanded/avro_rs_xxx_tag_content.rs
index b23413b..f6fb040 100644
--- a/avro_derive/tests/expanded/avro_rs_xxx_tag_content.rs
+++ b/avro_derive/tests/expanded/avro_rs_xxx_tag_content.rs
@@ -18,17 +18,18 @@
use apache_avro::AvroSchema;
#[derive(AvroSchema)]
-struct A {
- a: i32,
- b: String,
+#[avro(repr = "record_tag_content")]
+#[serde(tag = "type", content = "value")]
+enum Abc {
+ A,
+ B(bool),
+ D {},
+ E {
+ is_it_true: bool,
+ },
+ F {
+ #[avro(doc = "This is X")]
+ x: f64,
+ y: f32,
+ },
}
-
-#[derive(AvroSchema)]
-#[serde(transparent)]
-struct B {
- a: A,
-}
-
-// #[derive(AvroSchema)]
-// #[serde(transparent)]
-// struct C(A);
diff --git a/avro_derive/tests/expanded/avro_rs_xxx_union_of_records.expanded.rs b/avro_derive/tests/expanded/avro_rs_xxx_union_of_records.expanded.rs
index d75f321..ccad318 100644
--- a/avro_derive/tests/expanded/avro_rs_xxx_union_of_records.expanded.rs
+++ b/avro_derive/tests/expanded/avro_rs_xxx_union_of_records.expanded.rs
@@ -1,19 +1,23 @@
use apache_avro::AvroSchema;
-struct A {
- a: i32,
- b: String,
+#[avro(repr = "union_of_records")]
+enum Abc {
+ A,
+ B(bool),
+ D {},
+ E { is_it_true: bool },
+ F { #[avro(doc = "This is X")] x: f64, y: f32 },
}
#[automatically_derived]
-impl ::apache_avro::AvroSchemaComponent for A {
+impl ::apache_avro::AvroSchemaComponent for Abc {
fn get_schema_in_ctxt(
named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>,
enclosing_namespace: ::apache_avro::schema::NamespaceRef,
) -> ::apache_avro::schema::Schema {
let name = ::apache_avro::schema::Name::new_with_enclosing_namespace(
- "A",
+ "Abc",
enclosing_namespace,
)
- .expect("Unable to parse schema name A");
+ .expect("Unable to parse schema name Abc");
if named_schemas.contains(&name) {
::apache_avro::schema::Schema::Ref {
name,
@@ -21,162 +25,229 @@ impl ::apache_avro::AvroSchemaComponent for A {
} else {
let enclosing_namespace = name.namespace();
named_schemas.insert(name.clone());
- {
- let schema_fields = {
- let mut fields = ::std::vec::Vec::with_capacity(2usize);
- fields
- .push(
- ::apache_avro::schema::RecordField::builder()
- .name("a".to_string())
- .doc(::std::option::Option::None)
- .maybe_default(
- <i32 as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <i32 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
+ let mut builder = ::apache_avro::schema::UnionSchema::builder();
+ builder
+ .variant(
+ ::apache_avro::schema::Schema::Record(
+ ::apache_avro::schema::RecordSchema::builder()
+ .name(
+ ::apache_avro::schema::Name::new_with_enclosing_namespace(
+ "A",
enclosing_namespace,
+ )
+ .expect(
+ &::alloc::__export::must_use({
+ ::alloc::fmt::format(
+ format_args!(
+ "Unable to parse variant record name for schema {0}", "A",
+ ),
+ )
+ })[..],
),
- )
- .build(),
- );
- fields
- .push(
- ::apache_avro::schema::RecordField::builder()
- .name("b".to_string())
- .doc(::std::option::Option::None)
- .maybe_default(
- <String as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <String as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
+ )
+ .build(),
+ ),
+ )
+ .expect("Duplicate Schema found");
+ builder
+ .variant(
+ ::apache_avro::schema::Schema::Record(
+ ::apache_avro::schema::RecordSchema::builder()
+ .name(
+ ::apache_avro::schema::Name::new_with_enclosing_namespace(
+ "B",
enclosing_namespace,
+ )
+ .expect(
+ &::alloc::__export::must_use({
+ ::alloc::fmt::format(
+ format_args!(
+ "Unable to parse variant record name for schema {0}", "B",
+ ),
+ )
+ })[..],
),
- )
- .build(),
- );
- fields
- };
- let schema_field_set: ::std::collections::HashSet<_> = schema_fields
- .iter()
- .map(|rf| &rf.name)
- .collect();
- match (&schema_fields.len(), &schema_field_set.len()) {
- (left_val, right_val) => {
- if !(*left_val == *right_val) {
- let kind = ::core::panicking::AssertKind::Eq;
- ::core::panicking::assert_failed(
- kind,
- &*left_val,
- &*right_val,
- ::core::option::Option::Some(
- format_args!(
- "Duplicate field names found: {0:?}", schema_fields,
+ )
+ .fields(
+ ::alloc::boxed::box_assume_init_into_vec_unsafe(
+ ::alloc::intrinsics::write_box_via_move(
+ ::alloc::boxed::Box::new_uninit(),
+ [
+ ::apache_avro::schema::RecordField::builder()
+ .name("field_0".to_string())
+ .doc(::std::option::Option::None)
+ .maybe_default(
+ <bool as ::apache_avro::AvroSchemaComponent>::field_default(),
+ )
+ .aliases(::std::vec::Vec::new())
+ .schema(
+ <bool as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+ named_schemas,
+ enclosing_namespace,
+ ),
+ )
+ .build(),
+ ],
),
),
- );
- }
- }
- };
- let name = ::apache_avro::schema::Name::new("A")
- .expect(
- &::alloc::__export::must_use({
- ::alloc::fmt::format(
- format_args!(
- "Unable to parse struct name for schema {0}", "A",
- ),
)
- })[..],
- );
- let lookup: ::std::collections::BTreeMap<String, usize> = schema_fields
- .iter()
- .enumerate()
- .map(|(position, field)| (field.name.to_owned(), position))
- .collect();
- ::apache_avro::schema::Schema::Record(::apache_avro::schema::RecordSchema {
- name,
- aliases: ::std::option::Option::None,
- doc: ::std::option::Option::None,
- fields: schema_fields,
- lookup,
- attributes: ::std::collections::BTreeMap::new(),
- })
- }
+ .attributes(
+ [
+ (
+ "org.apache.avro.rust.tuple".to_string(),
+ ::serde_json::value::Value::Bool(true),
+ ),
+ (
+ "org.apache.avro.rust.union_of_records".to_string(),
+ ::serde_json::value::Value::Bool(true),
+ ),
+ ]
+ .into(),
+ )
+ .build(),
+ ),
+ )
+ .expect("Duplicate Schema found");
+ builder
+ .variant(
+ ::apache_avro::schema::Schema::Record(
+ ::apache_avro::schema::RecordSchema::builder()
+ .name(
+ ::apache_avro::schema::Name::new_with_enclosing_namespace(
+ "D",
+ enclosing_namespace,
+ )
+ .expect(
+ &::alloc::__export::must_use({
+ ::alloc::fmt::format(
+ format_args!(
+ "Unable to parse variant record name for schema {0}", "D",
+ ),
+ )
+ })[..],
+ ),
+ )
+ .fields({
+ let mut fields = ::std::vec::Vec::with_capacity(0usize);
+ fields
+ })
+ .build(),
+ ),
+ )
+ .expect("Duplicate Schema found");
+ builder
+ .variant(
+ ::apache_avro::schema::Schema::Record(
+ ::apache_avro::schema::RecordSchema::builder()
+ .name(
+ ::apache_avro::schema::Name::new_with_enclosing_namespace(
+ "E",
+ enclosing_namespace,
+ )
+ .expect(
+ &::alloc::__export::must_use({
+ ::alloc::fmt::format(
+ format_args!(
+ "Unable to parse variant record name for schema {0}", "E",
+ ),
+ )
+ })[..],
+ ),
+ )
+ .fields({
+ let mut fields = ::std::vec::Vec::with_capacity(1usize);
+ fields
+ .push(
+ ::apache_avro::schema::RecordField::builder()
+ .name("is_it_true".to_string())
+ .doc(::std::option::Option::None)
+ .maybe_default(
+ <bool as ::apache_avro::AvroSchemaComponent>::field_default(),
+ )
+ .aliases(::std::vec::Vec::new())
+ .schema(
+ <bool as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+ named_schemas,
+ enclosing_namespace,
+ ),
+ )
+ .build(),
+ );
+ fields
+ })
+ .build(),
+ ),
+ )
+ .expect("Duplicate Schema found");
+ builder
+ .variant(
+ ::apache_avro::schema::Schema::Record(
+ ::apache_avro::schema::RecordSchema::builder()
+ .name(
+ ::apache_avro::schema::Name::new_with_enclosing_namespace(
+ "F",
+ enclosing_namespace,
+ )
+ .expect(
+ &::alloc::__export::must_use({
+ ::alloc::fmt::format(
+ format_args!(
+ "Unable to parse variant record name for schema {0}", "F",
+ ),
+ )
+ })[..],
+ ),
+ )
+ .fields({
+ let mut fields = ::std::vec::Vec::with_capacity(2usize);
+ fields
+ .push(
+ ::apache_avro::schema::RecordField::builder()
+ .name("x".to_string())
+ .doc(::std::option::Option::Some("This is X".to_string()))
+ .maybe_default(
+ <f64 as ::apache_avro::AvroSchemaComponent>::field_default(),
+ )
+ .aliases(::std::vec::Vec::new())
+ .schema(
+ <f64 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+ named_schemas,
+ enclosing_namespace,
+ ),
+ )
+ .build(),
+ );
+ fields
+ .push(
+ ::apache_avro::schema::RecordField::builder()
+ .name("y".to_string())
+ .doc(::std::option::Option::None)
+ .maybe_default(
+ <f32 as ::apache_avro::AvroSchemaComponent>::field_default(),
+ )
+ .aliases(::std::vec::Vec::new())
+ .schema(
+ <f32 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
+ named_schemas,
+ enclosing_namespace,
+ ),
+ )
+ .build(),
+ );
+ fields
+ })
+ .build(),
+ ),
+ )
+ .expect("Duplicate Schema found");
+ ::apache_avro::schema::Schema::Union(builder.build())
}
}
fn get_record_fields_in_ctxt(
named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>,
enclosing_namespace: ::apache_avro::schema::NamespaceRef,
) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
- ::std::option::Option::Some({
- let mut fields = ::std::vec::Vec::with_capacity(2usize);
- fields
- .push(
- ::apache_avro::schema::RecordField::builder()
- .name("a".to_string())
- .doc(::std::option::Option::None)
- .maybe_default(
- <i32 as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <i32 as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
- enclosing_namespace,
- ),
- )
- .build(),
- );
- fields
- .push(
- ::apache_avro::schema::RecordField::builder()
- .name("b".to_string())
- .doc(::std::option::Option::None)
- .maybe_default(
- <String as ::apache_avro::AvroSchemaComponent>::field_default(),
- )
- .aliases(::std::vec::Vec::new())
- .schema(
- <String as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
- enclosing_namespace,
- ),
- )
- .build(),
- );
- fields
- })
- }
- fn field_default() -> ::std::option::Option<::serde_json::Value> {
- ::std::option::Option::None
- }
-}
-#[serde(transparent)]
-struct B {
- a: A,
-}
-#[automatically_derived]
-impl ::apache_avro::AvroSchemaComponent for B {
- fn get_schema_in_ctxt(
- named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>,
- enclosing_namespace: ::apache_avro::schema::NamespaceRef,
- ) -> ::apache_avro::schema::Schema {
- <A as ::apache_avro::AvroSchemaComponent>::get_schema_in_ctxt(
- named_schemas,
- enclosing_namespace,
- )
- }
- fn get_record_fields_in_ctxt(
- named_schemas: &mut ::std::collections::HashSet<::apache_avro::schema::Name>,
- enclosing_namespace: ::apache_avro::schema::NamespaceRef,
- ) -> ::std::option::Option<::std::vec::Vec<::apache_avro::schema::RecordField>> {
- <A as ::apache_avro::AvroSchemaComponent>::get_record_fields_in_ctxt(
- named_schemas,
- enclosing_namespace,
- )
+ None
}
fn field_default() -> ::std::option::Option<::serde_json::Value> {
::std::option::Option::None
diff --git a/avro_derive/tests/expanded/avro_rs_xxx_union_of_records.rs b/avro_derive/tests/expanded/avro_rs_xxx_union_of_records.rs
index b23413b..a46e709 100644
--- a/avro_derive/tests/expanded/avro_rs_xxx_union_of_records.rs
+++ b/avro_derive/tests/expanded/avro_rs_xxx_union_of_records.rs
@@ -18,17 +18,17 @@
use apache_avro::AvroSchema;
#[derive(AvroSchema)]
-struct A {
- a: i32,
- b: String,
+#[avro(repr = "union_of_records")]
+enum Abc {
+ A,
+ B(bool),
+ D {},
+ E {
+ is_it_true: bool,
+ },
+ F {
+ #[avro(doc = "This is X")]
+ x: f64,
+ y: f32,
+ },
}
-
-#[derive(AvroSchema)]
-#[serde(transparent)]
-struct B {
- a: A,
-}
-
-// #[derive(AvroSchema)]
-// #[serde(transparent)]
-// struct C(A);
diff --git a/avro_derive/tests/expanded/mod.rs b/avro_derive/tests/expanded/mod.rs
index 80b8b23..ed2c338 100644
--- a/avro_derive/tests/expanded/mod.rs
+++ b/avro_derive/tests/expanded/mod.rs
@@ -27,9 +27,9 @@ mod avro_rs_501_basic;
mod avro_rs_501_namespace;
mod avro_rs_501_reference;
mod avro_rs_501_struct_with_optional;
+mod avro_rs_xxx_bare_union;
+mod avro_rs_xxx_internally_tagged;
mod avro_rs_xxx_serde_from_into;
mod avro_rs_xxx_serde_transparent;
-// mod avro_rs_xxx_bare_union;
-mod avro_rs_xxx_internally_tagged;
mod avro_rs_xxx_tag_content;
mod avro_rs_xxx_union_of_records;
diff --git a/avro_derive/tests/ui/avro_rs_401_tuple.stderr b/avro_derive/tests/ui/avro_rs_401_tuple.stderr
deleted file mode 100644
index ffc0378..0000000
--- a/avro_derive/tests/ui/avro_rs_401_tuple.stderr
+++ /dev/null
@@ -1,5 +0,0 @@
-error: AvroSchema: derive does not support tuples
- --> tests/ui/avro_rs_401_tuple.rs:22:8
- |
-22 | a: (i32, String),
- | ^^^^^^^^^^^^^
diff --git a/avro_derive/tests/ui/avro_rs_401_tuple.rs b/avro_derive/tests/ui/avro_rs_xxx_bare_union_struct_variant.rs
similarity index 96%
copy from avro_derive/tests/ui/avro_rs_401_tuple.rs
copy to avro_derive/tests/ui/avro_rs_xxx_bare_union_struct_variant.rs
index cf00214..0dc8b0a 100644
--- a/avro_derive/tests/ui/avro_rs_401_tuple.rs
+++ b/avro_derive/tests/ui/avro_rs_xxx_bare_union_struct_variant.rs
@@ -18,8 +18,9 @@
use apache_avro::AvroSchema;
#[derive(AvroSchema)]
-struct Foo {
- a: (i32, String),
+#[serde(untagged)]
+enum D {
+ A{}
}
pub fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_xxx_bare_union_struct_variant.stderr b/avro_derive/tests/ui/avro_rs_xxx_bare_union_struct_variant.stderr
new file mode 100644
index 0000000..592afa8
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_bare_union_struct_variant.stderr
@@ -0,0 +1,5 @@
+error: AvroSchema: Struct variants are not supported for untagged structs
+ --> tests/ui/avro_rs_xxx_bare_union_struct_variant.rs:23:6
+ |
+23 | A{}
+ | ^^
diff --git a/avro_derive/tests/ui/avro_rs_401_tuple.rs b/avro_derive/tests/ui/avro_rs_xxx_untagged_newtype_docs.rs
similarity index 89%
rename from avro_derive/tests/ui/avro_rs_401_tuple.rs
rename to avro_derive/tests/ui/avro_rs_xxx_untagged_newtype_docs.rs
index cf00214..e9da9c5 100644
--- a/avro_derive/tests/ui/avro_rs_401_tuple.rs
+++ b/avro_derive/tests/ui/avro_rs_xxx_untagged_newtype_docs.rs
@@ -18,8 +18,12 @@
use apache_avro::AvroSchema;
#[derive(AvroSchema)]
-struct Foo {
- a: (i32, String),
+#[serde(untagged)]
+enum D {
+ A(
+ #[avro(doc = "This is a string")]
+ String
+ )
}
pub fn main() {}
diff --git a/avro_derive/tests/ui/avro_rs_xxx_untagged_newtype_docs.stderr b/avro_derive/tests/ui/avro_rs_xxx_untagged_newtype_docs.stderr
new file mode 100644
index 0000000..48ff5c4
--- /dev/null
+++ b/avro_derive/tests/ui/avro_rs_xxx_untagged_newtype_docs.stderr
@@ -0,0 +1,6 @@
+error: AvroSchema: `#[avro(doc = "..")]` only works on newtype variants when the enum uses `#[avro(repr = "union_of_records")`
+ --> tests/ui/avro_rs_xxx_untagged_newtype_docs.rs:24:9
+ |
+24 | / #[avro(doc = "This is a string")]
+25 | | String
+ | |______________^