desert_core 0.2.2

Binary serialization library for Rust (core crate)
Documentation
use crate::deserializer::DeserializationContext;
use crate::serializer::SerializationContext;
use crate::{BinaryDeserializer, BinaryInput, BinaryOutput, BinarySerializer, Error, Result};
use bigdecimal::num_bigint::BigInt;
use bigdecimal::num_traits::ToBytes;
use bigdecimal::BigDecimal;

impl BinarySerializer for BigDecimal {
    fn serialize<Output: BinaryOutput>(
        &self,
        context: &mut SerializationContext<Output>,
    ) -> Result<()> {
        if context.options().bigdecimal_binary {
            let (bigint, scale) = self.as_bigint_and_exponent();
            scale.serialize(context)?;
            bigint.serialize(context)
        } else {
            self.to_string().serialize(context)
        }
    }
}

impl BinaryDeserializer for BigDecimal {
    fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
        if context.options().bigdecimal_binary {
            let scale = i64::deserialize(context)?;
            let bigint = BigInt::deserialize(context)?;
            Ok(BigDecimal::new(bigint, scale))
        } else {
            let length = context.read_var_i32()?;
            if length < 0 {
                return Err(Error::DeserializationFailure(
                    "Failed to deserialize BigDecimal: negative string length".to_string(),
                ));
            }
            let bytes = context.read_bytes(length as usize)?;
            let string = std::str::from_utf8(bytes).map_err(|err| {
                Error::FailedToDecodeString(format!("Failed to decode BigDecimal string: {err}"))
            })?;
            string.parse().map_err(|err| {
                Error::DeserializationFailure(format!("Failed to deserialize BigDecimal: {err}"))
            })
        }
    }
}

impl BinarySerializer for BigInt {
    fn serialize<Output: BinaryOutput>(
        &self,
        context: &mut SerializationContext<Output>,
    ) -> Result<()> {
        self.to_be_bytes().serialize(context)
    }
}

impl BinaryDeserializer for BigInt {
    fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
        let bytes = Vec::<u8>::deserialize(context)?;
        Ok(BigInt::from_signed_bytes_be(&bytes))
    }
}

#[cfg(test)]
mod tests {
    use crate::tests::{roundtrip, roundtrip_with_options};
    use crate::{
        deserialize_with_options, serialize_to_byte_vec, serialize_to_byte_vec_with_options,
        Options,
    };
    use bigdecimal::num_bigint::BigInt;
    use bigdecimal::{BigDecimal, Num};
    use proptest::collection::vec;
    use proptest::prelude::*;
    use test_r::test;

    fn bigdecimal_strategy() -> impl Strategy<Value = BigDecimal> {
        ((0..u128::MAX), (0..u128::MAX), any::<bool>()).prop_map(|(a, b, has_fractional)| {
            let a = a.to_string();
            let b = b.to_string();
            let string = if has_fractional {
                format!("{}.{}", a, b)
            } else {
                a
            };
            string.parse().unwrap()
        })
    }

    fn bigint_strategy() -> impl Strategy<Value = BigInt> {
        vec(any::<u8>(), 1..1000).prop_map(|nums| {
            let s = nums.into_iter().map(|n| n.to_string()).collect::<String>();
            BigInt::from_str_radix(&s, 10).unwrap()
        })
    }

    proptest! {
        #[test]
        fn roundtrip_bigdecimal(value in bigdecimal_strategy()) {
            roundtrip(value);
        }

        #[test]
        fn roundtrip_bigdecimal_binary(value in bigdecimal_strategy()) {
            roundtrip_with_options(value, Options::default().with_binary_bigdecimal());
        }

        #[test]
        fn roundtrip_bigint(value in bigint_strategy()) {
            roundtrip(value);
        }
    }

    #[test]
    fn binary_bigdecimal_roundtrip() {
        let value = "123456789012345678901234567890.123456789"
            .parse::<BigDecimal>()
            .unwrap();
        let options = Options::default().with_binary_bigdecimal();
        let bytes = serialize_to_byte_vec_with_options(&value, options.clone()).unwrap();
        let result: BigDecimal = deserialize_with_options(&bytes, options).unwrap();

        assert_eq!(result, value);
    }

    #[test]
    fn default_bigdecimal_format_is_unchanged() {
        let value = "123456789012345678901234567890.123456789"
            .parse::<BigDecimal>()
            .unwrap();

        assert_eq!(
            serialize_to_byte_vec(&value).unwrap(),
            serialize_to_byte_vec_with_options(&value, Options::default()).unwrap()
        );
    }
}