psibase 0.23.0

Library and command-line tool for interacting with psibase networks
Documentation
use async_graphql::{InputObject, SimpleObject};
use fracpack::{Pack, ToSchema, Unpack};
use serde::{Deserialize, Serialize};
use std::ops::{Add, Sub};

use crate::services::tokens::{Precision, TokensError};

#[derive(
    Debug,
    Copy,
    Default,
    Clone,
    Pack,
    Unpack,
    ToSchema,
    Serialize,
    Deserialize,
    SimpleObject,
    InputObject,
    PartialOrd,
    PartialEq,
    Ord,
    Eq,
)]
#[fracpack(fracpack_mod = "fracpack")]
#[graphql(input_name = "QuantityInput")]
pub struct Quantity {
    pub value: u64,
}

impl From<u64> for Quantity {
    fn from(value: u64) -> Self {
        Self { value }
    }
}

impl Quantity {
    pub fn new(value: u64) -> Self {
        Self { value }
    }

    pub fn from_str(amount: &str, precision: Precision) -> Result<Self, TokensError> {
        if !amount.chars().all(|c| c == '.' || c.is_ascii_digit()) {
            return Err(TokensError::InvalidNumber);
        }
        let value = match amount.split_once('.') {
            Some((integer_part, fraction_part)) => {
                let integer_value: u64 = integer_part
                    .parse()
                    .map_err(|_| TokensError::InvalidNumber)?;

                let integer_value = integer_value
                    .checked_mul(10u64.pow(precision.value() as u32))
                    .ok_or(TokensError::Overflow)?;

                let fraction_len = fraction_part.len() as u8;

                if fraction_len > precision.value() {
                    return Err(TokensError::PrecisionOverflow);
                }
                let remaining_pow = precision.value() - fraction_len;

                let fraction_value: u64 = fraction_part
                    .parse()
                    .map_err(|_| TokensError::InvalidNumber)?;

                let fraction_value = fraction_value
                    .checked_mul(10u64.pow(remaining_pow as u32))
                    .ok_or(TokensError::Overflow)?;

                integer_value + fraction_value
            }
            None => {
                let num_value: u64 = amount.parse().map_err(|_| TokensError::InvalidNumber)?;
                num_value
                    .checked_mul(10u64.pow(precision.value() as u32))
                    .ok_or(TokensError::Overflow)?
            }
        };

        Ok(Self { value })
    }
}

impl Add for Quantity {
    type Output = Quantity;

    fn add(self, rhs: Quantity) -> Quantity {
        self.value.checked_add(rhs.value).unwrap().into()
    }
}

impl Sub for Quantity {
    type Output = Quantity;

    fn sub(self, rhs: Self) -> Self::Output {
        self.value.checked_sub(rhs.value).unwrap().into()
    }
}