typed-num 0.3.0

Typed number in Rust with serde and bincode support
Documentation
use crate::Num;
use bincode::{
    de::{BorrowDecoder, Decoder},
    enc::Encoder,
    error::{DecodeError, EncodeError},
    BorrowDecode, Decode, Encode,
};

impl<const N: i64> Encode for Num<N> {
    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
        Encode::encode(&N, encoder)?;
        Ok(())
    }
}

impl<const N: i64, Context> Decode<Context> for Num<N> {
    fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
        let n: i64 = bincode::Decode::<Context>::decode(decoder)?;
        if n != N {
            return Err(DecodeError::OtherString(format!("not {N}")));
        }
        Ok(Default::default())
    }
}

impl<'de, const N: i64, Context> BorrowDecode<'de, Context> for Num<N> {
    fn borrow_decode<D: BorrowDecoder<'de, Context = Context>>(
        decoder: &mut D,
    ) -> Result<Self, DecodeError> {
        let n: i64 = bincode::BorrowDecode::<Context>::borrow_decode(decoder)?;
        if n != N {
            return Err(DecodeError::OtherString(format!("not {N}")));
        }
        Ok(Default::default())
    }
}

#[cfg(test)]
mod tests {
    use crate::Num;
    use bincode::{Decode as BincodeDecode, Encode as BincodeEncode};

    #[test]
    fn test_bincode_typed_num() {
        #[derive(Debug, PartialEq, Eq, BincodeEncode, BincodeDecode)]
        struct BincodeConfig<const V: i64> {
            version: Num<V>,
            data: String,
        }

        const TEST_VERSION: i64 = 42;
        const ANOTHER_VERSION: i64 = 43;

        let config_correct_version = BincodeConfig::<TEST_VERSION> {
            version: Num,
            data: "test_data".to_string(),
        };

        let bincode_config = bincode::config::standard();

        let encoded_vec = bincode::encode_to_vec(&config_correct_version, bincode_config).unwrap();

        let (decoded_correct, len_correct): (BincodeConfig<TEST_VERSION>, usize) =
            bincode::decode_from_slice(&encoded_vec, bincode_config).unwrap();
        assert_eq!(decoded_correct, config_correct_version);
        assert_eq!(len_correct, encoded_vec.len());

        let decode_result_wrong_version: Result<(BincodeConfig<ANOTHER_VERSION>, usize), _> =
            bincode::decode_from_slice(&encoded_vec, bincode_config);

        assert!(decode_result_wrong_version.is_err());
        if let Err(e) = decode_result_wrong_version {
            match e {
                bincode::error::DecodeError::OtherString(s) => {
                    assert_eq!(s, format!("not {}", ANOTHER_VERSION));
                }
                _ => panic!("Expected OtherString error, got {:?}", e),
            }
        }
    }

