minifix 0.3.0

A bare essentials library for Financial Information Exchange (FIX)
use super::{ERR_INT_INVALID, ERR_UTF8, ZeroPadding};
use crate::{Buffer, BufferWriter, FieldType};
use std::fmt::Write;

const ERR_BOOL_LENGTH: &str = "Invalid length; a boolean is Y or N (1 char).";
const ERR_BOOL_CHAR: &str =
    "Invalid character for boolean. Only Y and N are valid.";

macro_rules! lossy_deserialize_unsigned {
    ($data:ident, $ty:ty) => {{
        fn ascii_digit_to_num(digit: u8) -> $ty {
            (digit as $ty).wrapping_sub(b'0' as $ty)
        }
        let mut n: $ty = 0;
        for byte in $data.iter().copied() {
            n = n.wrapping_mul(10).wrapping_add(ascii_digit_to_num(byte));
        }
        Ok(n)
    }};
}

macro_rules! lossy_deserialize_signed {
    ($data:ident, $ty:ty) => {{
        lossy_deserialize_unsigned!($data, $ty).map(|n| {
            if $data.get(0).copied() == Some(b'-' as u8) {
                n.wrapping_neg()
            } else {
                n
            }
        })
    }};
}

macro_rules! impl_unsigned {
    ($ty:ty) => {
        impl<'a> FieldType<'a> for $ty {
            type Error = &'static str;
            type SerializeSettings = ZeroPadding;

            fn serialize_with<B>(
                &self,
                buffer: &mut B,
                padding: ZeroPadding,
            ) -> usize
            where
                B: Buffer,
            {
                let initial_len = buffer.len();
                write!(
                    BufferWriter(buffer),
                    "{:0width$}",
                    self,
                    width = padding.0
                )
                .unwrap();
                buffer.len() - initial_len
            }

            fn deserialize(data: &'a [u8]) -> Result<Self, Self::Error> {
                std::str::from_utf8(data)
                    .map_err(|_| ERR_UTF8)?
                    .parse()
                    .map_err(|_| ERR_INT_INVALID)
            }

            fn deserialize_lossy(data: &'a [u8]) -> Result<Self, Self::Error> {
                lossy_deserialize_unsigned!(data, $ty)
            }
        }
    };
}

macro_rules! impl_signed {
    ($ty:ty) => {
        impl<'a> FieldType<'a> for $ty {
            type Error = &'static str;
            type SerializeSettings = ();

            fn serialize_with<B>(&self, buffer: &mut B, _settings: ()) -> usize
            where
                B: Buffer,
            {
                let initial_len = buffer.len();
                write!(BufferWriter(buffer), "{}", self).unwrap();
                buffer.len() - initial_len
            }

            fn deserialize(data: &'a [u8]) -> Result<Self, Self::Error> {
                std::str::from_utf8(data)
                    .map_err(|_| ERR_UTF8)?
                    .parse()
                    .map_err(|_| ERR_INT_INVALID)
            }

            fn deserialize_lossy(data: &'a [u8]) -> Result<Self, Self::Error> {
                lossy_deserialize_signed!(data, $ty)
            }
        }
    };
}

macro_rules! impl_float {
    ($ty:ty) => {
        impl<'a> FieldType<'a> for $ty {
            type Error = &'static str;
            type SerializeSettings = ();

            fn serialize_with<B>(&self, buffer: &mut B, _settings: ()) -> usize
            where
                B: Buffer,
            {
                let initial_len = buffer.len();
                write!(BufferWriter(buffer), "{}", self).unwrap();
                buffer.len() - initial_len
            }

            fn deserialize(data: &'a [u8]) -> Result<Self, Self::Error> {
                std::str::from_utf8(data)
                    .map_err(|_| ERR_UTF8)?
                    .parse()
                    .map_err(|_| ERR_INT_INVALID)
            }
        }
    };
}

impl_unsigned!(u32);
impl_unsigned!(u64);
impl_unsigned!(u128);
impl_unsigned!(usize);

impl_signed!(i32);
impl_signed!(i64);
impl_signed!(i128);

impl_float!(f32);
impl_float!(f64);

impl<'a> FieldType<'a> for bool {
    type Error = &'static str;
    type SerializeSettings = ();

    fn serialize_with<B>(&self, buffer: &mut B, _settings: ()) -> usize
    where
        B: Buffer,
    {
        let byte = if *self { b'Y' } else { b'N' };
        buffer.extend_from_slice(&[byte]);
        1
    }

    fn deserialize(data: &'a [u8]) -> Result<Self, Self::Error> {
        match data {
            b"Y" => Ok(true),
            b"N" => Ok(false),
            _ if data.len() == 1 => Err(ERR_BOOL_CHAR),
            _ => Err(ERR_BOOL_LENGTH),
        }
    }
}

impl<'a> FieldType<'a> for &'a str {
    type Error = std::str::Utf8Error;
    type SerializeSettings = ();

    fn serialize_with<B>(&self, buffer: &mut B, _settings: ()) -> usize
    where
        B: Buffer,
    {
        buffer.extend_from_slice(self.as_bytes());
        self.as_bytes().len()
    }

    fn deserialize(data: &'a [u8]) -> Result<Self, Self::Error> {
        std::str::from_utf8(data)
    }
}

impl<'a> FieldType<'a> for &'a [u8] {
    type Error = ();
    type SerializeSettings = ();

    fn serialize_with<B>(&self, buffer: &mut B, _settings: ()) -> usize
    where
        B: Buffer,
    {
        buffer.extend_from_slice(self);
        self.len()
    }

    fn deserialize(data: &'a [u8]) -> Result<Self, Self::Error> {
        Ok(data)
    }
}

impl<'a, const N: usize> FieldType<'a> for [u8; N] {
    type Error = ();
    type SerializeSettings = ();

    fn serialize_with<B>(&self, buffer: &mut B, settings: ()) -> usize
    where
        B: Buffer,
    {
        (&self).serialize_with(buffer, settings)
    }

    fn deserialize(data: &'a [u8]) -> Result<Self, Self::Error> {
        data.try_into().map_err(|_| ())
    }
}

impl<'a, const N: usize> FieldType<'a> for &'a [u8; N] {
    type Error = ();
    type SerializeSettings = ();

    fn serialize_with<B>(&self, buffer: &mut B, _settings: ()) -> usize
    where
        B: Buffer,
    {
        buffer.extend_from_slice(&self[..]);
        self.len()
    }

    fn deserialize(data: &'a [u8]) -> Result<Self, Self::Error> {
        data.try_into().map_err(|_| ())
    }
}