martin-g commented on code in PR #377:
URL: https://github.com/apache/avro-rs/pull/377#discussion_r2649624377


##########
avro_derive/src/attributes/avro.rs:
##########
@@ -0,0 +1,40 @@
+use crate::case::RenameRule;
+

Review Comment:
   Let's add some rustdoc for the `pub`lic structs and their fields.



##########
avro_derive/src/attributes/serde.rs:
##########
@@ -0,0 +1,298 @@
+//! Attribute parsing for Serde attributes
+//!
+//! # Serde attributes
+//! This module only parses the minimal amount to be able to read Serde 
attributes. This means
+//! most attributes are decoded as an `Option<String>` as the actual content 
is not relevant.
+//! Attributes which are not needed by the derive are prefixed with a `_` as 
we can't ignore them
+//! (this would be a compile error).
+//!
+//! If Serde adds new attributes they need to be added here too.
+
+use darling::{FromAttributes, FromMeta};
+use syn::Expr;
+
+use crate::case::RenameRule;
+
+// from_expr is used for `rename_all = ".."`
+// FromMeta is used for `rename_all(serialize = "..", ..)`
+#[derive(Debug, Default, FromMeta, PartialEq, Eq)]
+#[darling(from_expr = RenameAll::from_expr)]
+pub struct RenameAll {
+    #[darling(default)]
+    pub serialize: RenameRule,
+    #[darling(default)]
+    pub deserialize: RenameRule,
+}
+
+impl From<RenameRule> for RenameAll {
+    fn from(value: RenameRule) -> Self {
+        Self {
+            serialize: value,
+            deserialize: value,
+        }
+    }
+}
+
+impl RenameAll {
+    fn from_expr(expr: &Expr) -> darling::Result<Self> {
+        let Expr::Lit(lit) = expr else {
+            return Err(darling::Error::custom("Expected a string or a 
tuple!"));
+        };
+        let rule = RenameRule::from_value(&lit.lit)?;
+        Ok(RenameAll::from(rule))
+    }
+}
+
+#[derive(Debug, FromMeta, PartialEq)]
+#[darling(from_expr = |expr| Ok(SerdeDefault::Expr(expr.clone())))]
+pub enum SerdeDefault {
+    #[darling(word, skip)]
+    UseTrait,
+    Expr(Expr),
+}
+
+/// All Serde attributes that a container can have.
+#[derive(Debug, FromAttributes)]
+#[darling(attributes(serde))]
+pub struct ContainerAttributes {
+    #[darling(rename = "rename")]
+    /// Rename this container.
+    pub _rename: Option<String>,
+    /// Rename all the fields (if this is a struct) or variants (if this is an 
enum) according to the given case convention.
+    #[darling(default)]
+    pub rename_all: RenameAll,
+    /// Rename all the fields of the struct variants in this enum.
+    #[darling(default, rename = "rename_all_fields")]
+    pub _rename_all_fields: RenameAll,
+    /// Error when encountering unknown fields when deserialising.
+    #[darling(default, rename = "deny_unknown_fields")]
+    pub _deny_unknown_fields: bool,
+    /// Add/expect a tag during serialisation.
+    ///
+    /// When used on a struct, this adds an extra field which is not in the 
schema definition.
+    /// When used on an enum, serde transforms it into a struct which does not 
match the schema definition.
+    pub tag: Option<String>,
+    /// Put the content in a field with this name.
+    pub content: Option<String>,
+    /// This makes the enum transparent, (de)serializing based on the variant 
directly.
+    ///
+    /// This does not match the schema definition.
+    #[darling(default)]
+    pub untagged: bool,
+    /// Add a bound to the Serialize/Deserialize trait.
+    #[darling(default, rename = "bound")]
+    pub _bound: Option<String>,
+    /// When deserializing, any missing fields should be filled in from the 
struct's implementation of `Default`.
+    #[darling(rename = "default")]
+    pub _default: Option<SerdeDefault>,
+    /// This type is the serde implementation for a "remote" type.
+    ///
+    /// This makes the (de)serialisation use/return a different type.
+    pub remote: Option<String>,
+    /// Directly use the inner type for (de)serialisation.
+    #[darling(default)]
+    pub transparent: bool,
+    /// Deserialize using the given type and then convert to this type with 
`From`.
+    #[darling(default, rename = "from")]
+    pub _from: Option<String>,
+    /// Deserialize using the given type and then convert to this type with 
`TryFrom`.
+    #[darling(default, rename = "try_from")]
+    pub _try_from: Option<String>,
+    /// Convert this type to the given type using `Into` and then serialize 
using the given type.
+    #[darling(default, rename = "into")]
+    pub _into: Option<String>,
+    /// Use the Serde API at this path.
+    #[darling(default, rename = "crate")]
+    pub _crate: Option<String>,
+    /// Custom error text.
+    #[darling(default, rename = "expecting")]
+    pub _expecting: Option<String>,
+    /// This does something with tags, which are incompatible.
+    #[darling(default)]
+    pub variant_identifier: bool,
+    /// This does something with tags,  `` which are incompatible.

Review Comment:
   What is the purpose of the empty backticks here ?



##########
avro_derive/src/attributes/mod.rs:
##########
@@ -0,0 +1,202 @@
+use darling::FromAttributes;
+use proc_macro2::Span;
+use syn::Attribute;
+
+use crate::{case::RenameRule, darling_to_syn};
+
+pub mod avro;
+pub mod serde;
+
+#[derive(Default)]
+pub struct NamedTypeOptions {
+    pub name: Option<String>,
+    pub namespace: Option<String>,
+    pub doc: Option<String>,
+    pub alias: Vec<String>,
+    pub rename_all: RenameRule,
+}
+
+impl NamedTypeOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, 
Vec<syn::Error>> {
+        let avro =
+            
avro::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde =
+            
serde::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.tag.is_some()
+            || serde.content.is_some()
+            || serde.untagged
+            || serde.variant_identifier
+            || serde.field_identifier
+        {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support changing the tagging Serde 
generates (`tag`, `content`, `untagged`, `variant_identifier`, 
`field_identifier`)",
+            ));
+        }
+        if serde.remote.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `remote` 
attribute",
+            ));
+        }
+        if serde.transparent {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support Serde transparent",
+            ))
+        }
+        if serde.rename_all.deserialize != serde.rename_all.serialize {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support different rename rules for 
serializing and deserializing (`rename_all(serialize = \"..\", deserialize = 
\"..\")`)"
+            ))
+        }
+
+        // Check for conflicts between Serde and Avro
+        if avro.rename_all != RenameRule::None && serde.rename_all.serialize 
!= avro.rename_all {
+            errors.push(syn::Error::new(
+                span,
+                "#[avro(rename_all = \"..\")] must match #[serde(rename_all = 
\"..\")], it's also deprecated. Please use only `#[serde(rename_all = 
\"..\")]`",
+            ))
+        }
+
+        if !errors.is_empty() {
+            return Err(errors);
+        }
+
+        Ok(Self {
+            name: avro.name,
+            namespace: avro.namespace,
+            doc: avro.doc,
+            alias: avro.alias,
+            rename_all: serde.rename_all.serialize,
+        })
+    }
+}
+
+pub struct FieldOptions {
+    pub doc: Option<String>,
+    pub default: Option<String>,
+    pub alias: Vec<String>,
+    pub rename: Option<String>,
+    pub skip: bool,
+    pub flatten: bool,
+}
+
+impl FieldOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, 
Vec<syn::Error>> {
+        let avro = 
avro::FieldAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde = 
serde::FieldAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.getter.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `getter` 
attribute",
+            ));
+        }
+
+        // Check for conflicts between Serde and Avro
+        if avro.skip && !serde.skip {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(skip)]` requires `#[serde(skip)]`, it's also 
deprecated. Please use only `#[serde(skip)]`"
+            ));
+        }
+        if avro.flatten && !serde.flatten {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(flatten)]` requires `#[serde(flatten)]`, it's also 
deprecated. Please use only `#[serde(flatten)]`"
+            ));
+        }
+        // TODO: rename and alias checking can be relaxed with a more complex 
check, would require the field name
+        if avro.rename.is_some() && serde.rename != avro.rename {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(rename = \"..\")]` must match `#[serde(rename = 
\"..\")]`, it's also deprecated. Please use only `#[serde(rename  = \"..\")]`"
+            ));
+        }
+        if serde.alias != avro.alias {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(alias = \"..\")]` must match `#[serde(alias = 
\"..\")]`, it's also deprecated. Please use only `#[serde(alias  = \"..\")]`"
+            ));
+        }
+        if serde.skip_serializing && serde.skip_deserializing {
+            errors.push(syn::Error::new(
+                span,
+                "Use `#[serde(skip)]` instead of `#[serde(skip_serializing, 
skip_deserializing)]`",
+            ));
+
+            // Don't want to suggest default for skip_serializing as it's not 
needed for skip
+            return Err(errors);
+        }
+        if ((serde.skip_serializing) || serde.skip_serializing_if.is_some())
+            && avro.default.is_none()
+        {
+            errors.push(syn::Error::new(
+                span,
+                "`#[serde(skip_serializing)]` and 
`#[serde(skip_serializing_if)]` require `#[avro(default = \"..\")]"

Review Comment:
   ```suggestion
                   "`#[serde(skip_serializing)]` and 
`#[serde(skip_serializing_if)]` require `#[avro(default = \"..\")]`"
   ```



##########
avro_derive/src/case.rs:
##########
@@ -47,6 +51,15 @@ pub enum RenameRule {
     ScreamingKebabCase,
 }
 
+impl FromMeta for RenameRule {
+    fn from_value(value: &Lit) -> darling::Result<Self> {
+        let Lit::Str(litstr) = value else {
+            panic!("Only expected a string");

Review Comment:
   ```suggestion
               return Err(darling::Error::unexpected_lit_type(value));
   
   ```



##########
avro_derive/src/attributes/serde.rs:
##########
@@ -0,0 +1,298 @@
+//! Attribute parsing for Serde attributes
+//!
+//! # Serde attributes
+//! This module only parses the minimal amount to be able to read Serde 
attributes. This means
+//! most attributes are decoded as an `Option<String>` as the actual content 
is not relevant.
+//! Attributes which are not needed by the derive are prefixed with a `_` as 
we can't ignore them
+//! (this would be a compile error).
+//!
+//! If Serde adds new attributes they need to be added here too.
+
+use darling::{FromAttributes, FromMeta};
+use syn::Expr;
+
+use crate::case::RenameRule;
+
+// from_expr is used for `rename_all = ".."`
+// FromMeta is used for `rename_all(serialize = "..", ..)`
+#[derive(Debug, Default, FromMeta, PartialEq, Eq)]
+#[darling(from_expr = RenameAll::from_expr)]
+pub struct RenameAll {
+    #[darling(default)]
+    pub serialize: RenameRule,
+    #[darling(default)]
+    pub deserialize: RenameRule,
+}
+
+impl From<RenameRule> for RenameAll {
+    fn from(value: RenameRule) -> Self {
+        Self {
+            serialize: value,
+            deserialize: value,
+        }
+    }
+}
+
+impl RenameAll {
+    fn from_expr(expr: &Expr) -> darling::Result<Self> {
+        let Expr::Lit(lit) = expr else {
+            return Err(darling::Error::custom("Expected a string or a 
tuple!"));
+        };
+        let rule = RenameRule::from_value(&lit.lit)?;
+        Ok(RenameAll::from(rule))
+    }
+}
+
+#[derive(Debug, FromMeta, PartialEq)]
+#[darling(from_expr = |expr| Ok(SerdeDefault::Expr(expr.clone())))]
+pub enum SerdeDefault {
+    #[darling(word, skip)]
+    UseTrait,
+    Expr(Expr),
+}
+
+/// All Serde attributes that a container can have.
+#[derive(Debug, FromAttributes)]
+#[darling(attributes(serde))]
+pub struct ContainerAttributes {
+    #[darling(rename = "rename")]
+    /// Rename this container.
+    pub _rename: Option<String>,
+    /// Rename all the fields (if this is a struct) or variants (if this is an 
enum) according to the given case convention.
+    #[darling(default)]
+    pub rename_all: RenameAll,
+    /// Rename all the fields of the struct variants in this enum.
+    #[darling(default, rename = "rename_all_fields")]
+    pub _rename_all_fields: RenameAll,
+    /// Error when encountering unknown fields when deserialising.
+    #[darling(default, rename = "deny_unknown_fields")]
+    pub _deny_unknown_fields: bool,
+    /// Add/expect a tag during serialisation.
+    ///
+    /// When used on a struct, this adds an extra field which is not in the 
schema definition.
+    /// When used on an enum, serde transforms it into a struct which does not 
match the schema definition.
+    pub tag: Option<String>,
+    /// Put the content in a field with this name.
+    pub content: Option<String>,
+    /// This makes the enum transparent, (de)serializing based on the variant 
directly.
+    ///
+    /// This does not match the schema definition.
+    #[darling(default)]
+    pub untagged: bool,
+    /// Add a bound to the Serialize/Deserialize trait.
+    #[darling(default, rename = "bound")]
+    pub _bound: Option<String>,
+    /// When deserializing, any missing fields should be filled in from the 
struct's implementation of `Default`.
+    #[darling(rename = "default")]
+    pub _default: Option<SerdeDefault>,
+    /// This type is the serde implementation for a "remote" type.
+    ///
+    /// This makes the (de)serialisation use/return a different type.
+    pub remote: Option<String>,
+    /// Directly use the inner type for (de)serialisation.
+    #[darling(default)]
+    pub transparent: bool,
+    /// Deserialize using the given type and then convert to this type with 
`From`.
+    #[darling(default, rename = "from")]
+    pub _from: Option<String>,
+    /// Deserialize using the given type and then convert to this type with 
`TryFrom`.
+    #[darling(default, rename = "try_from")]
+    pub _try_from: Option<String>,
+    /// Convert this type to the given type using `Into` and then serialize 
using the given type.
+    #[darling(default, rename = "into")]
+    pub _into: Option<String>,
+    /// Use the Serde API at this path.
+    #[darling(default, rename = "crate")]
+    pub _crate: Option<String>,
+    /// Custom error text.
+    #[darling(default, rename = "expecting")]
+    pub _expecting: Option<String>,
+    /// This does something with tags, which are incompatible.
+    #[darling(default)]
+    pub variant_identifier: bool,
+    /// This does something with tags,  `` which are incompatible.
+    #[darling(default)]
+    pub field_identifier: bool,
+}
+
+/// All Serde attributes that a enum variant can have.
+#[derive(Debug, FromAttributes)]
+#[darling(attributes(serde))]
+pub struct VariantAttributes {
+    /// Rename the variant.
+    #[darling(default)]
+    pub rename: Option<String>,
+    /// Aliases for this variant, only used during deserialisation.
+    #[darling(multiple, rename = "alias")]
+    pub _alias: Vec<String>,
+    #[darling(default, rename = "rename_all")]
+    pub _rename_all: RenameAll,
+    /// Do not serialize or deserialize this variant.
+    #[darling(default, rename = "skip")]
+    pub _skip: bool,
+    /// Do not serialize this variant.
+    #[darling(default, rename = "skip_serializing")]
+    pub _skip_serializing: bool,
+    /// Do not deserialize this variant.
+    #[darling(default, rename = "skip_deserializing")]
+    pub _skip_deserializing: bool,
+    /// Use this function for serializing.
+    #[darling(rename = "serialize_with")]
+    pub _serialize_with: Option<String>,
+    /// Use this function for deserializing.
+    #[darling(rename = "deserialize_with")]
+    pub _deserialize_with: Option<String>,
+    /// Use this module for (de)serializing.
+    #[darling(rename = "with")]
+    pub _with: Option<String>,
+    /// Put trait bounds on the implementations.
+    #[darling(rename = "bound")]
+    pub _bound: Option<String>,
+    /// Put bounds on the lifetimes.
+    #[darling(rename = "borrow")]
+    pub _borrow: Option<SerdeBorrow>,
+    /// This does something with tags, which are incompatible.
+    #[darling(default)]
+    pub other: bool,
+    /// (De)serialize this variant as if it was not part of the enum.
+    pub untagged: Option<String>,

Review Comment:
   Is this the correct way to read it ?! 
   `#[serde(untagged)]` has no String value.



##########
avro_derive/src/attributes/mod.rs:
##########
@@ -0,0 +1,202 @@
+use darling::FromAttributes;
+use proc_macro2::Span;
+use syn::Attribute;
+
+use crate::{case::RenameRule, darling_to_syn};
+
+pub mod avro;
+pub mod serde;
+
+#[derive(Default)]
+pub struct NamedTypeOptions {
+    pub name: Option<String>,
+    pub namespace: Option<String>,
+    pub doc: Option<String>,
+    pub alias: Vec<String>,
+    pub rename_all: RenameRule,
+}
+
+impl NamedTypeOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, 
Vec<syn::Error>> {
+        let avro =
+            
avro::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde =
+            
serde::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.tag.is_some()
+            || serde.content.is_some()
+            || serde.untagged
+            || serde.variant_identifier
+            || serde.field_identifier
+        {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support changing the tagging Serde 
generates (`tag`, `content`, `untagged`, `variant_identifier`, 
`field_identifier`)",
+            ));
+        }
+        if serde.remote.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `remote` 
attribute",
+            ));
+        }
+        if serde.transparent {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support Serde transparent",
+            ))
+        }
+        if serde.rename_all.deserialize != serde.rename_all.serialize {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support different rename rules for 
serializing and deserializing (`rename_all(serialize = \"..\", deserialize = 
\"..\")`)"
+            ))
+        }
+
+        // Check for conflicts between Serde and Avro
+        if avro.rename_all != RenameRule::None && serde.rename_all.serialize 
!= avro.rename_all {
+            errors.push(syn::Error::new(
+                span,
+                "#[avro(rename_all = \"..\")] must match #[serde(rename_all = 
\"..\")], it's also deprecated. Please use only `#[serde(rename_all = 
\"..\")]`",
+            ))
+        }
+
+        if !errors.is_empty() {
+            return Err(errors);
+        }
+
+        Ok(Self {
+            name: avro.name,
+            namespace: avro.namespace,
+            doc: avro.doc,
+            alias: avro.alias,
+            rename_all: serde.rename_all.serialize,
+        })
+    }
+}
+
+pub struct FieldOptions {
+    pub doc: Option<String>,
+    pub default: Option<String>,
+    pub alias: Vec<String>,
+    pub rename: Option<String>,
+    pub skip: bool,
+    pub flatten: bool,
+}
+
+impl FieldOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, 
Vec<syn::Error>> {
+        let avro = 
avro::FieldAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde = 
serde::FieldAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.getter.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `getter` 
attribute",
+            ));
+        }
+
+        // Check for conflicts between Serde and Avro
+        if avro.skip && !serde.skip {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(skip)]` requires `#[serde(skip)]`, it's also 
deprecated. Please use only `#[serde(skip)]`"
+            ));
+        }
+        if avro.flatten && !serde.flatten {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(flatten)]` requires `#[serde(flatten)]`, it's also 
deprecated. Please use only `#[serde(flatten)]`"
+            ));
+        }
+        // TODO: rename and alias checking can be relaxed with a more complex 
check, would require the field name
+        if avro.rename.is_some() && serde.rename != avro.rename {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(rename = \"..\")]` must match `#[serde(rename = 
\"..\")]`, it's also deprecated. Please use only `#[serde(rename  = \"..\")]`"
+            ));
+        }
+        if serde.alias != avro.alias {

Review Comment:
   This would fail if the order of the items differ but I think it is a minor 
issue that the users can easily resolve by using the same order.



##########
avro_derive/src/attributes/mod.rs:
##########
@@ -0,0 +1,202 @@
+use darling::FromAttributes;
+use proc_macro2::Span;
+use syn::Attribute;
+
+use crate::{case::RenameRule, darling_to_syn};
+
+pub mod avro;
+pub mod serde;
+
+#[derive(Default)]
+pub struct NamedTypeOptions {
+    pub name: Option<String>,
+    pub namespace: Option<String>,
+    pub doc: Option<String>,
+    pub alias: Vec<String>,
+    pub rename_all: RenameRule,
+}
+
+impl NamedTypeOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, 
Vec<syn::Error>> {
+        let avro =
+            
avro::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde =
+            
serde::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.tag.is_some()
+            || serde.content.is_some()
+            || serde.untagged
+            || serde.variant_identifier
+            || serde.field_identifier
+        {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support changing the tagging Serde 
generates (`tag`, `content`, `untagged`, `variant_identifier`, 
`field_identifier`)",
+            ));
+        }
+        if serde.remote.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `remote` 
attribute",
+            ));
+        }
+        if serde.transparent {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support Serde transparent",
+            ))
+        }
+        if serde.rename_all.deserialize != serde.rename_all.serialize {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support different rename rules for 
serializing and deserializing (`rename_all(serialize = \"..\", deserialize = 
\"..\")`)"
+            ))
+        }
+
+        // Check for conflicts between Serde and Avro
+        if avro.rename_all != RenameRule::None && serde.rename_all.serialize 
!= avro.rename_all {
+            errors.push(syn::Error::new(
+                span,
+                "#[avro(rename_all = \"..\")] must match #[serde(rename_all = 
\"..\")], it's also deprecated. Please use only `#[serde(rename_all = 
\"..\")]`",
+            ))
+        }
+
+        if !errors.is_empty() {
+            return Err(errors);
+        }
+
+        Ok(Self {
+            name: avro.name,
+            namespace: avro.namespace,
+            doc: avro.doc,
+            alias: avro.alias,
+            rename_all: serde.rename_all.serialize,
+        })
+    }
+}
+
+pub struct FieldOptions {
+    pub doc: Option<String>,
+    pub default: Option<String>,
+    pub alias: Vec<String>,
+    pub rename: Option<String>,
+    pub skip: bool,
+    pub flatten: bool,
+}
+
+impl FieldOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, 
Vec<syn::Error>> {
+        let avro = 
avro::FieldAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde = 
serde::FieldAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.getter.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `getter` 
attribute",
+            ));
+        }
+
+        // Check for conflicts between Serde and Avro
+        if avro.skip && !serde.skip {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(skip)]` requires `#[serde(skip)]`, it's also 
deprecated. Please use only `#[serde(skip)]`"
+            ));
+        }
+        if avro.flatten && !serde.flatten {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(flatten)]` requires `#[serde(flatten)]`, it's also 
deprecated. Please use only `#[serde(flatten)]`"
+            ));
+        }
+        // TODO: rename and alias checking can be relaxed with a more complex 
check, would require the field name
+        if avro.rename.is_some() && serde.rename != avro.rename {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(rename = \"..\")]` must match `#[serde(rename = 
\"..\")]`, it's also deprecated. Please use only `#[serde(rename  = \"..\")]`"
+            ));
+        }
+        if serde.alias != avro.alias {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(alias = \"..\")]` must match `#[serde(alias = 
\"..\")]`, it's also deprecated. Please use only `#[serde(alias  = \"..\")]`"
+            ));
+        }
+        if serde.skip_serializing && serde.skip_deserializing {
+            errors.push(syn::Error::new(
+                span,
+                "Use `#[serde(skip)]` instead of `#[serde(skip_serializing, 
skip_deserializing)]`",
+            ));
+
+            // Don't want to suggest default for skip_serializing as it's not 
needed for skip
+            return Err(errors);
+        }
+        if ((serde.skip_serializing) || serde.skip_serializing_if.is_some())

Review Comment:
   ```suggestion
           if (serde.skip_serializing || serde.skip_serializing_if.is_some())
   ```



##########
avro_derive/src/attributes/mod.rs:
##########
@@ -0,0 +1,202 @@
+use darling::FromAttributes;
+use proc_macro2::Span;
+use syn::Attribute;
+
+use crate::{case::RenameRule, darling_to_syn};
+
+pub mod avro;
+pub mod serde;
+
+#[derive(Default)]
+pub struct NamedTypeOptions {
+    pub name: Option<String>,
+    pub namespace: Option<String>,
+    pub doc: Option<String>,
+    pub alias: Vec<String>,
+    pub rename_all: RenameRule,
+}
+
+impl NamedTypeOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, 
Vec<syn::Error>> {
+        let avro =
+            
avro::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde =
+            
serde::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.tag.is_some()
+            || serde.content.is_some()
+            || serde.untagged
+            || serde.variant_identifier
+            || serde.field_identifier
+        {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support changing the tagging Serde 
generates (`tag`, `content`, `untagged`, `variant_identifier`, 
`field_identifier`)",
+            ));
+        }
+        if serde.remote.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `remote` 
attribute",
+            ));
+        }
+        if serde.transparent {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support Serde transparent",
+            ))
+        }
+        if serde.rename_all.deserialize != serde.rename_all.serialize {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support different rename rules for 
serializing and deserializing (`rename_all(serialize = \"..\", deserialize = 
\"..\")`)"
+            ))
+        }
+
+        // Check for conflicts between Serde and Avro
+        if avro.rename_all != RenameRule::None && serde.rename_all.serialize 
!= avro.rename_all {
+            errors.push(syn::Error::new(
+                span,
+                "#[avro(rename_all = \"..\")] must match #[serde(rename_all = 
\"..\")], it's also deprecated. Please use only `#[serde(rename_all = 
\"..\")]`",
+            ))
+        }
+
+        if !errors.is_empty() {
+            return Err(errors);
+        }
+
+        Ok(Self {
+            name: avro.name,
+            namespace: avro.namespace,
+            doc: avro.doc,
+            alias: avro.alias,
+            rename_all: serde.rename_all.serialize,
+        })
+    }
+}
+
+pub struct FieldOptions {
+    pub doc: Option<String>,
+    pub default: Option<String>,
+    pub alias: Vec<String>,
+    pub rename: Option<String>,
+    pub skip: bool,
+    pub flatten: bool,
+}
+
+impl FieldOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, 
Vec<syn::Error>> {
+        let avro = 
avro::FieldAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde = 
serde::FieldAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.getter.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `getter` 
attribute",
+            ));
+        }
+
+        // Check for conflicts between Serde and Avro
+        if avro.skip && !serde.skip {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(skip)]` requires `#[serde(skip)]`, it's also 
deprecated. Please use only `#[serde(skip)]`"