    #[test]
    fn test_bincode_typed_num_negative() {
        #[derive(Debug, PartialEq, Eq, BincodeEncode, BincodeDecode)]
        struct BincodeConfigNegative<const V: i64> {
            version: Num<V>,
            payload: Vec<u8>,
        }

        const TEST_NEGATIVE_VERSION: i64 = -5;
        const ANOTHER_NEGATIVE_VERSION: i64 = -6;

        let config_correct_negative_version = BincodeConfigNegative::<TEST_NEGATIVE_VERSION> {
            version: Num,
            payload: vec![1, 2, 3],
        };

        let bincode_config = bincode::config::standard();

        let encoded_vec_negative =
            bincode::encode_to_vec(&config_correct_negative_version, bincode_config).unwrap();

        let (decoded_correct_negative, len_correct_negative): (
            BincodeConfigNegative<TEST_NEGATIVE_VERSION>,
            usize,
        ) = bincode::decode_from_slice(&encoded_vec_negative, bincode_config).unwrap();
        assert_eq!(decoded_correct_negative, config_correct_negative_version);
        assert_eq!(len_correct_negative, encoded_vec_negative.len());

        let decode_result_wrong_negative_version: Result<
            (BincodeConfigNegative<ANOTHER_NEGATIVE_VERSION>, usize),
            _,
        > = bincode::decode_from_slice(&encoded_vec_negative, bincode_config);

        assert!(decode_result_wrong_negative_version.is_err());
        if let Err(e) = decode_result_wrong_negative_version {
            match e {
                bincode::error::DecodeError::OtherString(s) => {
                    assert_eq!(s, format!("not {}", ANOTHER_NEGATIVE_VERSION));
                }
                _ => panic!("Expected OtherString error, got {:?}", e),
            }
        }
    }
    #[test]
    fn test_bincode_typed_num_i64_min() {
        #[derive(Debug, PartialEq, Eq, BincodeEncode, BincodeDecode)]
        struct BincodeConfigMin {
            version: Num<{ i64::MIN }>,
            data: String,
        }

        #[derive(Debug, PartialEq, Eq, BincodeEncode, BincodeDecode)]
        struct BincodeConfigMinMinusOne {
            version: Num<{ i64::MIN + 1 }>,
            data: String,
        }

        let config_min = BincodeConfigMin {
            version: Num,
            data: "test_data_min".to_string(),
        };

        let bincode_config = bincode::config::standard();

        let encoded_vec_min = bincode::encode_to_vec(&config_min, bincode_config).unwrap();

        let (decoded_min, len_min): (BincodeConfigMin, usize) =
            bincode::decode_from_slice(&encoded_vec_min, bincode_config).unwrap();
        assert_eq!(decoded_min, config_min);
        assert_eq!(len_min, encoded_vec_min.len());

        let config_min_minus_one = BincodeConfigMinMinusOne {
            version: Num,
            data: "test_data_min_minus_one".to_string(),
        };

        let encoded_vec_min_minus_one =
            bincode::encode_to_vec(&config_min_minus_one, bincode_config).unwrap();

        let decode_result_min_minus_one: Result<(BincodeConfigMin, usize), _> =
            bincode::decode_from_slice(&encoded_vec_min_minus_one, bincode_config);

        assert!(decode_result_min_minus_one.is_err());
        if let Err(e) = decode_result_min_minus_one {
            match e {
                bincode::error::DecodeError::OtherString(s) => {
                    assert_eq!(s, format!("not {}", i64::MIN));
                }
                _ => panic!("Expected OtherString error, got {:?}", e),
            }
        }
    }

    #[test]
    fn test_bincode_typed_num_i64_max() {
        #[derive(Debug, PartialEq, Eq, BincodeEncode, BincodeDecode)]
        struct BincodeConfigMax {
            version: Num<{ i64::MAX }>,
            data: String,
        }

        #[derive(Debug, PartialEq, Eq, BincodeEncode, BincodeDecode)]
        struct BincodeConfigMaxMinusOne {
            version: Num<{ i64::MAX - 1 }>,
            data: String,
        }

        let config_max = BincodeConfigMax {
            version: Num,
            data: "test_data_max".to_string(),
        };

        let bincode_config = bincode::config::standard();

        let encoded_vec_max = bincode::encode_to_vec(&config_max, bincode_config).unwrap();

        let (decoded_max, len_max): (BincodeConfigMax, usize) =
            bincode::decode_from_slice(&encoded_vec_max, bincode_config).unwrap();
        assert_eq!(decoded_max, config_max);
        assert_eq!(len_max, encoded_vec_max.len());

        let config_max_minus_one = BincodeConfigMaxMinusOne {
            version: Num,
            data: "test_data_max_minus_one".to_string(),
        };

        let encoded_vec_max_minus_one =
            bincode::encode_to_vec(&config_max_minus_one, bincode_config).unwrap();

        let decode_result_max_minus_one: Result<(BincodeConfigMax, usize), _> =
            bincode::decode_from_slice(&encoded_vec_max_minus_one, bincode_config);

        assert!(decode_result_max_minus_one.is_err());
        if let Err(e) = decode_result_max_minus_one {
            match e {
                bincode::error::DecodeError::OtherString(s) => {
                    assert_eq!(s, format!("not {}", i64::MAX));
                }
                _ => panic!("Expected OtherString error, got {:?}", e),
            }
        }
    }
}