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

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


The following commit(s) were added to refs/heads/main by this push:
     new 0d7f8d7  feat: Support `fixed` as type for `uuid` (#339)
0d7f8d7 is described below

commit 0d7f8d7a7937ecf4c80b7c8401fd6cfdf750fede
Author: Kriskras99 <[email protected]>
AuthorDate: Mon Dec 22 15:40:47 2025 +0100

    feat: Support `fixed` as type for `uuid` (#339)
    
    * feat!: Rework `Schema::Decimal` to not require a `Box<Schema>`
    
    * feat!: Rework `Schema::Uuid` to support `fixed` as the base type
    
    * fix: small fixed for uuid and schema compatibility
    
    * fix: Small fixes related to Schema::Uuid and Schema::Decimal
    
    ---------
    
    Co-authored-by: default <[email protected]>
---
 avro/src/decode.rs                       | 147 ++++++++++------
 avro/src/encode.rs                       |  52 ++++--
 avro/src/schema.rs                       | 282 ++++++++++++++++++++++++-------
 avro/src/schema_compatibility.rs         | 142 ++++++++++++++--
 avro/src/schema_equality.rs              |  82 ++++++++-
 avro/src/serde/ser_schema.rs             |  82 ++++-----
 avro/src/types.rs                        | 114 +++++++++----
 avro/src/writer.rs                       |  15 +-
 avro/tests/serde_human_readable_false.rs |   4 +-
 avro/tests/serde_human_readable_true.rs  |   7 +-
 10 files changed, 689 insertions(+), 238 deletions(-)

diff --git a/avro/src/decode.rs b/avro/src/decode.rs
index 78fefbd..06c59e6 100644
--- a/avro/src/decode.rs
+++ b/avro/src/decode.rs
@@ -15,6 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use crate::schema::{InnerDecimalSchema, UuidSchema};
 use crate::{
     AvroResult, Error,
     bigdecimal::deserialize_big_decimal,
@@ -82,7 +83,7 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>(
     enclosing_namespace: &Namespace,
     reader: &mut R,
 ) -> AvroResult<Value> {
-    match *schema {
+    match schema {
         Schema::Null => Ok(Value::Null),
         Schema::Boolean => {
             let mut buf = [0u8; 1];
@@ -101,18 +102,24 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>(
                 }
             }
         }
-        Schema::Decimal(DecimalSchema { ref inner, .. }) => match &**inner {
-            Schema::Fixed { .. } => {
-                match decode_internal(inner, names, enclosing_namespace, 
reader)? {
+        Schema::Decimal(DecimalSchema { inner, .. }) => match inner {
+            InnerDecimalSchema::Fixed(fixed) => {
+                match decode_internal(
+                    &Schema::Fixed(fixed.copy_only_size()),
+                    names,
+                    enclosing_namespace,
+                    reader,
+                )? {
                     Value::Fixed(_, bytes) => 
Ok(Value::Decimal(Decimal::from(bytes))),
                     value => Err(Details::FixedValue(value).into()),
                 }
             }
-            Schema::Bytes => match decode_internal(inner, names, 
enclosing_namespace, reader)? {
-                Value::Bytes(bytes) => 
Ok(Value::Decimal(Decimal::from(bytes))),
-                value => Err(Details::BytesValue(value).into()),
-            },
-            schema => Err(Details::ResolveDecimalSchema(schema.into()).into()),
+            InnerDecimalSchema::Bytes => {
+                match decode_internal(&Schema::Bytes, names, 
enclosing_namespace, reader)? {
+                    Value::Bytes(bytes) => 
Ok(Value::Decimal(Decimal::from(bytes))),
+                    value => Err(Details::BytesValue(value).into()),
+                }
+            }
         },
         Schema::BigDecimal => {
             match decode_internal(&Schema::Bytes, names, enclosing_namespace, 
reader)? {
@@ -120,20 +127,45 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>(
                 value => Err(Details::BytesValue(value).into()),
             }
         }
-        Schema::Uuid => {
+        Schema::Uuid(UuidSchema::String) => {
+            let Value::String(string) =
+                decode_internal(&Schema::String, names, enclosing_namespace, 
reader)?
+            else {
+                // decoding a String can also return a Null, indicating EOF
+                return Err(Error::new(Details::ReadBytes(std::io::Error::from(
+                    ErrorKind::UnexpectedEof,
+                ))));
+            };
+            let uuid = 
Uuid::parse_str(&string).map_err(Details::ConvertStrToUuid)?;
+            Ok(Value::Uuid(uuid))
+        }
+        Schema::Uuid(UuidSchema::Bytes) => {
             let Value::Bytes(bytes) =
                 decode_internal(&Schema::Bytes, names, enclosing_namespace, 
reader)?
             else {
-                // Calling decode_internal with Schema::Bytes can only return 
a Value::Bytes or an error
-                unreachable!();
+                unreachable!(
+                    "decode_internal(Schema::Bytes) can only return a 
Value::Bytes or an error"
+                )
             };
-
-            let uuid = if bytes.len() == 16 {
-                Uuid::from_slice(&bytes).map_err(Details::ConvertSliceToUuid)?
-            } else {
-                let string = 
std::str::from_utf8(&bytes).map_err(Details::ConvertToUtf8Error)?;
-                Uuid::parse_str(string).map_err(Details::ConvertStrToUuid)?
+            let uuid = 
Uuid::from_slice(&bytes).map_err(Details::ConvertSliceToUuid)?;
+            Ok(Value::Uuid(uuid))
+        }
+        Schema::Uuid(UuidSchema::Fixed(fixed)) => {
+            let Value::Fixed(n, bytes) = decode_internal(
+                &Schema::Fixed(fixed.copy_only_size()),
+                names,
+                enclosing_namespace,
+                reader,
+            )?
+            else {
+                unreachable!(
+                    "decode_internal(Schema::Fixed) can only return a 
Value::Fixed or an error"
+                )
             };
+            if n != 16 {
+                return Err(Details::ConvertFixedToUuid(n).into());
+            }
+            let uuid = 
Uuid::from_slice(&bytes).map_err(Details::ConvertSliceToUuid)?;
             Ok(Value::Uuid(uuid))
         }
         Schema::Int => decode_int(reader),
@@ -189,13 +221,13 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>(
             }
         }
         Schema::Fixed(FixedSchema { size, .. }) => {
-            let mut buf = vec![0u8; size];
+            let mut buf = vec![0u8; *size];
             reader
                 .read_exact(&mut buf)
-                .map_err(|e| Details::ReadFixed(e, size))?;
-            Ok(Value::Fixed(size, buf))
+                .map_err(|e| Details::ReadFixed(e, *size))?;
+            Ok(Value::Fixed(*size, buf))
         }
-        Schema::Array(ref inner) => {
+        Schema::Array(inner) => {
             let mut items = Vec::new();
 
             loop {
@@ -217,7 +249,7 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>(
 
             Ok(Value::Array(items))
         }
-        Schema::Map(ref inner) => {
+        Schema::Map(inner) => {
             let mut items = HashMap::new();
 
             loop {
@@ -241,7 +273,7 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>(
 
             Ok(Value::Map(items))
         }
-        Schema::Union(ref inner) => match 
zag_i64(reader).map_err(Error::into_details) {
+        Schema::Union(inner) => match 
zag_i64(reader).map_err(Error::into_details) {
             Ok(index) => {
                 let variants = inner.variants();
                 let variant = variants
@@ -262,11 +294,7 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>(
             }
             Err(io_err) => Err(Error::new(io_err)),
         },
-        Schema::Record(RecordSchema {
-            ref name,
-            ref fields,
-            ..
-        }) => {
+        Schema::Record(RecordSchema { name, fields, .. }) => {
             let fully_qualified_name = 
name.fully_qualified_name(enclosing_namespace);
             // Benchmarks indicate ~10% improvement using this method.
             let mut items = Vec::with_capacity(fields.len());
@@ -284,7 +312,7 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>(
             }
             Ok(Value::Record(items))
         }
-        Schema::Enum(EnumSchema { ref symbols, .. }) => {
+        Schema::Enum(EnumSchema { symbols, .. }) => {
             Ok(if let Value::Int(raw_index) = decode_int(reader)? {
                 let index = usize::try_from(raw_index)
                     .map_err(|e| Details::ConvertI32ToUsize(e, raw_index))?;
@@ -302,7 +330,7 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>(
                 return Err(Details::GetEnumUnknownIndexValue.into());
             })
         }
-        Schema::Ref { ref name } => {
+        Schema::Ref { name } => {
             let fully_qualified_name = 
name.fully_qualified_name(enclosing_namespace);
             if let Some(resolved) = names.get(&fully_qualified_name) {
                 decode_internal(
@@ -321,6 +349,7 @@ pub(crate) fn decode_internal<R: Read, S: Borrow<Schema>>(
 #[cfg(test)]
 #[allow(clippy::expect_fun_call)]
 mod tests {
+    use crate::schema::{InnerDecimalSchema, UuidSchema};
     use crate::{
         Decimal,
         decode::decode,
@@ -380,14 +409,13 @@ mod tests {
     fn test_negative_decimal_value() -> TestResult {
         use crate::{encode::encode, schema::Name};
         use num_bigint::ToBigInt;
-        let inner = Box::new(Schema::Fixed(
-            FixedSchema::builder()
-                .name(Name::new("decimal")?)
-                .size(2)
-                .build(),
-        ));
         let schema = Schema::Decimal(DecimalSchema {
-            inner,
+            inner: InnerDecimalSchema::Fixed(
+                FixedSchema::builder()
+                    .name(Name::new("decimal")?)
+                    .size(2)
+                    .build(),
+            ),
             precision: 4,
             scale: 2,
         });
@@ -408,16 +436,15 @@ mod tests {
     fn test_decode_decimal_with_bigger_than_necessary_size() -> TestResult {
         use crate::{encode::encode, schema::Name};
         use num_bigint::ToBigInt;
-        let inner = Box::new(Schema::Fixed(FixedSchema {
-            size: 13,
-            name: Name::new("decimal")?,
-            aliases: None,
-            doc: None,
-            default: None,
-            attributes: Default::default(),
-        }));
         let schema = Schema::Decimal(DecimalSchema {
-            inner,
+            inner: InnerDecimalSchema::Fixed(FixedSchema {
+                size: 13,
+                name: Name::new("decimal")?,
+                aliases: None,
+                doc: None,
+                default: None,
+                attributes: Default::default(),
+            }),
             precision: 4,
             scale: 2,
         });
@@ -844,7 +871,7 @@ mod tests {
         let mut buffer = Vec::new();
         encode(&value, &schema, &mut buffer).expect(&success(&value, &schema));
 
-        let result = decode(&Schema::Uuid, &mut &buffer[..])?;
+        let result = decode(&Schema::Uuid(UuidSchema::String), &mut 
&buffer[..])?;
         assert_eq!(result, value);
 
         Ok(())
@@ -854,20 +881,38 @@ mod tests {
     fn avro_3926_encode_decode_uuid_to_fixed() -> TestResult {
         use crate::encode::encode;
 
-        let schema = Schema::Fixed(FixedSchema {
+        let fixed = FixedSchema {
             size: 16,
             name: "uuid".into(),
             aliases: None,
             doc: None,
             default: None,
             attributes: Default::default(),
-        });
+        };
+
+        let schema = Schema::Fixed(fixed.clone());
+        let value = 
Value::Uuid(Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000")?);
+
+        let mut buffer = Vec::new();
+        encode(&value, &schema, &mut buffer).expect(&success(&value, &schema));
+
+        let result = decode(&Schema::Uuid(UuidSchema::Fixed(fixed)), &mut 
&buffer[..])?;
+        assert_eq!(result, value);
+
+        Ok(())
+    }
+
+    #[test]
+    fn encode_decode_uuid_to_bytes() -> TestResult {
+        use crate::encode::encode;
+
+        let schema = Schema::Bytes;
         let value = 
Value::Uuid(Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000")?);
 
         let mut buffer = Vec::new();
         encode(&value, &schema, &mut buffer).expect(&success(&value, &schema));
 
-        let result = decode(&Schema::Uuid, &mut &buffer[..])?;
+        let result = decode(&Schema::Uuid(UuidSchema::Bytes), &mut 
&buffer[..])?;
         assert_eq!(result, value);
 
         Ok(())
diff --git a/avro/src/encode.rs b/avro/src/encode.rs
index f19352c..467dd53 100644
--- a/avro/src/encode.rs
+++ b/avro/src/encode.rs
@@ -15,6 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use crate::schema::{InnerDecimalSchema, UuidSchema};
 use crate::{
     AvroResult,
     bigdecimal::serialize_big_decimal,
@@ -111,17 +112,24 @@ pub(crate) fn encode_internal<W: Write, S: 
Borrow<Schema>>(
             .write(&x.to_le_bytes())
             .map_err(|e| Details::WriteBytes(e).into()),
         Value::Decimal(decimal) => match schema {
-            Schema::Decimal(DecimalSchema { inner, .. }) => match 
*inner.clone() {
-                Schema::Fixed(FixedSchema { size, .. }) => {
-                    let bytes = 
decimal.to_sign_extended_bytes_with_len(size).unwrap();
+            Schema::Decimal(DecimalSchema { inner, .. }) => match inner {
+                InnerDecimalSchema::Fixed(fixed) => {
+                    let bytes = 
decimal.to_sign_extended_bytes_with_len(fixed.size)?;
                     let num_bytes = bytes.len();
-                    if num_bytes != size {
-                        return 
Err(Details::EncodeDecimalAsFixedError(num_bytes, size).into());
+                    if num_bytes != fixed.size {
+                        return Err(
+                            Details::EncodeDecimalAsFixedError(num_bytes, 
fixed.size).into()
+                        );
                     }
-                    encode(&Value::Fixed(size, bytes), inner, writer)
+                    encode(
+                        &Value::Fixed(fixed.size, bytes),
+                        &Schema::Fixed(fixed.copy_only_size()),
+                        writer,
+                    )
+                }
+                InnerDecimalSchema::Bytes => {
+                    encode(&Value::Bytes(decimal.try_into()?), &Schema::Bytes, 
writer)
                 }
-                Schema::Bytes => encode(&Value::Bytes(decimal.try_into()?), 
inner, writer),
-                _ => 
Err(Details::ResolveDecimalSchema(SchemaKind::from(*inner.clone())).into()),
             },
             _ => Err(Details::EncodeValueAsSchemaError {
                 value_kind: ValueKind::Decimal,
@@ -136,23 +144,35 @@ pub(crate) fn encode_internal<W: Write, S: 
Borrow<Schema>>(
                 .map_err(|e| Details::WriteBytes(e).into())
         }
         Value::Uuid(uuid) => match *schema {
-            Schema::Uuid | Schema::String => encode_bytes(
+            Schema::Uuid(UuidSchema::String) | Schema::String => encode_bytes(
                 // we need the call .to_string() to properly convert ASCII to 
UTF-8
                 #[allow(clippy::unnecessary_to_owned)]
                 &uuid.to_string(),
                 writer,
             ),
-            Schema::Fixed(FixedSchema { size, .. }) => {
+            Schema::Uuid(UuidSchema::Bytes) | Schema::Bytes => {
+                let bytes = uuid.as_bytes();
+                encode_bytes(bytes, writer)
+            }
+            Schema::Uuid(UuidSchema::Fixed(FixedSchema { size, .. }))
+            | Schema::Fixed(FixedSchema { size, .. }) => {
                 if size != 16 {
                     return Err(Details::ConvertFixedToUuid(size).into());
                 }
 
                 let bytes = uuid.as_bytes();
-                encode_bytes(bytes, writer)
+                writer
+                    .write(bytes.as_slice())
+                    .map_err(|e| Details::WriteBytes(e).into())
             }
             _ => Err(Details::EncodeValueAsSchemaError {
                 value_kind: ValueKind::Uuid,
-                supported_schema: vec![SchemaKind::Uuid, SchemaKind::Fixed],
+                supported_schema: vec![
+                    SchemaKind::Uuid,
+                    SchemaKind::Fixed,
+                    SchemaKind::Bytes,
+                    SchemaKind::String,
+                ],
             }
             .into()),
         },
@@ -163,18 +183,18 @@ pub(crate) fn encode_internal<W: Write, S: 
Borrow<Schema>>(
                 .map_err(|e| Details::WriteBytes(e).into())
         }
         Value::Bytes(bytes) => match *schema {
-            Schema::Bytes => encode_bytes(bytes, writer),
+            Schema::Bytes | Schema::Uuid(UuidSchema::Bytes) => 
encode_bytes(bytes, writer),
             Schema::Fixed { .. } => writer
                 .write(bytes.as_slice())
                 .map_err(|e| Details::WriteBytes(e).into()),
             _ => Err(Details::EncodeValueAsSchemaError {
                 value_kind: ValueKind::Bytes,
-                supported_schema: vec![SchemaKind::Bytes, SchemaKind::Fixed],
+                supported_schema: vec![SchemaKind::Bytes, SchemaKind::Fixed, 
SchemaKind::Uuid],
             }
             .into()),
         },
         Value::String(s) => match *schema {
-            Schema::String | Schema::Uuid => encode_bytes(s, writer),
+            Schema::String | Schema::Uuid(UuidSchema::String) => 
encode_bytes(s, writer),
             Schema::Enum(EnumSchema { ref symbols, .. }) => {
                 if let Some(index) = symbols.iter().position(|item| item == s) 
{
                     encode_int(index as i32, writer)
@@ -937,7 +957,7 @@ pub(crate) mod tests {
     #[test]
     fn test_avro_3585_encode_uuids() {
         let value = 
Value::String(String::from("00000000-0000-0000-0000-000000000000"));
-        let schema = Schema::Uuid;
+        let schema = Schema::Uuid(UuidSchema::String);
         let mut buffer = Vec::new();
         let encoded = encode(&value, &schema, &mut buffer);
         assert!(encoded.is_ok());
diff --git a/avro/src/schema.rs b/avro/src/schema.rs
index 8260cc2..a7ea493 100644
--- a/avro/src/schema.rs
+++ b/avro/src/schema.rs
@@ -110,8 +110,8 @@ pub enum Schema {
     /// Logical type which represents `Decimal` values without predefined 
scale.
     /// The underlying type is serialized and deserialized as `Schema::Bytes`
     BigDecimal,
-    /// A universally unique identifier, annotating a string.
-    Uuid,
+    /// A universally unique identifier, annotating a string, bytes or fixed.
+    Uuid(UuidSchema),
     /// Logical type which represents the number of days since the unix epoch.
     /// Serialization format is `Schema::Int`.
     Date,
@@ -487,7 +487,13 @@ impl<'s> ResolvedSchema<'s> {
                         self.resolve(vec![schema], enclosing_namespace, 
known_schemata)?
                     }
                 }
-                Schema::Enum(EnumSchema { name, .. }) | 
Schema::Fixed(FixedSchema { name, .. }) => {
+                Schema::Enum(EnumSchema { name, .. })
+                | Schema::Fixed(FixedSchema { name, .. })
+                | Schema::Uuid(UuidSchema::Fixed(FixedSchema { name, .. }))
+                | Schema::Decimal(DecimalSchema {
+                    inner: InnerDecimalSchema::Fixed(FixedSchema { name, .. }),
+                    ..
+                }) => {
                     let fully_qualified_name = 
name.fully_qualified_name(enclosing_namespace);
                     if self
                         .names_ref
@@ -574,7 +580,13 @@ pub(crate) fn resolve_names(
             }
             Ok(())
         }
-        Schema::Enum(EnumSchema { name, .. }) | Schema::Fixed(FixedSchema { 
name, .. }) => {
+        Schema::Enum(EnumSchema { name, .. })
+        | Schema::Fixed(FixedSchema { name, .. })
+        | Schema::Uuid(UuidSchema::Fixed(FixedSchema { name, .. }))
+        | Schema::Decimal(DecimalSchema {
+            inner: InnerDecimalSchema::Fixed(FixedSchema { name, .. }),
+            ..
+        }) => {
             let fully_qualified_name = 
name.fully_qualified_name(enclosing_namespace);
             if names
                 .insert(fully_qualified_name.clone(), schema.clone())
@@ -881,6 +893,23 @@ impl FixedSchema {
 
         Ok(map)
     }
+
+    /// Create a new `FixedSchema` copying only the size.
+    ///
+    /// All other fields are `None` or empty.
+    pub(crate) fn copy_only_size(&self) -> Self {
+        Self {
+            name: Name {
+                name: String::new(),
+                namespace: None,
+            },
+            aliases: None,
+            doc: None,
+            size: self.size,
+            default: None,
+            attributes: Default::default(),
+        }
+    }
 }
 
 /// A description of a Decimal schema.
@@ -894,7 +923,40 @@ pub struct DecimalSchema {
     /// The number of digits to the right of the decimal point
     pub scale: DecimalMetadata,
     /// The inner schema of the decimal (fixed or bytes)
-    pub inner: Box<Schema>,
+    pub inner: InnerDecimalSchema,
+}
+
+/// The inner schema of the Decimal type.
+#[derive(Debug, Clone)]
+pub enum InnerDecimalSchema {
+    Bytes,
+    Fixed(FixedSchema),
+}
+
+impl TryFrom<Schema> for InnerDecimalSchema {
+    type Error = Error;
+
+    fn try_from(value: Schema) -> Result<Self, Self::Error> {
+        match value {
+            Schema::Bytes => Ok(InnerDecimalSchema::Bytes),
+            Schema::Fixed(fixed) => Ok(InnerDecimalSchema::Fixed(fixed)),
+            _ => Err(Details::ResolveDecimalSchema(value.into()).into()),
+        }
+    }
+}
+
+/// The inner schema of the Uuid type.
+#[derive(Debug, Clone)]
+pub enum UuidSchema {
+    /// [`Schema::Bytes`] with size of 16.
+    ///
+    /// This is not according to specification, but was what happened in 
`0.21.0` and earlier when
+    /// a schema with logical type `uuid` and inner type `fixed` was used.
+    Bytes,
+    /// [`Schema::String`].
+    String,
+    /// [`Schema::Fixed`] with size of 16.
+    Fixed(FixedSchema),
 }
 
 /// A description of a Union schema
@@ -1199,7 +1261,12 @@ impl Schema {
             | Schema::Enum(EnumSchema { attributes, .. })
             | Schema::Fixed(FixedSchema { attributes, .. })
             | Schema::Array(ArraySchema { attributes, .. })
-            | Schema::Map(MapSchema { attributes, .. }) => Some(attributes),
+            | Schema::Map(MapSchema { attributes, .. })
+            | Schema::Decimal(DecimalSchema {
+                inner: InnerDecimalSchema::Fixed(FixedSchema { attributes, .. 
}),
+                ..
+            })
+            | Schema::Uuid(UuidSchema::Fixed(FixedSchema { attributes, .. })) 
=> Some(attributes),
             _ => None,
         }
     }
@@ -1210,7 +1277,12 @@ impl Schema {
             Schema::Ref { name, .. }
             | Schema::Record(RecordSchema { name, .. })
             | Schema::Enum(EnumSchema { name, .. })
-            | Schema::Fixed(FixedSchema { name, .. }) => Some(name),
+            | Schema::Fixed(FixedSchema { name, .. })
+            | Schema::Decimal(DecimalSchema {
+                inner: InnerDecimalSchema::Fixed(FixedSchema { name, .. }),
+                ..
+            })
+            | Schema::Uuid(UuidSchema::Fixed(FixedSchema { name, .. })) => 
Some(name),
             _ => None,
         }
     }
@@ -1225,7 +1297,12 @@ impl Schema {
         match self {
             Schema::Record(RecordSchema { aliases, .. })
             | Schema::Enum(EnumSchema { aliases, .. })
-            | Schema::Fixed(FixedSchema { aliases, .. }) => aliases.as_ref(),
+            | Schema::Fixed(FixedSchema { aliases, .. })
+            | Schema::Decimal(DecimalSchema {
+                inner: InnerDecimalSchema::Fixed(FixedSchema { aliases, .. }),
+                ..
+            })
+            | Schema::Uuid(UuidSchema::Fixed(FixedSchema { aliases, .. })) => 
aliases.as_ref(),
             _ => None,
         }
     }
@@ -1235,7 +1312,12 @@ impl Schema {
         match self {
             Schema::Record(RecordSchema { doc, .. })
             | Schema::Enum(EnumSchema { doc, .. })
-            | Schema::Fixed(FixedSchema { doc, .. }) => doc.as_ref(),
+            | Schema::Fixed(FixedSchema { doc, .. })
+            | Schema::Decimal(DecimalSchema {
+                inner: InnerDecimalSchema::Fixed(FixedSchema { doc, .. }),
+                ..
+            })
+            | Schema::Uuid(UuidSchema::Fixed(FixedSchema { doc, .. })) => 
doc.as_ref(),
             _ => None,
         }
     }
@@ -1547,7 +1629,7 @@ impl Parser {
                                 Ok((precision, scale)) => 
Ok(Schema::Decimal(DecimalSchema {
                                     precision,
                                     scale,
-                                    inner: Box::new(inner),
+                                    inner: inner.try_into()?,
                                 })),
                                 Err(err) => {
                                     warn!("Ignoring invalid decimal logical 
type: {err}");
@@ -1569,16 +1651,19 @@ impl Parser {
                     return try_convert_to_logical_type(
                         "uuid",
                         parse_as_native_complex(complex, self, 
enclosing_namespace)?,
-                        &[SchemaKind::String, SchemaKind::Fixed],
+                        &[SchemaKind::String, SchemaKind::Fixed, 
SchemaKind::Bytes],
                         |schema| match schema {
-                            Schema::String => Ok(Schema::Uuid),
-                            Schema::Fixed(FixedSchema { size: 16, .. }) => 
Ok(Schema::Uuid),
+                            Schema::String => 
Ok(Schema::Uuid(UuidSchema::String)),
+                            Schema::Fixed(fixed @ FixedSchema { size: 16, .. 
}) => {
+                                Ok(Schema::Uuid(UuidSchema::Fixed(fixed)))
+                            }
                             Schema::Fixed(FixedSchema { size, .. }) => {
                                 warn!(
                                     "Ignoring uuid logical type for a Fixed 
schema because its size ({size:?}) is not 16! Schema: {schema:?}"
                                 );
                                 Ok(schema)
                             }
+                            Schema::Bytes => 
Ok(Schema::Uuid(UuidSchema::Bytes)),
                             _ => {
                                 warn!("Ignoring invalid uuid logical type for 
schema: {schema:?}");
                                 Ok(schema)
@@ -2077,8 +2162,8 @@ impl Serialize for Schema {
     where
         S: Serializer,
     {
-        match *self {
-            Schema::Ref { ref name } => 
serializer.serialize_str(&name.fullname(None)),
+        match &self {
+            Schema::Ref { name } => 
serializer.serialize_str(&name.fullname(None)),
             Schema::Null => serializer.serialize_str("null"),
             Schema::Boolean => serializer.serialize_str("boolean"),
             Schema::Int => serializer.serialize_str("int"),
@@ -2087,7 +2172,7 @@ impl Serialize for Schema {
             Schema::Double => serializer.serialize_str("double"),
             Schema::Bytes => serializer.serialize_str("bytes"),
             Schema::String => serializer.serialize_str("string"),
-            Schema::Array(ref inner) => {
+            Schema::Array(inner) => {
                 let mut map = serializer.serialize_map(Some(2 + 
inner.attributes.len()))?;
                 map.serialize_entry("type", "array")?;
                 map.serialize_entry("items", &*inner.items.clone())?;
@@ -2096,7 +2181,7 @@ impl Serialize for Schema {
                 }
                 map.end()
             }
-            Schema::Map(ref inner) => {
+            Schema::Map(inner) => {
                 let mut map = serializer.serialize_map(Some(2 + 
inner.attributes.len()))?;
                 map.serialize_entry("type", "map")?;
                 map.serialize_entry("values", &*inner.types.clone())?;
@@ -2105,7 +2190,7 @@ impl Serialize for Schema {
                 }
                 map.end()
             }
-            Schema::Union(ref inner) => {
+            Schema::Union(inner) => {
                 let variants = inner.variants();
                 let mut seq = serializer.serialize_seq(Some(variants.len()))?;
                 for v in variants {
@@ -2114,11 +2199,11 @@ impl Serialize for Schema {
                 seq.end()
             }
             Schema::Record(RecordSchema {
-                ref name,
-                ref aliases,
-                ref doc,
-                ref fields,
-                ref attributes,
+                name,
+                aliases,
+                doc,
+                fields,
+                attributes,
                 ..
             }) => {
                 let mut map = serializer.serialize_map(None)?;
@@ -2140,10 +2225,10 @@ impl Serialize for Schema {
                 map.end()
             }
             Schema::Enum(EnumSchema {
-                ref name,
-                ref symbols,
-                ref aliases,
-                ref attributes,
+                name,
+                symbols,
+                aliases,
+                attributes,
                 ..
             }) => {
                 let mut map = serializer.serialize_map(None)?;
@@ -2162,30 +2247,24 @@ impl Serialize for Schema {
                 }
                 map.end()
             }
-            Schema::Fixed(ref fixed_schema) => {
+            Schema::Fixed(fixed_schema) => {
                 let mut map = serializer.serialize_map(None)?;
                 map = fixed_schema.serialize_to_map::<S>(map)?;
                 map.end()
             }
             Schema::Decimal(DecimalSchema {
-                ref scale,
-                ref precision,
-                ref inner,
+                scale,
+                precision,
+                inner,
             }) => {
                 let mut map = serializer.serialize_map(None)?;
-                match inner.as_ref() {
-                    Schema::Fixed(fixed_schema) => {
+                match inner {
+                    InnerDecimalSchema::Fixed(fixed_schema) => {
                         map = fixed_schema.serialize_to_map::<S>(map)?;
                     }
-                    Schema::Bytes => {
+                    InnerDecimalSchema::Bytes => {
                         map.serialize_entry("type", "bytes")?;
                     }
-                    others => {
-                        return Err(serde::ser::Error::custom(format!(
-                            "DecimalSchema inner type must be Fixed or Bytes, 
got {:?}",
-                            SchemaKind::from(others)
-                        )));
-                    }
                 }
                 map.serialize_entry("logicalType", "decimal")?;
                 map.serialize_entry("scale", scale)?;
@@ -2199,9 +2278,19 @@ impl Serialize for Schema {
                 map.serialize_entry("logicalType", "big-decimal")?;
                 map.end()
             }
-            Schema::Uuid => {
+            Schema::Uuid(inner) => {
                 let mut map = serializer.serialize_map(None)?;
-                map.serialize_entry("type", "string")?;
+                match inner {
+                    UuidSchema::Bytes => {
+                        map.serialize_entry("type", "bytes")?;
+                    }
+                    UuidSchema::String => {
+                        map.serialize_entry("type", "string")?;
+                    }
+                    UuidSchema::Fixed(fixed_schema) => {
+                        map = fixed_schema.serialize_to_map::<S>(map)?;
+                    }
+                }
                 map.serialize_entry("logicalType", "uuid")?;
                 map.end()
             }
@@ -2539,7 +2628,7 @@ pub mod derive {
     impl_schema!(f32, Schema::Float);
     impl_schema!(f64, Schema::Double);
     impl_schema!(String, Schema::String);
-    impl_schema!(uuid::Uuid, Schema::Uuid);
+    impl_schema!(uuid::Uuid, Schema::Uuid(UuidSchema::String));
     impl_schema!(core::time::Duration, Schema::Duration);
 
     impl<T> AvroSchemaComponent for Vec<T>
@@ -6698,7 +6787,7 @@ mod tests {
           "logicalType": "uuid"
         });
         let parse_result = Schema::parse(&schema)?;
-        assert_eq!(parse_result, Schema::Uuid);
+        assert_eq!(parse_result, Schema::Uuid(UuidSchema::String));
 
         Ok(())
     }
@@ -6713,7 +6802,17 @@ mod tests {
             "logicalType": "uuid"
         });
         let parse_result = Schema::parse(&schema)?;
-        assert_eq!(parse_result, Schema::Uuid);
+        assert_eq!(
+            parse_result,
+            Schema::Uuid(UuidSchema::Fixed(FixedSchema {
+                name: Name::new("FixedUUID")?,
+                aliases: None,
+                doc: None,
+                size: 16,
+                default: None,
+                attributes: Default::default(),
+            }))
+        );
         assert_not_logged(
             r#"Ignoring uuid logical type for a Fixed schema because its size 
(6) is not 16! Schema: Fixed(FixedSchema { name: Name { name: "FixedUUID", 
namespace: None }, aliases: None, doc: None, size: 6, attributes: 
{"logicalType": String("uuid")} })"#,
         );
@@ -6721,6 +6820,20 @@ mod tests {
         Ok(())
     }
 
+    #[test]
+    fn uuid_schema_bytes() -> TestResult {
+        let schema = json!(
+        {
+          "type": "bytes",
+          "name": "BytesUUID",
+          "logicalType": "uuid"
+        });
+        let parse_result = Schema::parse(&schema)?;
+        assert_eq!(parse_result, Schema::Uuid(UuidSchema::Bytes));
+
+        Ok(())
+    }
+
     #[test]
     fn avro_3926_uuid_schema_for_fixed_with_size_different_than_16() -> 
TestResult {
         let schema = json!(
@@ -6883,14 +6996,14 @@ mod tests {
         let schema = Schema::Decimal(DecimalSchema {
             precision: 36,
             scale: 10,
-            inner: Box::new(Schema::Fixed(FixedSchema {
+            inner: InnerDecimalSchema::Fixed(FixedSchema {
                 name: Name::new("decimal_36_10").unwrap(),
                 aliases: None,
                 doc: None,
                 size: 16,
                 default: None,
                 attributes: Default::default(),
-            })),
+            }),
         });
 
         let serialized_json = serde_json::to_string_pretty(&schema)?;
@@ -6914,7 +7027,7 @@ mod tests {
         let schema = Schema::Decimal(DecimalSchema {
             precision: 36,
             scale: 10,
-            inner: Box::new(Schema::Bytes),
+            inner: InnerDecimalSchema::Bytes,
         });
 
         let serialized_json = serde_json::to_string_pretty(&schema)?;
@@ -6931,21 +7044,6 @@ mod tests {
         Ok(())
     }
 
-    #[test]
-    fn test_avro_3925_serialize_decimal_inner_invalid() -> TestResult {
-        let schema = Schema::Decimal(DecimalSchema {
-            precision: 36,
-            scale: 10,
-            inner: Box::new(Schema::String),
-        });
-
-        let serialized_json = serde_json::to_string_pretty(&schema);
-
-        assert!(serialized_json.is_err());
-
-        Ok(())
-    }
-
     #[test]
     fn test_avro_3927_serialize_array_with_custom_attributes() -> TestResult {
         let expected = Schema::array_with_attributes(
@@ -7314,4 +7412,62 @@ mod tests {
 
         Ok(())
     }
+
+    #[test]
+    fn avro_rs_339_schema_ref_uuid() {
+        let schema = Schema::parse_str(
+            r#"{
+            "name": "foo",
+            "type": "record",
+            "fields": [
+                {
+                    "name": "a",
+                    "type": {
+                        "type": "fixed",
+                        "size": 16,
+                        "logicalType": "uuid",
+                        "name": "bar"
+                    }
+                },
+                {
+                    "name": "b",
+                    "type": "bar"
+                }
+            ]
+        }"#,
+        )
+        .unwrap();
+        let _resolved = ResolvedSchema::try_from(&schema).unwrap();
+        let _resolved_owned = ResolvedOwnedSchema::try_from(schema).unwrap();
+    }
+
+    #[test]
+    fn avro_rs_339_schema_ref_decimal() {
+        let schema = Schema::parse_str(
+            r#"{
+            "name": "foo",
+            "type": "record",
+            "fields": [
+                {
+                    "name": "a",
+                    "type": {
+                        "type": "fixed",
+                        "size": 16,
+                        "logicalType": "decimal",
+                        "precision": 4,
+                        "scale": 2,
+                        "name": "bar"
+                    }
+                },
+                {
+                    "name": "b",
+                    "type": "bar"
+                }
+            ]
+        }"#,
+        )
+        .unwrap();
+        let _resolved = ResolvedSchema::try_from(&schema).unwrap();
+        let _resolved_owned = ResolvedOwnedSchema::try_from(schema).unwrap();
+    }
 }
diff --git a/avro/src/schema_compatibility.rs b/avro/src/schema_compatibility.rs
index be75b91..2b21eee 100644
--- a/avro/src/schema_compatibility.rs
+++ b/avro/src/schema_compatibility.rs
@@ -16,6 +16,7 @@
 // under the License.
 
 //! Logic for checking schema compatibility
+use crate::schema::UuidSchema;
 use crate::{
     error::CompatibilityError,
     schema::{EnumSchema, FixedSchema, RecordSchema, Schema, SchemaKind},
@@ -426,13 +427,77 @@ impl SchemaCompatibility {
                         }
                     }
                 }
-                SchemaKind::Uuid => {
-                    return check_writer_type(
-                        writers_schema,
-                        readers_schema,
-                        vec![r_type, SchemaKind::String],
-                    );
-                }
+                SchemaKind::Uuid => match readers_schema {
+                    Schema::Uuid(UuidSchema::Bytes) => {
+                        return check_writer_type(
+                            writers_schema,
+                            readers_schema,
+                            vec![r_type, SchemaKind::Bytes],
+                        );
+                    }
+                    Schema::Uuid(UuidSchema::String) => {
+                        return check_writer_type(
+                            writers_schema,
+                            readers_schema,
+                            vec![r_type, SchemaKind::String],
+                        );
+                    }
+                    Schema::Uuid(UuidSchema::Fixed(FixedSchema {
+                        name: r_name,
+                        size: r_size,
+                        ..
+                    })) => match writers_schema {
+                        Schema::Uuid(UuidSchema::Fixed(FixedSchema {
+                            name: w_name,
+                            size: w_size,
+                            ..
+                        }))
+                        | Schema::Fixed(FixedSchema {
+                            name: w_name,
+                            size: w_size,
+                            ..
+                        }) => {
+                            return (w_name.name == r_name.name && w_size == 
r_size)
+                                .then_some(())
+                                .ok_or(CompatibilityError::FixedMismatch);
+                        }
+                        _ => {
+                            return Err(CompatibilityError::TypeExpected {
+                                schema_type: String::from("writers_schema"),
+                                expected_type: vec![SchemaKind::Uuid, 
SchemaKind::Fixed],
+                            });
+                        }
+                    },
+                    Schema::Null
+                    | Schema::Boolean
+                    | Schema::Int
+                    | Schema::Long
+                    | Schema::Float
+                    | Schema::Double
+                    | Schema::Bytes
+                    | Schema::String
+                    | Schema::Array(_)
+                    | Schema::Map(_)
+                    | Schema::Union(_)
+                    | Schema::Record(_)
+                    | Schema::Enum(_)
+                    | Schema::Fixed(_)
+                    | Schema::Decimal(_)
+                    | Schema::BigDecimal
+                    | Schema::Date
+                    | Schema::TimeMillis
+                    | Schema::TimeMicros
+                    | Schema::TimestampMillis
+                    | Schema::TimestampMicros
+                    | Schema::TimestampNanos
+                    | Schema::LocalTimestampMillis
+                    | Schema::LocalTimestampMicros
+                    | Schema::LocalTimestampNanos
+                    | Schema::Duration
+                    | Schema::Ref { .. } => {
+                        unreachable!("SchemaKind::Uuid can only be a 
Schema::Uuid")
+                    }
+                },
                 SchemaKind::Date | SchemaKind::TimeMillis => {
                     return check_writer_type(
                         writers_schema,
@@ -499,8 +564,19 @@ impl SchemaCompatibility {
             SchemaKind::String => {
                 check_reader_type_multi(r_type, vec![SchemaKind::Bytes, 
SchemaKind::Uuid], w_type)
             }
-            SchemaKind::Bytes => check_reader_type(r_type, SchemaKind::String, 
w_type),
-            SchemaKind::Uuid => check_reader_type(r_type, SchemaKind::String, 
w_type),
+            SchemaKind::Bytes => {
+                check_reader_type_multi(r_type, vec![SchemaKind::String, 
SchemaKind::Uuid], w_type)
+            }
+            SchemaKind::Uuid => check_reader_type_multi(
+                r_type,
+                vec![SchemaKind::String, SchemaKind::Bytes, SchemaKind::Fixed],
+                w_type,
+            ),
+            SchemaKind::Fixed => check_reader_type_multi(
+                r_type,
+                vec![SchemaKind::Duration, SchemaKind::Uuid],
+                w_type,
+            ),
             SchemaKind::Date | SchemaKind::TimeMillis => {
                 check_reader_type(r_type, SchemaKind::Int, w_type)
             }
@@ -523,6 +599,7 @@ impl SchemaCompatibility {
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::schema::{Name, UuidSchema};
     use crate::{
         Codec, Reader, Writer,
         types::{Record, Value},
@@ -1006,6 +1083,18 @@ mod tests {
 
     #[test]
     fn test_compatible_reader_writer_pairs() {
+        let uuid_fixed = FixedSchema {
+            name: Name {
+                name: String::new(),
+                namespace: None,
+            },
+            aliases: None,
+            doc: None,
+            size: 16,
+            default: None,
+            attributes: Default::default(),
+        };
+
         let compatible_schemas = vec![
             (Schema::Null, Schema::Null),
             (Schema::Long, Schema::Int),
@@ -1017,8 +1106,30 @@ mod tests {
             (Schema::String, Schema::Bytes),
             (Schema::Bytes, Schema::String),
             // logical types
-            (Schema::Uuid, Schema::Uuid),
-            (Schema::Uuid, Schema::String),
+            (
+                Schema::Uuid(UuidSchema::String),
+                Schema::Uuid(UuidSchema::String),
+            ),
+            (Schema::Uuid(UuidSchema::String), Schema::String),
+            (Schema::String, Schema::Uuid(UuidSchema::String)),
+            (
+                Schema::Uuid(UuidSchema::Bytes),
+                Schema::Uuid(UuidSchema::Bytes),
+            ),
+            (Schema::Uuid(UuidSchema::Bytes), Schema::Bytes),
+            (Schema::Bytes, Schema::Uuid(UuidSchema::Bytes)),
+            (
+                Schema::Uuid(UuidSchema::Fixed(uuid_fixed.clone())),
+                Schema::Uuid(UuidSchema::Fixed(uuid_fixed.clone())),
+            ),
+            (
+                Schema::Uuid(UuidSchema::Fixed(uuid_fixed.clone())),
+                Schema::Fixed(uuid_fixed.clone()),
+            ),
+            (
+                Schema::Fixed(uuid_fixed.clone()),
+                Schema::Uuid(UuidSchema::Fixed(uuid_fixed.clone())),
+            ),
             (Schema::Date, Schema::Int),
             (Schema::TimeMillis, Schema::Int),
             (Schema::TimeMicros, Schema::Long),
@@ -1028,7 +1139,6 @@ mod tests {
             (Schema::LocalTimestampMillis, Schema::Long),
             (Schema::LocalTimestampMicros, Schema::Long),
             (Schema::LocalTimestampNanos, Schema::Long),
-            (Schema::String, Schema::Uuid),
             (Schema::Int, Schema::Date),
             (Schema::Int, Schema::TimeMillis),
             (Schema::Long, Schema::TimeMicros),
@@ -1070,11 +1180,9 @@ mod tests {
             (nested_optional_record(), nested_record()),
         ];
 
-        assert!(
-            compatible_schemas
-                .iter()
-                .all(|(reader, writer)| SchemaCompatibility::can_read(writer, 
reader).is_ok())
-        );
+        for (reader, writer) in compatible_schemas {
+            SchemaCompatibility::can_read(&writer, &reader).unwrap();
+        }
     }
 
     fn writer_schema() -> Schema {
diff --git a/avro/src/schema_equality.rs b/avro/src/schema_equality.rs
index c14e5fb..b351584 100644
--- a/avro/src/schema_equality.rs
+++ b/avro/src/schema_equality.rs
@@ -15,6 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use crate::schema::{InnerDecimalSchema, UuidSchema};
 use crate::{
     Schema,
     schema::{
@@ -83,8 +84,6 @@ impl SchemataEq for StructFieldEq {
             (Schema::Bytes, _) => false,
             (Schema::String, Schema::String) => true,
             (Schema::String, _) => false,
-            (Schema::Uuid, Schema::Uuid) => true,
-            (Schema::Uuid, _) => false,
             (Schema::BigDecimal, Schema::BigDecimal) => true,
             (Schema::BigDecimal, _) => false,
             (Schema::Date, Schema::Date) => true,
@@ -143,9 +142,23 @@ impl SchemataEq for StructFieldEq {
                 Schema::Decimal(DecimalSchema { precision: precision_one, 
scale: scale_one, inner: inner_one }),
                 Schema::Decimal(DecimalSchema { precision: precision_two, 
scale: scale_two, inner: inner_two })
             ) => {
-                precision_one == precision_two && scale_one == scale_two && 
self.compare(inner_one, inner_two)
+                precision_one == precision_two && scale_one == scale_two && 
match (inner_one, inner_two) {
+                    (InnerDecimalSchema::Bytes, InnerDecimalSchema::Bytes) => 
true,
+                    (InnerDecimalSchema::Fixed(FixedSchema { size: size_one, 
.. }), InnerDecimalSchema::Fixed(FixedSchema { size: size_two, ..})) => {
+                        size_one == size_two
+                    }
+                    _ => false,
+                }
             }
             (Schema::Decimal(_), _) => false,
+            (Schema::Uuid(UuidSchema::Bytes), Schema::Uuid(UuidSchema::Bytes)) 
=> true,
+            (Schema::Uuid(UuidSchema::Bytes), _) => false,
+            (Schema::Uuid(UuidSchema::String), 
Schema::Uuid(UuidSchema::String)) => true,
+            (Schema::Uuid(UuidSchema::String), _) => false,
+            (Schema::Uuid(UuidSchema::Fixed(FixedSchema { size: size_one, 
..})), Schema::Uuid(UuidSchema::Fixed(FixedSchema { size: size_two, ..}))) => {
+                size_one == size_two
+            },
+            (Schema::Uuid(UuidSchema::Fixed(_)), _) => false,
             (
                 Schema::Array(ArraySchema { items: items_one, ..}),
                 Schema::Array(ArraySchema { items: items_two, ..})
@@ -212,7 +225,7 @@ pub(crate) fn compare_schemata(schema_one: &Schema, 
schema_two: &Schema) -> bool
 #[allow(non_snake_case)]
 mod tests {
     use super::*;
-    use crate::schema::{Name, RecordFieldOrder};
+    use crate::schema::{InnerDecimalSchema, Name, RecordFieldOrder};
     use apache_avro_test_helper::TestResult;
     use serde_json::Value;
     use std::collections::BTreeMap;
@@ -243,7 +256,6 @@ mod tests {
     test_primitives!(Double);
     test_primitives!(Bytes);
     test_primitives!(String);
-    test_primitives!(Uuid);
     test_primitives!(BigDecimal);
     test_primitives!(Date);
     test_primitives!(Duration);
@@ -351,7 +363,7 @@ mod tests {
         let schema_one = Schema::Decimal(DecimalSchema {
             precision: 10,
             scale: 2,
-            inner: Box::new(Schema::Bytes),
+            inner: InnerDecimalSchema::Bytes,
         });
         assert!(!SPECIFICATION_EQ.compare(&schema_one, &Schema::Boolean));
         assert!(!STRUCT_FIELD_EQ.compare(&schema_one, &Schema::Boolean));
@@ -359,7 +371,7 @@ mod tests {
         let schema_two = Schema::Decimal(DecimalSchema {
             precision: 10,
             scale: 2,
-            inner: Box::new(Schema::Bytes),
+            inner: InnerDecimalSchema::Bytes,
         });
 
         let specification_eq_res = SPECIFICATION_EQ.compare(&schema_one, 
&schema_two);
@@ -544,4 +556,60 @@ mod tests {
         assert_eq!(specification_eq_res, struct_field_eq_res);
         Ok(())
     }
+
+    #[test]
+    fn test_uuid_compare_uuid() -> TestResult {
+        let string = Schema::Uuid(UuidSchema::String);
+        let bytes = Schema::Uuid(UuidSchema::Bytes);
+        let mut fixed_schema = FixedSchema {
+            name: Name {
+                name: "some_name".to_string(),
+                namespace: None,
+            },
+            aliases: None,
+            doc: None,
+            size: 16,
+            default: None,
+            attributes: Default::default(),
+        };
+        let fixed = Schema::Uuid(UuidSchema::Fixed(fixed_schema.clone()));
+        fixed_schema
+            .attributes
+            .insert("Something".to_string(), Value::Null);
+        let fixed_different = Schema::Uuid(UuidSchema::Fixed(fixed_schema));
+
+        assert!(SPECIFICATION_EQ.compare(&string, &string));
+        assert!(STRUCT_FIELD_EQ.compare(&string, &string));
+        assert!(SPECIFICATION_EQ.compare(&bytes, &bytes));
+        assert!(STRUCT_FIELD_EQ.compare(&bytes, &bytes));
+        assert!(SPECIFICATION_EQ.compare(&fixed, &fixed));
+        assert!(STRUCT_FIELD_EQ.compare(&fixed, &fixed));
+
+        assert!(!SPECIFICATION_EQ.compare(&string, &bytes));
+        assert!(!STRUCT_FIELD_EQ.compare(&string, &bytes));
+        assert!(!SPECIFICATION_EQ.compare(&bytes, &string));
+        assert!(!STRUCT_FIELD_EQ.compare(&bytes, &string));
+        assert!(!SPECIFICATION_EQ.compare(&string, &fixed));
+        assert!(!STRUCT_FIELD_EQ.compare(&string, &fixed));
+        assert!(!SPECIFICATION_EQ.compare(&fixed, &string));
+        assert!(!STRUCT_FIELD_EQ.compare(&fixed, &string));
+        assert!(!SPECIFICATION_EQ.compare(&bytes, &fixed));
+        assert!(!STRUCT_FIELD_EQ.compare(&bytes, &fixed));
+        assert!(!SPECIFICATION_EQ.compare(&fixed, &bytes));
+        assert!(!STRUCT_FIELD_EQ.compare(&fixed, &bytes));
+
+        assert!(SPECIFICATION_EQ.compare(&fixed, &fixed_different));
+        assert!(STRUCT_FIELD_EQ.compare(&fixed, &fixed_different));
+        assert!(SPECIFICATION_EQ.compare(&fixed_different, &fixed));
+        assert!(STRUCT_FIELD_EQ.compare(&fixed_different, &fixed));
+
+        let strict = StructFieldEq {
+            include_attributes: true,
+        };
+
+        assert!(!strict.compare(&fixed, &fixed_different));
+        assert!(!strict.compare(&fixed_different, &fixed));
+
+        Ok(())
+    }
 }
diff --git a/avro/src/serde/ser_schema.rs b/avro/src/serde/ser_schema.rs
index 02a65bc..33d5cd5 100644
--- a/avro/src/serde/ser_schema.rs
+++ b/avro/src/serde/ser_schema.rs
@@ -18,6 +18,7 @@
 //! Logic for serde-compatible schema-aware serialization
 //! which writes directly to a `Write` stream
 
+use crate::schema::{InnerDecimalSchema, UuidSchema};
 use crate::{
     bigdecimal::big_decimal_as_bytes,
     encode::{encode_int, encode_long},
@@ -1054,7 +1055,9 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> {
         };
 
         match schema {
-            Schema::String | Schema::Bytes | Schema::Uuid => 
self.write_bytes(value.as_bytes()),
+            Schema::String | Schema::Bytes | Schema::Uuid(UuidSchema::String) 
=> {
+                self.write_bytes(value.as_bytes())
+            }
             Schema::BigDecimal => {
                 // If we get a string for a `BigDecimal` type, expect a 
display string representation, such as "12.75"
                 let decimal_val =
@@ -1084,7 +1087,7 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> {
                     match variant_schema {
                         Schema::String
                         | Schema::Bytes
-                        | Schema::Uuid
+                        | Schema::Uuid(UuidSchema::String)
                         | Schema::Fixed(_)
                         | Schema::Ref { name: _ } => {
                             encode_int(i as i32, &mut *self.writer)?;
@@ -1123,10 +1126,11 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> {
         };
 
         match schema {
-            Schema::String | Schema::Bytes | Schema::Uuid | Schema::BigDecimal 
=> {
-                self.write_bytes(value)
-            }
-            Schema::Fixed(fixed_schema) => {
+            Schema::String
+            | Schema::Bytes
+            | Schema::Uuid(UuidSchema::Bytes | UuidSchema::String)
+            | Schema::BigDecimal => self.write_bytes(value),
+            Schema::Fixed(fixed_schema) | 
Schema::Uuid(UuidSchema::Fixed(fixed_schema)) => {
                 if value.len() == fixed_schema.size {
                     self.writer
                         .write(value)
@@ -1151,31 +1155,31 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> {
                     )))
                 }
             }
-            Schema::Decimal(decimal_schema) => match 
decimal_schema.inner.as_ref() {
-                Schema::Bytes => self.write_bytes(value),
-                Schema::Fixed(fixed_schema) => match 
fixed_schema.size.checked_sub(value.len()) {
-                    Some(pad) => {
-                        let pad_val = match value.len() {
-                            0 => 0,
-                            _ => value[0],
-                        };
-                        let padding = vec![pad_val; pad];
-                        self.writer
-                            .write(padding.as_slice())
-                            .map_err(Details::WriteBytes)?;
-                        self.writer
-                            .write(value)
-                            .map_err(|e| Details::WriteBytes(e).into())
-                    }
-                    None => Err(Details::CompareFixedSizes {
-                        size: fixed_schema.size,
-                        n: value.len(),
+            Schema::Decimal(decimal_schema) => match &decimal_schema.inner {
+                InnerDecimalSchema::Bytes => self.write_bytes(value),
+                InnerDecimalSchema::Fixed(fixed_schema) => {
+                    match fixed_schema.size.checked_sub(value.len()) {
+                        Some(pad) => {
+                            let pad_val = match value.len() {
+                                0 => 0,
+                                _ => value[0],
+                            };
+                            let padding = vec![pad_val; pad];
+                            let mut bytes_written = self
+                                .writer
+                                .write(padding.as_slice())
+                                .map_err(Details::WriteBytes)?;
+                            bytes_written +=
+                                
self.writer.write(value).map_err(Details::WriteBytes)?;
+                            Ok(bytes_written)
+                        }
+                        None => Err(Details::CompareFixedSizes {
+                            size: fixed_schema.size,
+                            n: value.len(),
+                        }
+                        .into()),
                     }
-                    .into()),
-                },
-                unsupported => Err(create_error(format!(
-                    "Decimal schema's inner should be Bytes or Fixed schema. 
Got: {unsupported}"
-                ))),
+                }
             },
             Schema::Ref { name } => {
                 let ref_schema = self.get_ref_schema(name)?;
@@ -1186,7 +1190,7 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> {
                     match variant_schema {
                         Schema::String
                         | Schema::Bytes
-                        | Schema::Uuid
+                        | Schema::Uuid(_)
                         | Schema::BigDecimal
                         | Schema::Fixed(_)
                         | Schema::Duration
@@ -2590,8 +2594,10 @@ mod tests {
     fn test_serialize_uuid() -> TestResult {
         let schema = Schema::parse_str(
             r#"{
-            "type": "string",
-            "logicalType": "uuid"
+            "type": "fixed",
+            "size": 16,
+            "logicalType": "uuid",
+            "name": "FixedUuid"
         }"#,
         )?;
 
@@ -2620,7 +2626,7 @@ mod tests {
         assert_eq!(
             buffer.as_slice(),
             &[
-                32, 140, 40, 218, 129, 35, 140, 67, 38, 189, 221, 78, 61, 0, 
204, 80, 153
+                140, 40, 218, 129, 35, 140, 67, 38, 189, 221, 78, 61, 0, 204, 
80, 153
             ]
         );
 
@@ -2911,10 +2917,10 @@ mod tests {
         assert_eq!(
             buffer.as_slice(),
             &[
-                8, 116, 101, 115, 116, 20, 10, 6, 0, 195, 104, 4, 32, 140, 40, 
218, 129, 35, 140,
-                67, 38, 189, 221, 78, 61, 0, 204, 80, 152, 2, 20, 105, 110, 
110, 101, 114, 95, 116,
-                101, 115, 116, 200, 1, 8, 4, 78, 70, 4, 32, 140, 40, 218, 129, 
35, 140, 67, 38,
-                189, 221, 78, 61, 0, 204, 80, 153, 0
+                8, 116, 101, 115, 116, 20, 10, 6, 0, 195, 104, 4, 140, 40, 
218, 129, 35, 140, 67,
+                38, 189, 221, 78, 61, 0, 204, 80, 152, 2, 20, 105, 110, 110, 
101, 114, 95, 116,
+                101, 115, 116, 200, 1, 8, 4, 78, 70, 4, 140, 40, 218, 129, 35, 
140, 67, 38, 189,
+                221, 78, 61, 0, 204, 80, 153, 0
             ]
         );
 
diff --git a/avro/src/types.rs b/avro/src/types.rs
index 5a54c3f..8cca5b9 100644
--- a/avro/src/types.rs
+++ b/avro/src/types.rs
@@ -16,6 +16,7 @@
 // under the License.
 
 //! Logic handling the intermediate representation of Avro values.
+use crate::schema::{InnerDecimalSchema, UuidSchema};
 use crate::{
     AvroResult, Error,
     bigdecimal::{deserialize_big_decimal, serialize_big_decimal},
@@ -441,14 +442,15 @@ impl Value {
             (&Value::Decimal(_), &Schema::Decimal { .. }) => None,
             (&Value::BigDecimal(_), &Schema::BigDecimal) => None,
             (&Value::Duration(_), &Schema::Duration) => None,
-            (&Value::Uuid(_), &Schema::Uuid) => None,
+            (&Value::Uuid(_), &Schema::Uuid(_)) => None,
             (&Value::Float(_), &Schema::Float) => None,
             (&Value::Float(_), &Schema::Double) => None,
             (&Value::Double(_), &Schema::Double) => None,
             (&Value::Bytes(_), &Schema::Bytes) => None,
             (&Value::Bytes(_), &Schema::Decimal { .. }) => None,
+            (&Value::Bytes(_), &Schema::Uuid(UuidSchema::Bytes)) => None,
             (&Value::String(_), &Schema::String) => None,
-            (&Value::String(_), &Schema::Uuid) => None,
+            (&Value::String(_), &Schema::Uuid(UuidSchema::String)) => None,
             (&Value::Fixed(n, _), &Schema::Fixed(FixedSchema { size, .. })) => 
{
                 if n != size {
                     Some(format!(
@@ -478,6 +480,20 @@ impl Value {
                     None
                 }
             }
+            (&Value::Fixed(n, _), Schema::Uuid(UuidSchema::Fixed(size, ..))) 
=> {
+                if size.size != 16 {
+                    Some(format!(
+                        "The schema's size ('{}') must be exactly 16 to be a 
Uuid",
+                        size.size
+                    ))
+                } else if n != 16 {
+                    Some(format!(
+                        "The value's size ('{n}') must be exactly 16 to be a 
Uuid"
+                    ))
+                } else {
+                    None
+                }
+            }
             // TODO: check precision against n
             (&Value::Fixed(_n, _), &Schema::Decimal { .. }) => None,
             (Value::String(s), Schema::Enum(EnumSchema { symbols, .. })) => {
@@ -653,8 +669,8 @@ impl Value {
             };
             self = v;
         }
-        match *schema {
-            Schema::Ref { ref name } => {
+        match schema {
+            Schema::Ref { name } => {
                 let name = name.fully_qualified_name(enclosing_namespace);
 
                 if let Some(resolved) = names.get(&name) {
@@ -673,27 +689,23 @@ impl Value {
             Schema::Double => self.resolve_double(),
             Schema::Bytes => self.resolve_bytes(),
             Schema::String => self.resolve_string(),
-            Schema::Fixed(FixedSchema { size, .. }) => 
self.resolve_fixed(size),
-            Schema::Union(ref inner) => {
+            Schema::Fixed(FixedSchema { size, .. }) => 
self.resolve_fixed(*size),
+            Schema::Union(inner) => {
                 self.resolve_union(inner, names, enclosing_namespace, 
field_default)
             }
             Schema::Enum(EnumSchema {
-                ref symbols,
-                ref default,
-                ..
+                symbols, default, ..
             }) => self.resolve_enum(symbols, default, field_default),
-            Schema::Array(ref inner) => {
-                self.resolve_array(&inner.items, names, enclosing_namespace)
-            }
-            Schema::Map(ref inner) => self.resolve_map(&inner.types, names, 
enclosing_namespace),
-            Schema::Record(RecordSchema { ref fields, .. }) => {
+            Schema::Array(inner) => self.resolve_array(&inner.items, names, 
enclosing_namespace),
+            Schema::Map(inner) => self.resolve_map(&inner.types, names, 
enclosing_namespace),
+            Schema::Record(RecordSchema { fields, .. }) => {
                 self.resolve_record(fields, names, enclosing_namespace)
             }
             Schema::Decimal(DecimalSchema {
                 scale,
                 precision,
-                ref inner,
-            }) => self.resolve_decimal(precision, scale, inner),
+                inner,
+            }) => self.resolve_decimal(*precision, *scale, inner),
             Schema::BigDecimal => self.resolve_bigdecimal(),
             Schema::Date => self.resolve_date(),
             Schema::TimeMillis => self.resolve_time_millis(),
@@ -705,18 +717,28 @@ impl Value {
             Schema::LocalTimestampMicros => 
self.resolve_local_timestamp_micros(),
             Schema::LocalTimestampNanos => 
self.resolve_local_timestamp_nanos(),
             Schema::Duration => self.resolve_duration(),
-            Schema::Uuid => self.resolve_uuid(),
+            Schema::Uuid(inner) => self.resolve_uuid(inner),
         }
     }
 
-    fn resolve_uuid(self) -> Result<Self, Error> {
-        Ok(match self {
-            uuid @ Value::Uuid(_) => uuid,
-            Value::String(ref string) => {
+    fn resolve_uuid(self, inner: &UuidSchema) -> Result<Self, Error> {
+        let value = match (self, inner) {
+            (uuid @ Value::Uuid(_), _) => uuid,
+            (Value::String(ref string), UuidSchema::String) => {
                 
Value::Uuid(Uuid::from_str(string).map_err(Details::ConvertStrToUuid)?)
             }
-            other => return Err(Details::GetUuid(other).into()),
-        })
+            (Value::Bytes(ref bytes), UuidSchema::Bytes) => {
+                
Value::Uuid(Uuid::from_slice(bytes).map_err(Details::ConvertSliceToUuid)?)
+            }
+            (Value::Fixed(n, ref bytes), UuidSchema::Fixed(_)) => {
+                if n != 16 {
+                    return Err(Details::ConvertFixedToUuid(n).into());
+                }
+                
Value::Uuid(Uuid::from_slice(bytes).map_err(Details::ConvertSliceToUuid)?)
+            }
+            (other, _) => return Err(Details::GetUuid(other).into()),
+        };
+        Ok(value)
     }
 
     fn resolve_bigdecimal(self) -> Result<Self, Error> {
@@ -747,19 +769,18 @@ impl Value {
         self,
         precision: Precision,
         scale: Scale,
-        inner: &Schema,
+        inner: &InnerDecimalSchema,
     ) -> Result<Self, Error> {
         if scale > precision {
             return Err(Details::GetScaleAndPrecision { scale, precision 
}.into());
         }
         match inner {
-            &Schema::Fixed(FixedSchema { size, .. }) => {
+            &InnerDecimalSchema::Fixed(FixedSchema { size, .. }) => {
                 if max_prec_for_len(size)? < precision {
                     return Err(Details::GetScaleWithFixedSize { size, 
precision }.into());
                 }
             }
-            Schema::Bytes => (),
-            _ => return 
Err(Details::ResolveDecimalSchema(inner.into()).into()),
+            InnerDecimalSchema::Bytes => (),
         };
         match self {
             Value::Decimal(num) => {
@@ -1716,7 +1737,7 @@ Field with name '"b"' is not a member of the map items"#,
         value.clone().resolve(&Schema::Decimal(DecimalSchema {
             precision: 10,
             scale: 4,
-            inner: Box::new(Schema::Bytes),
+            inner: InnerDecimalSchema::Bytes,
         }))?;
         assert!(value.resolve(&Schema::String).is_err());
 
@@ -1731,7 +1752,7 @@ Field with name '"b"' is not a member of the map items"#,
                 .resolve(&Schema::Decimal(DecimalSchema {
                     precision: 2,
                     scale: 3,
-                    inner: Box::new(Schema::Bytes),
+                    inner: InnerDecimalSchema::Bytes,
                 }))
                 .is_err()
         );
@@ -1745,7 +1766,7 @@ Field with name '"b"' is not a member of the map items"#,
                 .resolve(&Schema::Decimal(DecimalSchema {
                     precision: 1,
                     scale: 0,
-                    inner: Box::new(Schema::Bytes),
+                    inner: InnerDecimalSchema::Bytes,
                 }))
                 .is_ok()
         );
@@ -1760,14 +1781,14 @@ Field with name '"b"' is not a member of the map 
items"#,
                 .resolve(&Schema::Decimal(DecimalSchema {
                     precision: 10,
                     scale: 1,
-                    inner: Box::new(Schema::Fixed(FixedSchema {
+                    inner: InnerDecimalSchema::Fixed(FixedSchema {
                         name: Name::new("decimal").unwrap(),
                         aliases: None,
                         size: 20,
                         doc: None,
                         default: None,
                         attributes: Default::default(),
-                    }))
+                    })
                 }))
                 .is_ok()
         );
@@ -1870,7 +1891,34 @@ Field with name '"b"' is not a member of the map items"#,
     #[test]
     fn resolve_uuid() -> TestResult {
         let value = 
Value::Uuid(Uuid::parse_str("1481531d-ccc9-46d9-a56f-5b67459c0537")?);
-        assert!(value.clone().resolve(&Schema::Uuid).is_ok());
+        assert!(
+            value
+                .clone()
+                .resolve(&Schema::Uuid(UuidSchema::String))
+                .is_ok()
+        );
+        assert!(
+            value
+                .clone()
+                .resolve(&Schema::Uuid(UuidSchema::Bytes))
+                .is_ok()
+        );
+        assert!(
+            value
+                .clone()
+                .resolve(&Schema::Uuid(UuidSchema::Fixed(FixedSchema {
+                    name: Name {
+                        name: "some_name".to_string(),
+                        namespace: None
+                    },
+                    aliases: None,
+                    doc: None,
+                    size: 16,
+                    default: None,
+                    attributes: Default::default(),
+                })))
+                .is_ok()
+        );
         assert!(value.resolve(&Schema::TimestampMicros).is_err());
 
         Ok(())
diff --git a/avro/src/writer.rs b/avro/src/writer.rs
index a1ae239..7fda617 100644
--- a/avro/src/writer.rs
+++ b/avro/src/writer.rs
@@ -789,6 +789,7 @@ mod tests {
     use serde::{Deserialize, Serialize};
     use uuid::Uuid;
 
+    use crate::schema::InnerDecimalSchema;
     use crate::{codec::DeflateSettings, error::Details};
     use apache_avro_test_helper::TestResult;
 
@@ -990,41 +991,41 @@ mod tests {
     #[test]
     fn decimal_fixed() -> TestResult {
         let size = 30;
-        let inner = Schema::Fixed(FixedSchema {
+        let fixed = FixedSchema {
             name: Name::new("decimal")?,
             aliases: None,
             doc: None,
             size,
             default: None,
             attributes: Default::default(),
-        });
+        };
+        let inner = InnerDecimalSchema::Fixed(fixed.clone());
         let value = vec![0u8; size];
         logical_type_test(
             r#"{"type": {"type": "fixed", "size": 30, "name": "decimal"}, 
"logicalType": "decimal", "precision": 20, "scale": 5}"#,
             &Schema::Decimal(DecimalSchema {
                 precision: 20,
                 scale: 5,
-                inner: Box::new(inner.clone()),
+                inner,
             }),
             Value::Decimal(Decimal::from(value.clone())),
-            &inner,
+            &Schema::Fixed(fixed),
             Value::Fixed(size, value),
         )
     }
 
     #[test]
     fn decimal_bytes() -> TestResult {
-        let inner = Schema::Bytes;
         let value = vec![0u8; 10];
         logical_type_test(
             r#"{"type": "bytes", "logicalType": "decimal", "precision": 4, 
"scale": 3}"#,
             &Schema::Decimal(DecimalSchema {
                 precision: 4,
                 scale: 3,
-                inner: Box::new(inner.clone()),
+                inner: InnerDecimalSchema::Bytes,
             }),
             Value::Decimal(Decimal::from(value.clone())),
-            &inner,
+            &Schema::Bytes,
             value,
         )
     }
diff --git a/avro/tests/serde_human_readable_false.rs 
b/avro/tests/serde_human_readable_false.rs
index 1a76ff5..3095657 100644
--- a/avro/tests/serde_human_readable_false.rs
+++ b/avro/tests/serde_human_readable_false.rs
@@ -35,11 +35,11 @@ fn avro_rs_53_uuid_with_fixed() -> TestResult {
     };
     let mut buffer = Vec::new();
 
-    // serialize the Uuid as Bytes
+    // serialize the Uuid as Fixed
     assert!(!apache_avro::util::set_serde_human_readable(false));
     let bytes = SpecificSingleObjectWriter::<Comment>::with_capacity(64)?
         .write_ref(&payload, &mut buffer)?;
-    assert_eq!(bytes, 27);
+    assert_eq!(bytes, 26);
 
     Ok(())
 }
diff --git a/avro/tests/serde_human_readable_true.rs 
b/avro/tests/serde_human_readable_true.rs
index b651b38..0b6d665 100644
--- a/avro/tests/serde_human_readable_true.rs
+++ b/avro/tests/serde_human_readable_true.rs
@@ -3,7 +3,7 @@ use apache_avro_test_helper::TestResult;
 use serde::{Deserialize, Serialize};
 
 #[test]
-fn avro_rs_53_uuid_with_fixed_true() -> TestResult {
+fn avro_rs_53_uuid_with_string_true() -> TestResult {
     #[derive(Debug, Serialize, Deserialize)]
     struct Comment {
         id: apache_avro::Uuid,
@@ -18,10 +18,9 @@ fn avro_rs_53_uuid_with_fixed_true() -> TestResult {
                         "fields" : [ {
                           "name" : "id",
                           "type" : {
-                            "type" : "fixed",
-                            "size" : 16,
+                            "type" : "string",
                             "logicalType" : "uuid",
-                            "name": "FixedUUID"
+                            "name": "StringUUID"
                           }
                         } ]
                      }"#,

Reply via email to