Review Comment:
   Is there a way to report the usage of a deprecated `avro(attribute)` as a 
warning without depending on the value of its respective `serde` counter-part ?
   I.e. pseudo code:
   ```
   if avro.skip { report a warning like "#[avro(skip)] is deprecated. Use 
#[serde(skip)] instead" }
   
   if avro.skip != serde.skip {report an error, without the ", it's also 
deprecated" text}
   ```
   
   Currently the user will be notified that a deprecated avro attribute is used 
only if the avro value differs from the serde's one.



##########
avro_derive/src/attributes/mod.rs:
##########
@@ -0,0 +1,202 @@
+use darling::FromAttributes;
+use proc_macro2::Span;
+use syn::Attribute;
+
+use crate::{case::RenameRule, darling_to_syn};
+
+pub mod avro;
+pub mod serde;
+
+#[derive(Default)]
+pub struct NamedTypeOptions {
+    pub name: Option<String>,
+    pub namespace: Option<String>,
+    pub doc: Option<String>,
+    pub alias: Vec<String>,
+    pub rename_all: RenameRule,
+}
+
+impl NamedTypeOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, 
Vec<syn::Error>> {
+        let avro =
+            
avro::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde =
+            
serde::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.tag.is_some()
+            || serde.content.is_some()
+            || serde.untagged
+            || serde.variant_identifier
+            || serde.field_identifier
+        {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support changing the tagging Serde 
generates (`tag`, `content`, `untagged`, `variant_identifier`, 
`field_identifier`)",
+            ));
+        }
+        if serde.remote.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `remote` 
attribute",
+            ));
+        }
+        if serde.transparent {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support Serde transparent",

Review Comment:
   ```suggestion
                   "AvroSchema derive does not support Serde `transparent` 
attribute",
   ```



##########
avro_derive/src/attributes/mod.rs:
##########
@@ -0,0 +1,202 @@
+use darling::FromAttributes;
+use proc_macro2::Span;
+use syn::Attribute;
+
+use crate::{case::RenameRule, darling_to_syn};
+
+pub mod avro;
+pub mod serde;
+
+#[derive(Default)]
+pub struct NamedTypeOptions {
+    pub name: Option<String>,
+    pub namespace: Option<String>,
+    pub doc: Option<String>,
+    pub alias: Vec<String>,
+    pub rename_all: RenameRule,
+}
+
+impl NamedTypeOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, 
Vec<syn::Error>> {
+        let avro =
+            
avro::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde =
+            
serde::ContainerAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.tag.is_some()
+            || serde.content.is_some()
+            || serde.untagged
+            || serde.variant_identifier
+            || serde.field_identifier
+        {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support changing the tagging Serde 
generates (`tag`, `content`, `untagged`, `variant_identifier`, 
`field_identifier`)",
+            ));
+        }
+        if serde.remote.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `remote` 
attribute",
+            ));
+        }
+        if serde.transparent {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support Serde transparent",
+            ))
+        }
+        if serde.rename_all.deserialize != serde.rename_all.serialize {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support different rename rules for 
serializing and deserializing (`rename_all(serialize = \"..\", deserialize = 
\"..\")`)"
+            ))
+        }
+
+        // Check for conflicts between Serde and Avro
+        if avro.rename_all != RenameRule::None && serde.rename_all.serialize 
!= avro.rename_all {
+            errors.push(syn::Error::new(
+                span,
+                "#[avro(rename_all = \"..\")] must match #[serde(rename_all = 
\"..\")], it's also deprecated. Please use only `#[serde(rename_all = 
\"..\")]`",
+            ))
+        }
+
+        if !errors.is_empty() {
+            return Err(errors);
+        }
+
+        Ok(Self {
+            name: avro.name,
+            namespace: avro.namespace,
+            doc: avro.doc,
+            alias: avro.alias,
+            rename_all: serde.rename_all.serialize,
+        })
+    }
+}
+
+pub struct FieldOptions {
+    pub doc: Option<String>,
+    pub default: Option<String>,
+    pub alias: Vec<String>,
+    pub rename: Option<String>,
+    pub skip: bool,
+    pub flatten: bool,
+}
+
+impl FieldOptions {
+    pub fn new(attributes: &[Attribute], span: Span) -> Result<Self, 
Vec<syn::Error>> {
+        let avro = 
avro::FieldAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+        let serde = 
serde::FieldAttributes::from_attributes(attributes).map_err(darling_to_syn)?;
+
+        // Collect errors so user gets all feedback at once
+        let mut errors = Vec::new();
+
+        // Check for any Serde attributes that are hard errors
+        if serde.getter.is_some() {
+            errors.push(syn::Error::new(
+                span,
+                "AvroSchema derive does not support the Serde `getter` 
attribute",
+            ));
+        }
+
+        // Check for conflicts between Serde and Avro
+        if avro.skip && !serde.skip {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(skip)]` requires `#[serde(skip)]`, it's also 
deprecated. Please use only `#[serde(skip)]`"
+            ));
+        }
+        if avro.flatten && !serde.flatten {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(flatten)]` requires `#[serde(flatten)]`, it's also 
deprecated. Please use only `#[serde(flatten)]`"
+            ));
+        }
+        // TODO: rename and alias checking can be relaxed with a more complex 
check, would require the field name
+        if avro.rename.is_some() && serde.rename != avro.rename {
+            errors.push(syn::Error::new(
+                span,
+                "`#[avro(rename = \"..\")]` must match `#[serde(rename = 
\"..\")]`, it's also deprecated. Please use only `#[serde(rename  = \"..\")]`"
+            ));
+        }
+        if serde.alias != avro.alias {

Review Comment:
   This would fail if only `#[serde(alias= ...)]` is used because `avro.alias` 
will be resolved to an empty Vec.
   ```suggestion
           if !avro.alias.is_empty() && serde.alias != avro.alias {
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to