serde_field_result 0.1.0

Field-level Serde recovery for schema-drift-tolerant API clients
Documentation
use serde::{
    Deserialize, Deserializer,
    de::{
        Visitor,
        value::{
            BorrowedBytesDeserializer, BytesDeserializer, Error as ValueError, MapDeserializer,
        },
    },
};
use serde_field_result::{Field, FieldError, ScalarFieldDecode};

#[test]
fn default_scalar_bytes_become_invalid_fields() {
    let borrowed =
        Field::<String>::deserialize(BorrowedBytesDeserializer::<ValueError>::new(b"borrowed"))
            .unwrap();
    let transient =
        Field::<String>::deserialize(BytesDeserializer::<ValueError>::new(b"transient")).unwrap();

    assert!(borrowed.is_invalid());
    assert_eq!(
        borrowed.error().map(FieldError::message),
        Some("expected string, got bytes".into()),
    );
    assert!(transient.is_invalid());
    assert_eq!(
        transient.error().map(FieldError::message),
        Some("expected string, got bytes".into()),
    );
}

#[test]
fn byte_map_keys_become_invalid_scalar_objects() {
    let entries = [(b"key".as_slice(), true)];
    let deserializer = MapDeserializer::<_, ValueError>::new(entries.into_iter());

    let field = Field::<String>::deserialize(deserializer).unwrap();

    assert!(field.is_invalid());
    assert_eq!(
        field.error().map(FieldError::message),
        Some("expected string, got object".into()),
    );
}

#[test]
fn custom_scalars_can_decode_borrowed_and_owned_bytes() {
    #[derive(Debug, Eq, PartialEq)]
    struct ByteScalar(Vec<u8>);

    impl ScalarFieldDecode for ByteScalar {
        const EXPECTED: &'static str = "bytes";

        fn from_bytes(value: &[u8]) -> Result<Self, FieldError> {
            Ok(Self(value.to_vec()))
        }

        fn from_byte_buf(value: Vec<u8>) -> Result<Self, FieldError> {
            Ok(Self(value))
        }
    }

    let borrowed =
        Field::<ByteScalar>::deserialize(BorrowedBytesDeserializer::<ValueError>::new(b"borrowed"))
            .unwrap();
    let transient =
        Field::<ByteScalar>::deserialize(BytesDeserializer::<ValueError>::new(b"transient"))
            .unwrap();
    let owned = Field::<ByteScalar>::deserialize(ByteBufDeserializer(b"owned".to_vec())).unwrap();

    assert_eq!(borrowed.as_ref(), Some(&ByteScalar(b"borrowed".to_vec())));
    assert_eq!(transient.as_ref(), Some(&ByteScalar(b"transient".to_vec())));
    assert_eq!(owned.as_ref(), Some(&ByteScalar(b"owned".to_vec())));
}

struct ByteBufDeserializer(Vec<u8>);

impl<'de> Deserializer<'de> for ByteBufDeserializer {
    type Error = ValueError;

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        visitor.visit_byte_buf(self.0)
    }

    serde::forward_to_deserialize_any! {
        bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
        bytes byte_buf option unit unit_struct newtype_struct seq tuple
        tuple_struct map struct enum identifier ignored_any
    }
}