safe_decimal_core 0.1.1

This is a Rust fixed-point numeric library targeting blockchain development. Originally created and used as a part of the Invariant Protocol. The current version leverages macros, traits and generics to exchange dozens of lines of error prone code with a single line and generating the rest.
Documentation
use quote::quote;

use crate::utils::string_to_ident;
use crate::DecimalCharacteristics;

pub fn generate_big_ops(characteristics: DecimalCharacteristics) -> proc_macro::TokenStream {
    let DecimalCharacteristics {
        struct_name,
        big_type,
        underlying_type,
        ..
    } = characteristics;

    let name_str = &struct_name.to_string();
    let underlying_str = &underlying_type.to_string();
    let big_str = &big_type.to_string();

    let module_name = string_to_ident("tests_big_ops_", &name_str);

    proc_macro::TokenStream::from(quote!(
        impl<T: Decimal> BigOps<T> for #struct_name
        where
            T::U: TryInto<#big_type>,
        {
            fn big_mul(self, rhs: T) -> Self {
                Self::new(
                    #big_type::try_from(self.get())
                        .unwrap_or_else(|_| std::panic!("decimal: lhs value can't fit into `{}` type in {}::big_mul()", #big_str, #name_str))
                        .checked_mul(
                            rhs.get()
                                .try_into()
                                .unwrap_or_else(|_| std::panic!("decimal: rhs value can't fit into `{}` type in {}::big_mul()", #big_str, #name_str))
                        )
                        .unwrap_or_else(|| std::panic!("decimal: overflow in method {}::big_mul()", #name_str))
                        .checked_div(
                            T::one()
                        )
                        .unwrap_or_else(|| std::panic!("decimal: overflow in method {}::big_mul()", #name_str))
                        .try_into()
                        .unwrap_or_else(|_| std::panic!("decimal: overflow casting result to `{}` type in method {}::big_mul()", #underlying_str, #name_str))

                )
            }

            fn big_mul_up(self, rhs: T) -> Self {
                Self::new(
                    #big_type::try_from(self.get())
                        .unwrap_or_else(|_| std::panic!("decimal: lhs value can't fit into `{}` type in {}::big_mul_up()", #big_str, #name_str))
                        .checked_mul(
                            rhs.get()
                                .try_into()
                                .unwrap_or_else(|_| std::panic!("decimal: rhs value can't fit into `{}` type in {}::big_mul_up()", #big_str, #name_str))
                        )
                        .unwrap_or_else(|| std::panic!("decimal: overflow in method {}::big_mul_up()", #name_str))
                        .checked_add(T::almost_one())
                        .unwrap_or_else(|| std::panic!("decimal: overflow in method {}::big_mul_up()", #name_str))
                        .checked_div(
                            T::one()
                        )
                        .unwrap_or_else(|| std::panic!("decimal: overflow in method {}::big_mul_up()", #name_str))
                        .try_into()
                        .unwrap_or_else(|_| std::panic!("decimal: overflow casting result to `{}` type in method {}::big_mul_up()", #underlying_str, #name_str))
                )
            }

            fn big_div(self, rhs: T) -> Self {
                Self::new(
                    #big_type::try_from(self.get())
                        .unwrap_or_else(|_| std::panic!("decimal: lhs value can't fit into `{}` type in {}::big_div()", #big_str, #name_str))
                        .checked_mul(
                            T::one()
                        )
                        .unwrap_or_else(|| std::panic!("decimal: overflow in method {}::big_div()", #name_str))
                        .checked_div(
                            rhs.get()
                                .try_into()
                                .unwrap_or_else(|_| std::panic!("decimal: rhs value can't fit into `{}` type in {}::big_div()", #big_str, #name_str))
                        )
                        .unwrap_or_else(|| std::panic!("decimal: overflow in method {}::big_div()", #name_str))
                        .try_into()
                        .unwrap_or_else(|_| std::panic!("decimal: overflow casting result to `{}` type in method {}::big_div()", #underlying_str, #name_str))
                )
            }

            fn big_div_up(self, rhs: T) -> Self {
                Self::new(
                    #big_type::try_from(self.get())
                        .unwrap_or_else(|_| std::panic!("decimal: lhs value can't fit into `{}` type in {}::big_div_up()", #big_str, #name_str))
                        .checked_mul(
                            T::one()
                        )
                        .unwrap_or_else(|| std::panic!("decimal: overflow in method {}::big_div_up()", #name_str))
                        .checked_add(
                            rhs.get()
                                .try_into()
                                .unwrap_or_else(|_| std::panic!("decimal: rhs value can't fit into `{}` type in {}::big_div_up()", #big_str, #name_str))
                                .checked_sub(#big_type::from(1u128)).unwrap()
                        )
                        .unwrap_or_else(|| std::panic!("decimal: overflow in method {}::big_div_up()", #name_str))
                        .checked_div(
                            rhs.get()
                                .try_into().unwrap_or_else(|_| std::panic!("rhs value could not be converted to big type in `big_div_up`")),
                        )
                        .unwrap_or_else(|| std::panic!("decimal: overflow in method {}::big_div_up()", #name_str))
                        .try_into()
                        .unwrap_or_else(|_| std::panic!("decimal: overflow casting result to `{}` type in method {}::big_div_up()", #underlying_str, #name_str))
                )
            }
        }

        #[cfg(test)]
        pub mod #module_name {
            use super::*;

            #[test]
            fn test_big_mul () {
                let a = #struct_name::new(2);
                let b = #struct_name::new(#struct_name::one());
                assert_eq!(a.big_mul(b), #struct_name::new(2));
            }

            #[test]
            fn test_big_mul_up () {
                let a = #struct_name::new(2);
                let b = #struct_name::new(#struct_name::one());
                assert_eq!(a.big_mul_up(b), #struct_name::new(2));
            }

            #[test]
            fn test_big_div () {
                let a = #struct_name::new(2);
                let b = #struct_name::new(#struct_name::one());
                assert_eq!(a.big_div(b), #struct_name::new(2));
            }

            #[test]
            fn test_big_div_up () {
                let a = #struct_name::new(2);
                let b = #struct_name::new(#struct_name::one());
                assert_eq!(a.big_div_up(b), #struct_name::new(2));
            }
        }
    ))
}