crypto-pay-api 0.2.1

A Rust client library for Crypto Pay API provided by Telegram CryptoBot
Documentation
pub use reqwest::header::{HeaderName, HeaderValue};
pub use reqwest::Client;
pub use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
pub use rust_decimal::Decimal;
pub use rust_decimal_macros::dec;
pub use serde::{Deserialize, Serialize};
use std::str::FromStr;

pub trait IntoDecimal {
    fn into_decimal(self) -> Decimal;
    fn try_into_decimal(self) -> Result<Decimal, String>;
}

impl IntoDecimal for Decimal {
    fn into_decimal(self) -> Decimal {
        self
    }

    fn try_into_decimal(self) -> Result<Decimal, String> {
        Ok(self)
    }
}

impl IntoDecimal for &Decimal {
    fn into_decimal(self) -> Decimal {
        *self
    }

    fn try_into_decimal(self) -> Result<Decimal, String> {
        Ok(*self)
    }
}

macro_rules! impl_into_decimal_int {
    ($($t:ty),*) => {
        $(
            impl IntoDecimal for $t {
                fn into_decimal(self) -> Decimal {
                    Decimal::from(self)
                }

                fn try_into_decimal(self) -> Result<Decimal, String> {
                    Ok(Decimal::from(self))
                }
            }
        )*
    };
}

impl_into_decimal_int!(u8, i8, u16, i16, u32, i32, u64, i64, isize, usize);

impl IntoDecimal for f32 {
    fn into_decimal(self) -> Decimal {
        self.try_into_decimal().unwrap_or(Decimal::ZERO)
    }

    fn try_into_decimal(self) -> Result<Decimal, String> {
        Decimal::from_f32(self).ok_or_else(|| format!("Invalid float value: {}", self))
    }
}

impl IntoDecimal for f64 {
    fn into_decimal(self) -> Decimal {
        self.try_into_decimal().unwrap_or(Decimal::ZERO)
    }

    fn try_into_decimal(self) -> Result<Decimal, String> {
        Decimal::from_f64(self).ok_or_else(|| format!("Invalid float value: {}", self))
    }
}

impl IntoDecimal for &str {
    fn into_decimal(self) -> Decimal {
        self.try_into_decimal().unwrap_or(Decimal::ZERO)
    }

    fn try_into_decimal(self) -> Result<Decimal, String> {
        Decimal::from_str(self).map_err(|e| format!("Invalid decimal string '{}': {}", self, e))
    }
}

impl IntoDecimal for String {
    fn into_decimal(self) -> Decimal {
        self.try_into_decimal().unwrap_or(Decimal::ZERO)
    }

    fn try_into_decimal(self) -> Result<Decimal, String> {
        Decimal::from_str(&self).map_err(|e| format!("Invalid decimal string '{}': {}", self, e))
    }
}

impl IntoDecimal for &String {
    fn into_decimal(self) -> Decimal {
        self.try_into_decimal().unwrap_or(Decimal::ZERO)
    }

    fn try_into_decimal(self) -> Result<Decimal, String> {
        Decimal::from_str(self).map_err(|e| format!("Invalid decimal string '{}': {}", self, e))
    }
}

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

    #[test]
    fn test_decimal_conversions() {
        let decimal = dec!(123.45);
        assert_eq!(decimal.into_decimal(), dec!(123.45));
        assert!(decimal.try_into_decimal().is_ok());
    }

    #[test]
    fn test_decimal_ref_conversions() {
        let decimal = dec!(123.45);
        assert_eq!((&decimal).into_decimal(), dec!(123.45));
        assert!((&decimal).try_into_decimal().is_ok());
    }

    #[test]
    fn test_integer_conversions() {
        assert_eq!(42u8.into_decimal(), dec!(42));
        assert_eq!((-100i32).into_decimal(), dec!(-100));
        assert_eq!(999u64.into_decimal(), dec!(999));

        assert!(42u8.try_into_decimal().is_ok());
        assert!((-100i32).try_into_decimal().is_ok());
        assert!(999u64.try_into_decimal().is_ok());
    }

    #[test]
    fn test_float_conversions() {
        assert_eq!(3.14f32.into_decimal(), dec!(3.14));
        assert!(3.14f32.try_into_decimal().is_ok());

        assert_eq!(2.718281828f64.into_decimal(), dec!(2.718281828));
        assert!(2.718281828f64.try_into_decimal().is_ok());

        assert!(f32::NAN.try_into_decimal().is_err());
        assert!(f32::INFINITY.try_into_decimal().is_err());
        assert!(f64::NAN.try_into_decimal().is_err());
        assert!(f64::INFINITY.try_into_decimal().is_err());

        assert_eq!(f32::NAN.into_decimal(), Decimal::ZERO);
        assert_eq!(f64::INFINITY.into_decimal(), Decimal::ZERO);
    }

    #[test]
    fn test_string_conversions() {
        let valid_str = "123.45";
        let valid_string = String::from("678.90");
        let valid_string_ref = &String::from("42.0");

        assert_eq!(valid_str.into_decimal(), dec!(123.45));
        assert_eq!(valid_string.clone().into_decimal(), dec!(678.90));
        assert_eq!(valid_string_ref.into_decimal(), dec!(42.0));

        assert!(valid_str.try_into_decimal().is_ok());
        assert!(valid_string.try_into_decimal().is_ok());
        assert!(valid_string_ref.try_into_decimal().is_ok());

        let invalid_str = "not_a_number";
        let invalid_string = String::from("abc123");
        let invalid_string_ref = &String::from("invalid");

        assert!(invalid_str.try_into_decimal().is_err());
        assert!(invalid_string.clone().try_into_decimal().is_err());
        assert!(invalid_string_ref.try_into_decimal().is_err());

        assert_eq!(invalid_str.into_decimal(), Decimal::ZERO);
        assert_eq!(invalid_string.into_decimal(), Decimal::ZERO);
        assert_eq!(invalid_string_ref.into_decimal(), Decimal::ZERO);
    }

    #[test]
    fn test_empty_string_conversion() {
        let empty_str = "";
        let empty_string = String::new();
        let empty_string_ref = &String::new();

        assert!(empty_str.try_into_decimal().is_err());
        assert!(empty_string.clone().try_into_decimal().is_err());
        assert!(empty_string_ref.try_into_decimal().is_err());

        assert_eq!(empty_str.into_decimal(), Decimal::ZERO);
        assert_eq!(empty_string.into_decimal(), Decimal::ZERO);
        assert_eq!(empty_string_ref.into_decimal(), Decimal::ZERO);
    }

    #[test]
    fn test_edge_case_numbers() {
        assert_eq!(0.into_decimal(), Decimal::ZERO);
        assert_eq!(0.0f32.into_decimal(), Decimal::ZERO);
        assert_eq!("0".into_decimal(), Decimal::ZERO);

        assert_eq!((-42i32).into_decimal(), dec!(-42));
        assert_eq!((-3.14f64).into_decimal(), dec!(-3.14));
        assert_eq!("-123.45".into_decimal(), dec!(-123.45));

        assert_eq!(0.000001f32.into_decimal(), dec!(0.000001));
        assert_eq!(999999999.0f64.into_decimal(), dec!(999999999.0));
    }

    #[test]
    fn test_error_messages() {
        let error = "invalid".try_into_decimal().unwrap_err();
        assert!(error.contains("Invalid decimal string 'invalid'"));

        let error = f32::NAN.try_into_decimal().unwrap_err();
        assert!(error.contains("Invalid float value"));

        let error = f64::INFINITY.try_into_decimal().unwrap_err();
        assert!(error.contains("Invalid float value"));
    }
}