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

mgrigorov 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 fec1951  feat!: Add support for `char`,`u64`,`u128`,`i128` (#414)
fec1951 is described below

commit fec19517dc1bde4ac726a03d02e9c76add116295
Author: Kriskras99 <[email protected]>
AuthorDate: Tue Jan 20 08:10:33 2026 +0100

    feat!: Add support for `char`,`u64`,`u128`,`i128` (#414)
    
    * feat: Add support for `char`,`u64`,`u128`,`i128`
    
    * fix: Return the correct amount of bytes written
    
    * fix: proper deserialisation for `u64`/`u128`/`i128`
    
    * fix: Fix error types and descriptions
---
 avro/src/error.rs            |  12 ++
 avro/src/schema/mod.rs       | 146 +++++++++++++--
 avro/src/serde/de.rs         | 406 +++++++++++++++++++++++++++++++++++-------
 avro/src/serde/ser.rs        |  54 +++++-
 avro/src/serde/ser_schema.rs | 411 ++++++++++++++++++++++++++++++++++++++++++-
 avro_derive/tests/derive.rs  |  58 ++++++
 6 files changed, 1006 insertions(+), 81 deletions(-)

diff --git a/avro/src/error.rs b/avro/src/error.rs
index 8e42ce1..cbb00c0 100644
--- a/avro/src/error.rs
+++ b/avro/src/error.rs
@@ -329,6 +329,18 @@ pub enum Details {
     #[error("Cannot convert i32 to usize: {1}")]
     ConvertI32ToUsize(#[source] std::num::TryFromIntError, i32),
 
+    #[error("Cannot convert i64 to u64: {1}")]
+    ConvertI64ToU64(#[source] std::num::TryFromIntError, i64),
+
+    #[error("Cannot convert i32 to u64: {1}")]
+    ConvertI32ToU64(#[source] std::num::TryFromIntError, i32),
+
+    #[error("Cannot convert i64 to u128: {1}")]
+    ConvertI64ToU128(#[source] std::num::TryFromIntError, i64),
+
+    #[error("Cannot convert i32 to u128: {1}")]
+    ConvertI32ToU128(#[source] std::num::TryFromIntError, i32),
+
     #[error("Invalid JSON value for decimal precision/scale integer: {0}")]
     GetPrecisionOrScaleFromJson(serde_json::Number),
 
diff --git a/avro/src/schema/mod.rs b/avro/src/schema/mod.rs
index 90a48b8..42a0b80 100644
--- a/avro/src/schema/mod.rs
+++ b/avro/src/schema/mod.rs
@@ -2258,10 +2258,6 @@ fn field_ordering_position(field: &str) -> Option<usize> 
{
 /// through `derive` feature. Do not implement directly!
 /// Implement [`AvroSchemaComponent`] to get this trait
 /// through a blanket implementation.
-///
-/// Note: This trait is **not** implemented for `char` and `u64`. `char` is a 
32-bit value
-/// that does not have a logical mapping to an Avro schema. `u64` is too large 
to fit in a
-/// Avro `long`.
 pub trait AvroSchema {
     fn get_schema() -> Schema;
 }
@@ -2270,10 +2266,6 @@ pub trait AvroSchema {
 /// implementation available through `derive` feature. This is what is 
implemented by
 /// the `derive(AvroSchema)` macro.
 ///
-/// Note: This trait is **not** implemented for `char` and `u64`. `char` is a 
32-bit value
-/// that does not have a logical mapping to an Avro schema. `u64` is too large 
to fit in a
-/// Avro `long`.
-///
 /// # Implementation guide
 ///
 /// ### Simple implementation
@@ -2357,6 +2349,7 @@ impl_schema!(f32, Schema::Float);
 impl_schema!(f64, Schema::Double);
 impl_schema!(String, Schema::String);
 impl_schema!(str, Schema::String);
+impl_schema!(char, Schema::String);
 
 impl<T> AvroSchemaComponent for &T
 where
@@ -2516,6 +2509,78 @@ impl AvroSchemaComponent for uuid::Uuid {
     }
 }
 
+impl AvroSchemaComponent for u64 {
+    /// The schema is [`Schema::Fixed`] of size 8 with the name `u64`.
+    #[expect(clippy::map_entry, reason = "We don't use the value from the 
map")]
+    fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace: 
&Namespace) -> Schema {
+        let name = Name::new("u64")
+            .expect("Name is valid")
+            .fully_qualified_name(enclosing_namespace);
+        if named_schemas.contains_key(&name) {
+            Schema::Ref { name }
+        } else {
+            let schema = Schema::Fixed(FixedSchema {
+                name: name.clone(),
+                aliases: None,
+                doc: None,
+                size: 8,
+                default: None,
+                attributes: Default::default(),
+            });
+            named_schemas.insert(name, schema.clone());
+            schema
+        }
+    }
+}
+
+impl AvroSchemaComponent for u128 {
+    /// The schema is [`Schema::Fixed`] of size 16 with the name `u128`.
+    #[expect(clippy::map_entry, reason = "We don't use the value from the 
map")]
+    fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace: 
&Namespace) -> Schema {
+        let name = Name::new("u128")
+            .expect("Name is valid")
+            .fully_qualified_name(enclosing_namespace);
+        if named_schemas.contains_key(&name) {
+            Schema::Ref { name }
+        } else {
+            let schema = Schema::Fixed(FixedSchema {
+                name: name.clone(),
+                aliases: None,
+                doc: None,
+                size: 16,
+                default: None,
+                attributes: Default::default(),
+            });
+            named_schemas.insert(name, schema.clone());
+            schema
+        }
+    }
+}
+
+impl AvroSchemaComponent for i128 {
+    /// The schema is [`Schema::Fixed`] of size 16 with the name `i128`.
+    #[expect(clippy::map_entry, reason = "We don't use the value from the 
map")]
+    fn get_schema_in_ctxt(named_schemas: &mut Names, enclosing_namespace: 
&Namespace) -> Schema {
+        let name = Name::new("i128")
+            .expect("Name is valid")
+            .fully_qualified_name(enclosing_namespace);
+        if named_schemas.contains_key(&name) {
+            Schema::Ref { name }
+        } else {
+            let schema = Schema::Fixed(FixedSchema {
+                name: name.clone(),
+                aliases: None,
+                doc: None,
+                size: 16,
+                default: None,
+                attributes: Default::default(),
+            });
+            named_schemas.insert(name, schema.clone());
+            schema
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -7367,8 +7432,69 @@ mod tests {
             Schema::union(vec![
                 Schema::Null,
                 Schema::array(Schema::array(Schema::Int))
-            ])
-            .unwrap()
+            ])?
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_char() -> TestResult {
+        let schema = char::get_schema();
+        assert_eq!(schema, Schema::String);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_u64() -> TestResult {
+        let schema = u64::get_schema();
+        assert_eq!(
+            schema,
+            Schema::Fixed(FixedSchema {
+                name: Name::new("u64")?,
+                aliases: None,
+                doc: None,
+                size: 8,
+                default: None,
+                attributes: Default::default(),
+            })
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_i128() -> TestResult {
+        let schema = i128::get_schema();
+        assert_eq!(
+            schema,
+            Schema::Fixed(FixedSchema {
+                name: Name::new("i128")?,
+                aliases: None,
+                doc: None,
+                size: 16,
+                default: None,
+                attributes: Default::default(),
+            })
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_u128() -> TestResult {
+        let schema = u128::get_schema();
+        assert_eq!(
+            schema,
+            Schema::Fixed(FixedSchema {
+                name: Name::new("u128")?,
+                aliases: None,
+                doc: None,
+                size: 16,
+                default: None,
+                attributes: Default::default(),
+            })
         );
 
         Ok(())
diff --git a/avro/src/serde/de.rs b/avro/src/serde/de.rs
index ce42576..257dd6a 100644
--- a/avro/src/serde/de.rs
+++ b/avro/src/serde/de.rs
@@ -22,6 +22,7 @@ use serde::{
     de::{self, DeserializeSeed, Deserializer as _, Visitor},
     forward_to_deserialize_any,
 };
+use std::ops::Deref;
 use std::{
     collections::{
         HashMap,
@@ -382,26 +383,241 @@ impl<'de> de::Deserializer<'de> for &Deserializer<'de> {
     }
 
     forward_to_deserialize_any! {
-        bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64
+        bool i8 i16 i32 i64 u8 u16 u32 f32 f64
     }
 
-    fn deserialize_char<V>(self, _: V) -> Result<V::Value, Self::Error>
+    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
     where
         V: Visitor<'de>,
     {
-        Err(de::Error::custom("avro does not support char"))
+        match self.input {
+            Value::Int(i) | Value::Date(i) | Value::TimeMillis(i) => {
+                let n = u64::try_from(*i).map_err(|e| 
Details::ConvertI32ToU64(e, *i))?;
+                visitor.visit_u64(n)
+            }
+            Value::Long(i)
+            | Value::TimeMicros(i)
+            | Value::TimestampMillis(i)
+            | Value::TimestampMicros(i)
+            | Value::TimestampNanos(i)
+            | Value::LocalTimestampMillis(i)
+            | Value::LocalTimestampMicros(i)
+            | Value::LocalTimestampNanos(i) => {
+                let n = u64::try_from(*i).map_err(|e| 
Details::ConvertI64ToU64(e, *i))?;
+                visitor.visit_u64(n)
+            }
+            Value::Fixed(8, bytes) => {
+                let n = 
u64::from_le_bytes(bytes.as_slice().try_into().expect("Size is 8"));
+                visitor.visit_u64(n)
+            }
+            Value::Union(_i, x) => match x.deref() {
+                Value::Int(i) | Value::Date(i) | Value::TimeMillis(i) => {
+                    let n = u64::try_from(*i).map_err(|e| 
Details::ConvertI32ToU64(e, *i))?;
+                    visitor.visit_u64(n)
+                }
+                Value::Long(i)
+                | Value::TimeMicros(i)
+                | Value::TimestampMillis(i)
+                | Value::TimestampMicros(i)
+                | Value::TimestampNanos(i)
+                | Value::LocalTimestampMillis(i)
+                | Value::LocalTimestampMicros(i)
+                | Value::LocalTimestampNanos(i) => {
+                    let n = u64::try_from(*i).map_err(|e| 
Details::ConvertI64ToU64(e, *i))?;
+                    visitor.visit_u64(n)
+                }
+                Value::Fixed(8, bytes) => {
+                    let n = 
u64::from_le_bytes(bytes.as_slice().try_into().expect("Size is 8"));
+                    visitor.visit_u64(n)
+                }
+                _ => Err(de::Error::custom(format!(
+                    "Expected a Int|Long|Fixed(8), but got {:?}",
+                    self.input
+                ))),
+            },
+            _ => Err(de::Error::custom(format!(
+                "Expected a Int|Long|Fixed(8), but got {:?}",
+                self.input
+            ))),
+        }
+    }
+
+    fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input {
+            Value::Int(i) | Value::Date(i) | Value::TimeMillis(i) => {
+                let n = u128::try_from(*i).map_err(|e| 
Details::ConvertI32ToU128(e, *i))?;
+                visitor.visit_u128(n)
+            }
+            Value::Long(i)
+            | Value::TimeMicros(i)
+            | Value::TimestampMillis(i)
+            | Value::TimestampMicros(i)
+            | Value::TimestampNanos(i)
+            | Value::LocalTimestampMillis(i)
+            | Value::LocalTimestampMicros(i)
+            | Value::LocalTimestampNanos(i) => {
+                let n = u128::try_from(*i).map_err(|e| 
Details::ConvertI64ToU128(e, *i))?;
+                visitor.visit_u128(n)
+            }
+            Value::Fixed(16, bytes) => {
+                let n = 
u128::from_le_bytes(bytes.as_slice().try_into().expect("Size is 16"));
+                visitor.visit_u128(n)
+            }
+            Value::Union(_i, x) => match x.deref() {
+                Value::Int(i) | Value::Date(i) | Value::TimeMillis(i) => {
+                    let n = u128::try_from(*i).map_err(|e| 
Details::ConvertI32ToU128(e, *i))?;
+                    visitor.visit_u128(n)
+                }
+                Value::Long(i)
+                | Value::TimeMicros(i)
+                | Value::TimestampMillis(i)
+                | Value::TimestampMicros(i)
+                | Value::TimestampNanos(i)
+                | Value::LocalTimestampMillis(i)
+                | Value::LocalTimestampMicros(i)
+                | Value::LocalTimestampNanos(i) => {
+                    let n = u128::try_from(*i).map_err(|e| 
Details::ConvertI64ToU128(e, *i))?;
+                    visitor.visit_u128(n)
+                }
+                Value::Fixed(16, bytes) => {
+                    let n = 
u128::from_le_bytes(bytes.as_slice().try_into().expect("Size is 16"));
+                    visitor.visit_u128(n)
+                }
+                _ => Err(de::Error::custom(format!(
+                    "Expected a Int|Long|Fixed(16), but got {:?}",
+                    self.input
+                ))),
+            },
+            _ => Err(de::Error::custom(format!(
+                "Expected a Int|Long|Fixed(16), but got {:?}",
+                self.input
+            ))),
+        }
+    }
+
+    fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input {
+            Value::Int(i) | Value::Date(i) | Value::TimeMillis(i) => {
+                visitor.visit_i128(i128::from(*i))
+            }
+            Value::Long(i)
+            | Value::TimeMicros(i)
+            | Value::TimestampMillis(i)
+            | Value::TimestampMicros(i)
+            | Value::TimestampNanos(i)
+            | Value::LocalTimestampMillis(i)
+            | Value::LocalTimestampMicros(i)
+            | Value::LocalTimestampNanos(i) => 
visitor.visit_i128(i128::from(*i)),
+            Value::Fixed(16, bytes) => {
+                let n = 
i128::from_le_bytes(bytes.as_slice().try_into().expect("Size is 16"));
+                visitor.visit_i128(n)
+            }
+            Value::Union(_i, x) => match x.deref() {
+                Value::Int(i) | Value::Date(i) | Value::TimeMillis(i) => {
+                    visitor.visit_i128(i128::from(*i))
+                }
+                Value::Long(i)
+                | Value::TimeMicros(i)
+                | Value::TimestampMillis(i)
+                | Value::TimestampMicros(i)
+                | Value::TimestampNanos(i)
+                | Value::LocalTimestampMillis(i)
+                | Value::LocalTimestampMicros(i)
+                | Value::LocalTimestampNanos(i) => 
visitor.visit_i128(i128::from(*i)),
+                Value::Fixed(16, bytes) => {
+                    let n = 
i128::from_le_bytes(bytes.as_slice().try_into().expect("Size is 16"));
+                    visitor.visit_i128(n)
+                }
+                _ => Err(de::Error::custom(format!(
+                    "Expected a Int|Long|Fixed(16), but got {:?}",
+                    self.input
+                ))),
+            },
+            _ => Err(de::Error::custom(format!(
+                "Expected a Int|Long|Fixed(16), but got {:?}",
+                self.input
+            ))),
+        }
+    }
+
+    fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
+    where
+        V: Visitor<'de>,
+    {
+        match self.input {
+            Value::String(s) => {
+                if s.chars().count() == 1 {
+                    visitor.visit_char(s.chars().next().expect("There is 
exactly one char"))
+                } else {
+                    Err(de::Error::custom(format!("Tried to deserialize char 
from string, but the string was longer than one char: {s}")))
+                }
+            }
+            Value::Bytes(bytes) => std::str::from_utf8(bytes)
+                .map_err(|e| de::Error::custom(e.to_string()))
+                .and_then(|s| {
+                    if s.chars().count() == 1 {
+                        visitor.visit_char(s.chars().next().expect("There is 
exactly one char"))
+                    } else {
+                        Err(de::Error::custom(format!("Tried to deserialize 
char from a byte array, but the byte array was longer than one char: {}", 
s.len())))
+                    }
+                }
+            ),
+            Value::Fixed(4, bytes) => {
+                
visitor.visit_char(char::from_u32(u32::from_le_bytes(bytes.as_slice().try_into().expect("Size
 is 4"))).ok_or_else(|| <Self::Error as de::Error>::custom("Tried to 
deserialize char from fixed, but was invalid value"))?)
+            }
+            Value::Union(_i, x) => match x.deref() {
+                Value::String(s) => {
+                    if s.chars().count() == 1 {
+                        visitor.visit_char(s.chars().next().expect("There is 
exactly one char"))
+                    } else {
+                        Err(de::Error::custom(format!("Tried to deserialize 
char from string, but the string was longer than one char: {s}")))
+                    }
+                }
+                Value::Bytes(bytes) => std::str::from_utf8(bytes)
+                    .map_err(|e| de::Error::custom(e.to_string()))
+                    .and_then(|s| {
+                        if s.chars().count() == 1 {
+                            visitor.visit_char(s.chars().next().expect("There 
is exactly one char"))
+                        } else {
+                            Err(de::Error::custom(format!("Tried to 
deserialize char from a byte array, but the byte array was longer than one 
char: {}", s.len())))
+                        }
+                    }
+                    ),
+                Value::Fixed(4, bytes) => {
+                    
visitor.visit_char(char::from_u32(u32::from_le_bytes(bytes.as_slice().try_into().expect("Size
 is 4"))).ok_or_else(|| <Self::Error as de::Error>::custom("Tried to 
deserialize char from fixed, but was invalid value"))?)
+                }
+                _ => Err(de::Error::custom(format!("Expected a 
String|Bytes|Fixed(4) for char, but got {:?}", self.input)))
+            },
+            _ => Err(de::Error::custom(format!("Expected a 
String|Bytes|Fixed(4) for char, but got {:?}", self.input)))
+        }
     }
 
     fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
     where
         V: Visitor<'de>,
     {
-        match *self.input {
-            Value::String(ref s) => visitor.visit_borrowed_str(s),
-            Value::Bytes(ref bytes) | Value::Fixed(_, ref bytes) => 
::std::str::from_utf8(bytes)
+        match self.input {
+            Value::String(s) => visitor.visit_borrowed_str(s),
+            Value::Bytes(bytes) | Value::Fixed(_, bytes) => 
std::str::from_utf8(bytes)
                 .map_err(|e| de::Error::custom(e.to_string()))
                 .and_then(|s| visitor.visit_borrowed_str(s)),
-            Value::Uuid(ref u) => visitor.visit_str(&u.to_string()),
+            Value::Uuid(u) => visitor.visit_str(&u.to_string()),
+            Value::Union(_i, x) => match x.deref() {
+                Value::String(s) => visitor.visit_borrowed_str(s),
+                Value::Bytes(bytes) | Value::Fixed(_, bytes) => 
std::str::from_utf8(bytes)
+                    .map_err(|e| de::Error::custom(e.to_string()))
+                    .and_then(|s| visitor.visit_borrowed_str(s)),
+                Value::Uuid(u) => visitor.visit_str(&u.to_string()),
+                _ => Err(de::Error::custom(format!(
+                    "Expected a String|Bytes|Fixed|Uuid, but got {x:?}"
+                ))),
+            },
             _ => Err(de::Error::custom(format!(
                 "Expected a String|Bytes|Fixed|Uuid, but got {:?}",
                 self.input
@@ -413,22 +629,18 @@ impl<'de> de::Deserializer<'de> for &Deserializer<'de> {
     where
         V: Visitor<'de>,
     {
-        match *self.input {
-            Value::Enum(_, ref s) | Value::String(ref s) => 
visitor.visit_borrowed_str(s),
-            Value::Bytes(ref bytes) | Value::Fixed(_, ref bytes) => {
-                String::from_utf8(bytes.to_owned())
+        match self.input {
+            Value::Enum(_, s) | Value::String(s) => 
visitor.visit_borrowed_str(s),
+            Value::Bytes(bytes) | Value::Fixed(_, bytes) => 
String::from_utf8(bytes.to_owned())
+                .map_err(|e| de::Error::custom(e.to_string()))
+                .and_then(|s| visitor.visit_string(s)),
+            Value::Uuid(u) => visitor.visit_str(&u.to_string()),
+            Value::Union(_i, x) => match x.deref() {
+                Value::String(s) => visitor.visit_borrowed_str(s),
+                Value::Bytes(bytes) | Value::Fixed(_, bytes) => 
String::from_utf8(bytes.to_owned())
                     .map_err(|e| de::Error::custom(e.to_string()))
-                    .and_then(|s| visitor.visit_string(s))
-            }
-            Value::Uuid(ref u) => visitor.visit_str(&u.to_string()),
-            Value::Union(_i, ref x) => match **x {
-                Value::String(ref s) => visitor.visit_borrowed_str(s),
-                Value::Bytes(ref bytes) | Value::Fixed(_, ref bytes) => {
-                    String::from_utf8(bytes.to_owned())
-                        .map_err(|e| de::Error::custom(e.to_string()))
-                        .and_then(|s| visitor.visit_string(s))
-                }
-                Value::Uuid(ref u) => visitor.visit_str(&u.to_string()),
+                    .and_then(|s| visitor.visit_string(s)),
+                Value::Uuid(u) => visitor.visit_str(&u.to_string()),
                 _ => Err(de::Error::custom(format!(
                     "Expected a String|Bytes|Fixed|Uuid, but got {x:?}"
                 ))),
@@ -444,18 +656,18 @@ impl<'de> de::Deserializer<'de> for &Deserializer<'de> {
     where
         V: Visitor<'de>,
     {
-        match *self.input {
-            Value::String(ref s) => visitor.visit_bytes(s.as_bytes()),
-            Value::Bytes(ref bytes) | Value::Fixed(_, ref bytes) => {
+        match self.input {
+            Value::String(s) => visitor.visit_bytes(s.as_bytes()),
+            Value::Bytes(bytes) | Value::Fixed(_, bytes) => {
                 if DE_BYTES_BORROWED.get() {
                     visitor.visit_borrowed_bytes(bytes)
                 } else {
                     visitor.visit_bytes(bytes)
                 }
             }
-            Value::Uuid(ref u) => visitor.visit_bytes(u.as_bytes()),
-            Value::Decimal(ref d) => visitor.visit_bytes(&d.to_vec()?),
-            Value::Duration(ref d) => {
+            Value::Uuid(u) => visitor.visit_bytes(u.as_bytes()),
+            Value::Decimal(d) => visitor.visit_bytes(&d.to_vec()?),
+            Value::Duration(d) => {
                 let d_bytes: [u8; 12] = d.into();
                 visitor.visit_bytes(&d_bytes[..])
             }
@@ -470,14 +682,14 @@ impl<'de> de::Deserializer<'de> for &Deserializer<'de> {
     where
         V: Visitor<'de>,
     {
-        match *self.input {
-            Value::String(ref s) => 
visitor.visit_byte_buf(s.clone().into_bytes()),
-            Value::Bytes(ref bytes) | Value::Fixed(_, ref bytes) => {
+        match self.input {
+            Value::String(s) => visitor.visit_byte_buf(s.clone().into_bytes()),
+            Value::Bytes(bytes) | Value::Fixed(_, bytes) => {
                 visitor.visit_byte_buf(bytes.to_owned())
             }
-            Value::Uuid(ref u) => 
visitor.visit_byte_buf(Vec::from(u.as_bytes())),
-            Value::Decimal(ref d) => visitor.visit_byte_buf(d.to_vec()?),
-            Value::Duration(ref d) => {
+            Value::Uuid(u) => visitor.visit_byte_buf(Vec::from(u.as_bytes())),
+            Value::Decimal(d) => visitor.visit_byte_buf(d.to_vec()?),
+            Value::Duration(d) => {
                 let d_bytes: [u8; 12] = d.into();
                 visitor.visit_byte_buf(Vec::from(d_bytes))
             }
@@ -492,9 +704,9 @@ impl<'de> de::Deserializer<'de> for &Deserializer<'de> {
     where
         V: Visitor<'de>,
     {
-        match *self.input {
-            Value::Union(_i, ref inner) if inner.as_ref() == &Value::Null => 
visitor.visit_none(),
-            Value::Union(_i, ref inner) => 
visitor.visit_some(&Deserializer::new(inner)),
+        match self.input {
+            Value::Union(_i, inner) if inner.as_ref() == &Value::Null => 
visitor.visit_none(),
+            Value::Union(_i, inner) => 
visitor.visit_some(&Deserializer::new(inner)),
             _ => Err(de::Error::custom(format!(
                 "Expected a Union, but got {:?}",
                 self.input
@@ -506,9 +718,9 @@ impl<'de> de::Deserializer<'de> for &Deserializer<'de> {
     where
         V: Visitor<'de>,
     {
-        match *self.input {
+        match self.input {
             Value::Null => visitor.visit_unit(),
-            Value::Union(_i, ref x) => match **x {
+            Value::Union(_i, x) => match **x {
                 Value::Null => visitor.visit_unit(),
                 _ => Err(de::Error::custom(format!(
                     "Expected a Null, but got {:?}",
@@ -548,10 +760,10 @@ impl<'de> de::Deserializer<'de> for &Deserializer<'de> {
     where
         V: Visitor<'de>,
     {
-        match *self.input {
-            Value::Array(ref items) => 
visitor.visit_seq(SeqDeserializer::new(items)),
-            Value::Union(_i, ref inner) => match **inner {
-                Value::Array(ref items) => 
visitor.visit_seq(SeqDeserializer::new(items)),
+        match self.input {
+            Value::Array(items) => 
visitor.visit_seq(SeqDeserializer::new(items)),
+            Value::Union(_i, inner) => match inner.deref() {
+                Value::Array(items) => 
visitor.visit_seq(SeqDeserializer::new(items)),
                 Value::Null => visitor.visit_seq(SeqDeserializer::new(&[])),
                 _ => Err(de::Error::custom(format!(
                     "Expected an Array or Null, but got: {inner:?}"
@@ -587,9 +799,9 @@ impl<'de> de::Deserializer<'de> for &Deserializer<'de> {
     where
         V: Visitor<'de>,
     {
-        match *self.input {
-            Value::Map(ref items) => 
visitor.visit_map(MapDeserializer::new(items)),
-            Value::Record(ref fields) => 
visitor.visit_map(RecordDeserializer::new(fields)),
+        match self.input {
+            Value::Map(items) => 
visitor.visit_map(MapDeserializer::new(items)),
+            Value::Record(fields) => 
visitor.visit_map(RecordDeserializer::new(fields)),
             _ => Err(de::Error::custom(format_args!(
                 "Expected a record or a map. Got: {:?}",
                 &self.input
@@ -606,10 +818,10 @@ impl<'de> de::Deserializer<'de> for &Deserializer<'de> {
     where
         V: Visitor<'de>,
     {
-        match *self.input {
-            Value::Record(ref fields) => 
visitor.visit_map(RecordDeserializer::new(fields)),
-            Value::Union(_i, ref inner) => match **inner {
-                Value::Record(ref fields) => 
visitor.visit_map(RecordDeserializer::new(fields)),
+        match self.input {
+            Value::Record(fields) => 
visitor.visit_map(RecordDeserializer::new(fields)),
+            Value::Union(_i, inner) => match inner.deref() {
+                Value::Record(fields) => 
visitor.visit_map(RecordDeserializer::new(fields)),
                 Value::Null => visitor.visit_map(RecordDeserializer::new(&[])),
                 _ => Err(de::Error::custom(format!(
                     "Expected a Record or Null, got: {inner:?}"
@@ -631,26 +843,26 @@ impl<'de> de::Deserializer<'de> for &Deserializer<'de> {
     where
         V: Visitor<'de>,
     {
-        match *self.input {
+        match self.input {
             // This branch can be anything...
-            Value::Record(ref fields) => 
visitor.visit_enum(EnumDeserializer::new(fields)),
-            Value::String(ref field) => 
visitor.visit_enum(EnumUnitDeserializer::new(field)),
-            Value::Union(idx, ref inner) => {
-                if (idx as usize) < variants.len() {
+            Value::Record(fields) => 
visitor.visit_enum(EnumDeserializer::new(fields)),
+            Value::String(field) => 
visitor.visit_enum(EnumUnitDeserializer::new(field)),
+            Value::Union(idx, inner) => {
+                if (*idx as usize) < variants.len() {
                     visitor.visit_enum(UnionDeserializer::new(
-                        variants[idx as usize],
+                        variants[*idx as usize],
                         inner.as_ref(),
                     ))
                 } else {
                     Err(Details::GetUnionVariant {
-                        index: idx as i64,
+                        index: *idx as i64,
                         num_variants: variants.len(),
                     }
                     .into())
                 }
             }
             // This has to be a unit Enum
-            Value::Enum(_index, ref field) => 
visitor.visit_enum(EnumUnitDeserializer::new(field)),
+            Value::Enum(_index, field) => 
visitor.visit_enum(EnumUnitDeserializer::new(field)),
             _ => Err(de::Error::custom(format!(
                 "Expected a Record|Enum, but got {:?}",
                 self.input
@@ -727,8 +939,7 @@ impl<'de> de::MapAccess<'de> for RecordDeserializer<'de> {
         K: DeserializeSeed<'de>,
     {
         match self.input.next() {
-            Some(item) => {
-                let (ref field, ref value) = *item;
+            Some((field, value)) => {
                 self.value = Some(value);
                 seed.deserialize(StringDeserializer {
                     input: field.clone(),
@@ -1667,4 +1878,79 @@ mod tests {
         assert_eq!(raw_bytes.unwrap().0, expected_bytes);
         Ok(())
     }
+
+    #[test]
+    fn avro_rs_414_deserialize_char_from_string() -> TestResult {
+        let value = Value::String('a'.to_string());
+        let result = from_value::<char>(&value)?;
+        assert_eq!(result, 'a');
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_deserialize_char_from_bytes() -> TestResult {
+        let value = Value::Bytes([b'a'].to_vec());
+        let result = from_value::<char>(&value)?;
+        assert_eq!(result, 'a');
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_deserialize_char_from_fixed() -> TestResult {
+        let value = Value::Fixed(4, [b'a', 0, 0, 0].to_vec());
+        let result = from_value::<char>(&value)?;
+        assert_eq!(result, 'a');
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_deserialize_char_from_long_string() -> TestResult {
+        let value = Value::String("avro".to_string());
+        let result = from_value::<char>(&value).unwrap_err().to_string();
+        assert_eq!(
+            result,
+            "Failed to deserialize Avro value into value: Tried to deserialize 
char from string, but the string was longer than one char: avro"
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_deserialize_char_from_long_bytes() -> TestResult {
+        let value = Value::Bytes(b"avro".to_vec());
+        let result = from_value::<char>(&value).unwrap_err().to_string();
+        assert_eq!(
+            result,
+            "Failed to deserialize Avro value into value: Tried to deserialize 
char from a byte array, but the byte array was longer than one char: 4"
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_deserialize_char_from_long_fixed() -> TestResult {
+        let value = Value::Fixed(5, [b'a', 0, 0, 0, 0].to_vec());
+        let result = from_value::<char>(&value).unwrap_err().to_string();
+        assert_eq!(
+            result,
+            "Failed to deserialize Avro value into value: Expected a 
String|Bytes|Fixed(4) for char, but got Fixed(5, [97, 0, 0, 0, 0])"
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_deserialize_char_from_short_fixed() -> TestResult {
+        let value = Value::Fixed(3, [b'a', 0, 0].to_vec());
+        let result = from_value::<char>(&value).unwrap_err().to_string();
+        assert_eq!(
+            result,
+            "Failed to deserialize Avro value into value: Expected a 
String|Bytes|Fixed(4) for char, but got Fixed(3, [97, 0, 0])"
+        );
+
+        Ok(())
+    }
 }
diff --git a/avro/src/serde/ser.rs b/avro/src/serde/ser.rs
index d78f501..b983358 100644
--- a/avro/src/serde/ser.rs
+++ b/avro/src/serde/ser.rs
@@ -22,7 +22,7 @@ use crate::{
     types::Value,
 };
 use serde::{Serialize, ser};
-use std::{collections::HashMap, iter::once};
+use std::collections::HashMap;
 
 #[derive(Clone, Default)]
 pub struct Serializer {}
@@ -154,11 +154,15 @@ impl<'b> ser::Serializer for &'b mut Serializer {
     }
 
     fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
-        if v <= i64::MAX as u64 {
-            self.serialize_i64(v as i64)
-        } else {
-            Err(ser::Error::custom("u64 is too large"))
-        }
+        Ok(Value::Fixed(8, v.to_le_bytes().to_vec()))
+    }
+
+    fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> {
+        Ok(Value::Fixed(16, v.to_le_bytes().to_vec()))
+    }
+
+    fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {
+        Ok(Value::Fixed(16, v.to_le_bytes().to_vec()))
     }
 
     fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
@@ -170,7 +174,7 @@ impl<'b> ser::Serializer for &'b mut Serializer {
     }
 
     fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
-        self.serialize_str(&once(v).collect::<String>())
+        self.serialize_str(&v.to_string())
     }
 
     fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
@@ -1033,4 +1037,40 @@ mod tests {
 
         assert!(!ser.is_human_readable());
     }
+
+    #[test]
+    fn avro_rs_414_char_to_value() -> TestResult {
+        let value = to_value('s')?;
+
+        assert_eq!(value, Value::String("s".to_string()));
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_u64_to_value() -> TestResult {
+        let value = to_value(u64::MAX)?;
+
+        assert_eq!(value, Value::Fixed(8, u64::MAX.to_le_bytes().to_vec()));
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_u128_to_value() -> TestResult {
+        let value = to_value(u128::MAX)?;
+
+        assert_eq!(value, Value::Fixed(16, u128::MAX.to_le_bytes().to_vec()));
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_i128_to_value() -> TestResult {
+        let value = to_value(i128::MAX)?;
+
+        assert_eq!(value, Value::Fixed(16, i128::MAX.to_le_bytes().to_vec()));
+
+        Ok(())
+    }
 }
diff --git a/avro/src/serde/ser_schema.rs b/avro/src/serde/ser_schema.rs
index 3909875..5a092c5 100644
--- a/avro/src/serde/ser_schema.rs
+++ b/avro/src/serde/ser_schema.rs
@@ -647,7 +647,9 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> {
         let mut bytes_written: usize = 0;
 
         bytes_written += encode_long(bytes.len() as i64, &mut self.writer)?;
-        bytes_written += 
self.writer.write(bytes).map_err(Details::WriteBytes)?;
+        // write_all() will retry when the error is ErrorKind::Interrupted 
(happens mostly on network storage)
+        self.writer.write_all(bytes).map_err(Details::WriteBytes)?;
+        bytes_written += bytes.len();
 
         Ok(bytes_written)
     }
@@ -784,6 +786,41 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> {
         }
     }
 
+    fn serialize_i128_with_schema(&mut self, value: i128, schema: &Schema) -> 
Result<usize, Error> {
+        let create_error = |cause: String| {
+            Error::new(Details::SerializeValueWithSchema {
+                value_type: "i128",
+                value: format!("{value}. Cause: {cause}"),
+                schema: schema.clone(),
+            })
+        };
+
+        match schema {
+            Schema::Fixed(fixed) if fixed.size == 16 && fixed.name.name == 
"i128" => {
+                self.writer
+                    .write_all(&value.to_le_bytes())
+                    .map_err(Details::WriteBytes)?;
+                Ok(16)
+            }
+            Schema::Union(union_schema) => {
+                for (i, variant_schema) in 
union_schema.schemas.iter().enumerate() {
+                    match variant_schema {
+                        Schema::Fixed(fixed) if fixed.size == 16 && 
fixed.name.name == "i128" => {
+                            encode_int(i as i32, &mut *self.writer)?;
+                            return self.serialize_i128_with_schema(value, 
variant_schema);
+                        }
+                        _ => { /* skip */ }
+                    }
+                }
+                Err(create_error(format!(
+                    "Cannot find a Fixed(size = 16, name = \"i128\") schema in 
{:?}",
+                    union_schema.schemas
+                )))
+            }
+            expected => Err(create_error(format!("Expected {expected}. Got: 
i128"))),
+        }
+    }
+
     fn serialize_u8_with_schema(&mut self, value: u8, schema: &Schema) -> 
Result<usize, Error> {
         let create_error = |cause: String| {
             Error::new(Details::SerializeValueWithSchema {
@@ -913,6 +950,12 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> {
                     i64::try_from(value).map_err(|cause| 
create_error(cause.to_string()))?;
                 encode_long(long_value, &mut self.writer)
             }
+            Schema::Fixed(fixed) if fixed.size == 8 && fixed.name.name == 
"u64" => {
+                self.writer
+                    .write_all(&value.to_le_bytes())
+                    .map_err(Details::WriteBytes)?;
+                Ok(8)
+            }
             Schema::Union(union_schema) => {
                 for (i, variant_schema) in 
union_schema.schemas.iter().enumerate() {
                     match variant_schema {
@@ -930,15 +973,54 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> {
                             encode_int(i as i32, &mut *self.writer)?;
                             return self.serialize_u64_with_schema(value, 
variant_schema);
                         }
+                        Schema::Fixed(fixed) if fixed.size == 8 && 
fixed.name.name == "u64" => {
+                            encode_int(i as i32, &mut *self.writer)?;
+                            return self.serialize_u64_with_schema(value, 
variant_schema);
+                        }
                         _ => { /* skip */ }
                     }
                 }
                 Err(create_error(format!(
-                    "Cannot find a matching Int-like or Long-like schema in 
{:?}",
+                    "Cannot find a matching Int-like, Long-like or Fixed(size 
= 8, name \"u64\") schema in {:?}",
                     union_schema.schemas
                 )))
             }
-            expected => Err(create_error(format!("Expected {expected}. Got: 
Int/Long"))),
+            expected => Err(create_error(format!("Expected {expected}. Got: 
u64"))),
+        }
+    }
+
+    fn serialize_u128_with_schema(&mut self, value: u128, schema: &Schema) -> 
Result<usize, Error> {
+        let create_error = |cause: String| {
+            Error::new(Details::SerializeValueWithSchema {
+                value_type: "u128",
+                value: format!("{value}. Cause: {cause}"),
+                schema: schema.clone(),
+            })
+        };
+
+        match schema {
+            Schema::Fixed(fixed) if fixed.size == 16 && fixed.name.name == 
"u128" => {
+                self.writer
+                    .write_all(&value.to_le_bytes())
+                    .map_err(Details::WriteBytes)?;
+                Ok(16)
+            }
+            Schema::Union(union_schema) => {
+                for (i, variant_schema) in 
union_schema.schemas.iter().enumerate() {
+                    match variant_schema {
+                        Schema::Fixed(fixed) if fixed.size == 16 && 
fixed.name.name == "u128" => {
+                            encode_int(i as i32, &mut *self.writer)?;
+                            return self.serialize_u128_with_schema(value, 
variant_schema);
+                        }
+                        _ => { /* skip */ }
+                    }
+                }
+                Err(create_error(format!(
+                    "Cannot find a Fixed(size = 16, name = \"u128\") schema in 
{:?}",
+                    union_schema.schemas
+                )))
+            }
+            expected => Err(create_error(format!("Expected {expected}. Got: 
u128"))),
         }
     }
 
@@ -1027,6 +1109,12 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> {
 
         match schema {
             Schema::String | Schema::Bytes => 
self.write_bytes(String::from(value).as_bytes()),
+            Schema::Fixed(fixed) if fixed.size == 4 && fixed.name.name == 
"char" => {
+                self.writer
+                    .write_all(&u32::from(value).to_le_bytes())
+                    .map_err(Details::WriteBytes)?;
+                Ok(4)
+            }
             Schema::Union(union_schema) => {
                 for (i, variant_schema) in 
union_schema.schemas.iter().enumerate() {
                     match variant_schema {
@@ -1034,11 +1122,15 @@ impl<'s, W: Write> SchemaAwareWriteSerializer<'s, W> {
                             encode_int(i as i32, &mut *self.writer)?;
                             return self.serialize_char_with_schema(value, 
variant_schema);
                         }
+                        Schema::Fixed(fixed) if fixed.size == 4 && 
fixed.name.name == "char" => {
+                            encode_int(i as i32, &mut *self.writer)?;
+                            return self.serialize_char_with_schema(value, 
variant_schema);
+                        }
                         _ => { /* skip */ }
                     }
                 }
                 Err(create_error(format!(
-                    "Cannot find a matching String or Bytes schema in 
{union_schema:?}"
+                    "Cannot find a matching String, Bytes or Fixed(size = 4, 
name = \"char\") schema in {union_schema:?}"
                 )))
             }
             expected => Err(create_error(format!("Expected {expected}. Got: 
char"))),
@@ -1756,6 +1848,10 @@ impl<'a, 's, W: Write> ser::Serializer for &'a mut 
SchemaAwareWriteSerializer<'s
         self.serialize_i64_with_schema(v, self.root_schema)
     }
 
+    fn serialize_i128(self, v: i128) -> Result<Self::Ok, Self::Error> {
+        self.serialize_i128_with_schema(v, self.root_schema)
+    }
+
     fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
         self.serialize_u8_with_schema(v, self.root_schema)
     }
@@ -1772,6 +1868,10 @@ impl<'a, 's, W: Write> ser::Serializer for &'a mut 
SchemaAwareWriteSerializer<'s
         self.serialize_u64_with_schema(v, self.root_schema)
     }
 
+    fn serialize_u128(self, v: u128) -> Result<Self::Ok, Self::Error> {
+        self.serialize_u128_with_schema(v, self.root_schema)
+    }
+
     fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
         self.serialize_f32_with_schema(v, self.root_schema)
     }
@@ -1918,6 +2018,7 @@ impl<'a, 's, W: Write> ser::Serializer for &'a mut 
SchemaAwareWriteSerializer<'s
 #[cfg(test)]
 mod tests {
     use super::*;
+    use crate::schema::FixedSchema;
     use crate::{
         Days, Duration, Millis, Months, Reader, Writer, decimal::Decimal, 
error::Details,
         from_value, schema::ResolvedSchema,
@@ -3146,4 +3247,306 @@ mod tests {
         );
         Ok(())
     }
+
+    #[test]
+    fn avro_rs_414_serialize_char_as_string() -> TestResult {
+        let schema = Schema::String;
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        'a'.serialize(&mut serializer)?;
+
+        assert_eq!(buffer.as_slice(), &[2, b'a']);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_char_as_bytes() -> TestResult {
+        let schema = Schema::Bytes;
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        'a'.serialize(&mut serializer)?;
+
+        assert_eq!(buffer.as_slice(), &[2, b'a']);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_char_as_fixed() -> TestResult {
+        let schema = Schema::Fixed(FixedSchema {
+            name: Name::new("char")?,
+            aliases: None,
+            doc: None,
+            size: 4,
+            default: None,
+            attributes: Default::default(),
+        });
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        'a'.serialize(&mut serializer)?;
+
+        assert_eq!(buffer.as_slice(), &[b'a', 0, 0, 0]);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_emoji_char_as_string() -> TestResult {
+        let schema = Schema::String;
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        '👹'.serialize(&mut serializer)?;
+
+        assert_eq!(buffer.as_slice(), &[8, 240, 159, 145, 185]);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_emoji_char_as_bytes() -> TestResult {
+        let schema = Schema::Bytes;
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        '👹'.serialize(&mut serializer)?;
+
+        assert_eq!(buffer.as_slice(), &[8, 240, 159, 145, 185]);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_emoji_char_as_fixed() -> TestResult {
+        let schema = Schema::Fixed(FixedSchema {
+            name: Name::new("char")?,
+            aliases: None,
+            doc: None,
+            size: 4,
+            default: None,
+            attributes: Default::default(),
+        });
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        '👹'.serialize(&mut serializer)?;
+
+        // This is a different byte value than the tests above. This is 
because by creating a String
+        // the unicode value is normalized by Rust
+        assert_eq!(buffer.as_slice(), &[121, 244, 1, 0]);
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_char_as_fixed_wrong_name() -> TestResult {
+        let schema = Schema::Fixed(FixedSchema {
+            name: Name::new("characters")?,
+            aliases: None,
+            doc: None,
+            size: 4,
+            default: None,
+            attributes: Default::default(),
+        });
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        assert!(matches!(
+            'a'.serialize(&mut serializer).unwrap_err().details(),
+            Details::SerializeValueWithSchema { .. }
+        ));
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_char_as_fixed_wrong_size() -> TestResult {
+        let schema = Schema::Fixed(FixedSchema {
+            name: Name::new("char")?,
+            aliases: None,
+            doc: None,
+            size: 1,
+            default: None,
+            attributes: Default::default(),
+        });
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        assert!(matches!(
+            'a'.serialize(&mut serializer).unwrap_err().details(),
+            Details::SerializeValueWithSchema { .. }
+        ));
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_i128_as_fixed() -> TestResult {
+        let schema = Schema::Fixed(FixedSchema {
+            name: Name::new("i128")?,
+            aliases: None,
+            doc: None,
+            size: 16,
+            default: None,
+            attributes: Default::default(),
+        });
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        let bytes_written = i128::MAX.serialize(&mut serializer)?;
+        assert_eq!(bytes_written, 16);
+
+        assert_eq!(
+            buffer.as_slice(),
+            &[
+                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF,
+                0xFF, 0x7F
+            ]
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_i128_as_fixed_wrong_name() -> TestResult {
+        let schema = Schema::Fixed(FixedSchema {
+            name: Name::new("onehundredtwentyeight")?,
+            aliases: None,
+            doc: None,
+            size: 16,
+            default: None,
+            attributes: Default::default(),
+        });
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        assert!(matches!(
+            i128::MAX.serialize(&mut serializer).unwrap_err().details(),
+            Details::SerializeValueWithSchema { .. }
+        ));
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_i128_as_fixed_wrong_size() -> TestResult {
+        let schema = Schema::Fixed(FixedSchema {
+            name: Name::new("i128")?,
+            aliases: None,
+            doc: None,
+            size: 8,
+            default: None,
+            attributes: Default::default(),
+        });
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        assert!(matches!(
+            i128::MAX.serialize(&mut serializer).unwrap_err().details(),
+            Details::SerializeValueWithSchema { .. }
+        ));
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_u128_as_fixed() -> TestResult {
+        let schema = Schema::Fixed(FixedSchema {
+            name: Name::new("u128")?,
+            aliases: None,
+            doc: None,
+            size: 16,
+            default: None,
+            attributes: Default::default(),
+        });
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        let bytes_written = u128::MAX.serialize(&mut serializer)?;
+        assert_eq!(bytes_written, 16);
+
+        assert_eq!(
+            buffer.as_slice(),
+            &[
+                0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
0xFF, 0xFF, 0xFF, 0xFF,
+                0xFF, 0xFF
+            ]
+        );
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_u128_as_fixed_wrong_name() -> TestResult {
+        let schema = Schema::Fixed(FixedSchema {
+            name: Name::new("onehundredtwentyeight")?,
+            aliases: None,
+            doc: None,
+            size: 16,
+            default: None,
+            attributes: Default::default(),
+        });
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        assert!(matches!(
+            u128::MAX.serialize(&mut serializer).unwrap_err().details(),
+            Details::SerializeValueWithSchema { .. }
+        ));
+
+        Ok(())
+    }
+
+    #[test]
+    fn avro_rs_414_serialize_u128_as_fixed_wrong_size() -> TestResult {
+        let schema = Schema::Fixed(FixedSchema {
+            name: Name::new("u128")?,
+            aliases: None,
+            doc: None,
+            size: 8,
+            default: None,
+            attributes: Default::default(),
+        });
+
+        let mut buffer: Vec<u8> = Vec::new();
+        let names = HashMap::new();
+        let mut serializer = SchemaAwareWriteSerializer::new(&mut buffer, 
&schema, &names, None);
+
+        assert!(matches!(
+            u128::MAX.serialize(&mut serializer).unwrap_err().details(),
+            Details::SerializeValueWithSchema { .. }
+        ));
+
+        Ok(())
+    }
 }
diff --git a/avro_derive/tests/derive.rs b/avro_derive/tests/derive.rs
index 74c0e1e..a9012ed 100644
--- a/avro_derive/tests/derive.rs
+++ b/avro_derive/tests/derive.rs
@@ -2101,3 +2101,61 @@ fn avro_rs_401_supported_type_variants() {
     let schema = Schema::parse_str(schema).unwrap();
     assert_eq!(schema, Foo::get_schema());
 }
+
+#[test]
+fn avro_rs_414_round_trip_char_u64_u128_i128() {
+    let schema = Schema::parse_str(
+        r#"
+      {
+        "type":"record",
+        "name":"Foo",
+        "fields": [
+          {
+            "name": "a",
+            "type": "string"
+          },
+          {
+            "name": "b",
+            "type": {
+                "name": "u64",
+                "type": "fixed",
+                "size": 8
+            }
+          },
+          {
+            "name": "c",
+            "type": {
+                "name": "u128",
+                "type": "fixed",
+                "size": 16
+            }
+          },
+          {
+            "name": "d",
+            "type": {
+                "name": "i128",
+                "type": "fixed",
+                "size": 16
+            }
+          }
+        ]
+      }"#,
+    )
+    .unwrap();
+
+    #[derive(AvroSchema, Serialize, Deserialize, PartialEq, Debug, Clone)]
+    struct Foo {
+        a: char,
+        b: u64,
+        c: u128,
+        d: i128,
+    }
+
+    assert_eq!(schema, Foo::get_schema());
+    serde_assert(Foo {
+        a: '👺',
+        b: u64::MAX,
+        c: u128::MAX,
+        d: i128::MAX,
+    });
+}

Reply via email to