Skip to main content

icydb_schema/
nat_big.rs

1//! Canonical arbitrary-precision unsigned-integer atom.
2
3use crate::{Decimal, NumericValue};
4use candid::{CandidType, Nat as WrappedNat};
5use derive_more::{Add, AddAssign, Sub, SubAssign};
6use num_bigint::BigUint;
7use serde::{Deserialize, Serialize};
8use std::{
9    fmt,
10    iter::Sum,
11    ops::{Div, DivAssign, Mul, MulAssign},
12    str::FromStr,
13};
14
15//
16// NatBig
17//
18
19#[derive(
20    Add,
21    AddAssign,
22    CandidType,
23    Clone,
24    Debug,
25    Default,
26    Eq,
27    PartialEq,
28    Hash,
29    Ord,
30    PartialOrd,
31    Serialize,
32    Deserialize,
33    Sub,
34    SubAssign,
35)]
36/// Arbitrary-precision unsigned integer used by schema and typed values.
37pub struct NatBig(WrappedNat);
38
39impl NatBig {
40    /// Construct from the canonical Candid natural-number representation.
41    #[must_use]
42    pub const fn from_candid(value: WrappedNat) -> Self {
43        Self(value)
44    }
45
46    /// Construct from a `num_bigint` unsigned integer.
47    #[must_use]
48    pub fn from_biguint(value: BigUint) -> Self {
49        Self::from_candid(WrappedNat::from(value))
50    }
51
52    /// Return base-2^32 limbs for decimal key encoding.
53    ///
54    /// This allocates for the returned limb vector.
55    #[must_use]
56    pub fn u32_digits(&self) -> Vec<u32> {
57        self.0.0.to_u32_digits()
58    }
59
60    /// Convert to `u128` when the value is in range.
61    #[must_use]
62    pub fn to_u128(&self) -> Option<u128> {
63        let big = &self.0.0;
64
65        u128::try_from(big).ok()
66    }
67
68    /// Convert to `u64` when the value is in range.
69    #[must_use]
70    pub fn to_u64(&self) -> Option<u64> {
71        let big = &self.0.0;
72
73        u64::try_from(big).ok()
74    }
75
76    /// Serialize this arbitrary-precision natural for internal hash and sort-key framing.
77    #[must_use]
78    pub fn to_leb128(&self) -> Vec<u8> {
79        let mut out = Vec::new();
80        let encoded = self.0.encode(&mut out);
81        debug_assert!(
82            encoded.is_ok(),
83            "Vec-backed unsigned LEB128 encoding failed"
84        );
85
86        out
87    }
88
89    /// Saturating addition (unbounded; equivalent to normal addition).
90    #[must_use]
91    pub fn saturating_add(self, rhs: Self) -> Self {
92        Self(self.0 + rhs.0)
93    }
94
95    /// Saturating subtraction; clamps at zero on underflow.
96    #[must_use]
97    pub fn saturating_sub(self, rhs: Self) -> Self {
98        if rhs > self {
99            return Self::default();
100        }
101
102        Self(self.0 - rhs.0)
103    }
104}
105
106impl fmt::Display for NatBig {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        self.0.fmt(f)
109    }
110}
111
112impl FromStr for NatBig {
113    type Err = <WrappedNat as FromStr>::Err;
114
115    fn from_str(s: &str) -> Result<Self, Self::Err> {
116        WrappedNat::from_str(s).map(Self::from_candid)
117    }
118}
119
120impl Div for NatBig {
121    type Output = Self;
122
123    fn div(self, other: Self) -> Self::Output {
124        Self(self.0 / other.0)
125    }
126}
127
128impl DivAssign for NatBig {
129    fn div_assign(&mut self, other: Self) {
130        self.0 /= other.0;
131    }
132}
133
134impl From<u64> for NatBig {
135    fn from(n: u64) -> Self {
136        Self::from_candid(WrappedNat::from(n))
137    }
138}
139
140impl From<u32> for NatBig {
141    fn from(n: u32) -> Self {
142        Self::from_candid(WrappedNat::from(n))
143    }
144}
145
146impl Mul for NatBig {
147    type Output = Self;
148
149    fn mul(self, other: Self) -> Self::Output {
150        Self(self.0 * other.0)
151    }
152}
153
154impl MulAssign for NatBig {
155    fn mul_assign(&mut self, other: Self) {
156        self.0 *= other.0;
157    }
158}
159
160impl NumericValue for NatBig {
161    fn try_to_decimal(&self) -> Option<Decimal> {
162        self.to_u128().and_then(Decimal::from_u128)
163    }
164
165    fn try_from_decimal(value: Decimal) -> Option<Self> {
166        value.to_u128().map(WrappedNat::from).map(Self::from_candid)
167    }
168}
169
170impl Sum for NatBig {
171    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
172        iter.fold(Self::default(), |acc, x| acc + x)
173    }
174}
175
176impl TryFrom<i32> for NatBig {
177    type Error = std::num::TryFromIntError;
178
179    fn try_from(n: i32) -> Result<Self, Self::Error> {
180        let v = Self::from_candid(WrappedNat::from(u32::try_from(n)?));
181        Ok(v)
182    }
183}