Skip to content

Commit

Permalink
feat: require checked_* methods to warn if result was not used
Browse files Browse the repository at this point in the history
  • Loading branch information
Rexagon committed Sep 30, 2024
1 parent a512632 commit 42d60f5
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 0 deletions.
76 changes: 76 additions & 0 deletions src/boc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ impl Boc {
}
}

/// Encodes the specified cell tree as BOC and
/// returns the `hex` encoded bytes as a string.
pub fn encode_hex<T>(cell: T) -> String
where
T: AsRef<DynCell>,
{
hex::encode(Self::encode(cell))
}

/// Encodes the specified cell tree as BOC and
/// returns the `base64` encoded bytes as a string.
#[cfg(any(feature = "base64", test))]
Expand All @@ -128,6 +137,18 @@ impl Boc {
crate::util::encode_base64(Self::encode(cell))
}

/// Encodes the specified cell tree as BOC and
/// returns the `hex` encoded bytes as a string.
///
/// Uses `rayon` under the hood to parallelize encoding.
#[cfg(feature = "rayon")]
pub fn encode_hex_rayon<T>(cell: T) -> String
where
T: AsRef<DynCell>,
{
hex::encode(Self::encode_rayon(cell))
}

/// Encodes the specified cell tree as BOC and
/// returns the `base64` encoded bytes as a string.
///
Expand Down Expand Up @@ -185,6 +206,18 @@ impl Boc {
encode_pair_impl(cell1.as_ref(), cell2.as_ref())
}

/// Decodes a `hex` encoded BOC into a cell tree
/// using an empty cell context.
pub fn decode_hex<T: AsRef<[u8]>>(data: T) -> Result<Cell, de::Error> {
fn decode_hex_impl(data: &[u8]) -> Result<Cell, de::Error> {
match hex::decode(data) {
Ok(data) => Boc::decode_ext(data.as_slice(), &mut Cell::empty_context()),
Err(_) => Err(de::Error::UnknownBocTag),
}
}
decode_hex_impl(data.as_ref())
}

/// Decodes a `base64` encoded BOC into a cell tree
/// using an empty cell context.
#[cfg(any(feature = "base64", test))]
Expand Down Expand Up @@ -314,6 +347,16 @@ impl Boc {
pub struct BocRepr;

impl BocRepr {
/// Encodes the specified cell tree as BOC using an empty cell context and
/// returns the `hex` encoded bytes as a string.
pub fn encode_hex<T>(data: T) -> Result<String, crate::error::Error>
where
T: Store,
{
let boc = ok!(Self::encode_ext(data, &mut Cell::empty_context()));
Ok(hex::encode(boc))
}

/// Encodes the specified cell tree as BOC using an empty cell context and
/// returns the `base64` encoded bytes as a string.
#[cfg(any(feature = "base64", test))]
Expand All @@ -325,6 +368,19 @@ impl BocRepr {
Ok(crate::util::encode_base64(boc))
}

/// Encodes the specified cell tree as BOC using an empty cell context and
/// returns the `hex` encoded bytes as a string.
///
/// Uses `rayon` under the hood to parallelize encoding.
#[cfg(feature = "rayon")]
pub fn encode_hex_rayon<T>(data: T) -> Result<String, crate::error::Error>
where
T: Store,
{
let boc = ok!(Self::encode_rayon_ext(data, &mut Cell::empty_context()));
Ok(hex::encode(boc))
}

/// Encodes the specified cell tree as BOC using an empty cell context and
/// returns the `base64` encoded bytes as a string.
///
Expand Down Expand Up @@ -357,6 +413,26 @@ impl BocRepr {
Self::encode_rayon_ext(data, &mut Cell::empty_context())
}

/// Decodes a `hex` encoded BOC into an object
/// using an empty cell context.
#[inline]
pub fn decode_hex<T, D>(data: D) -> Result<T, BocReprError>
where
for<'a> T: Load<'a>,
D: AsRef<[u8]>,
{
fn decode_hex_impl<T>(data: &[u8]) -> Result<T, BocReprError>
where
for<'a> T: Load<'a>,
{
match hex::decode(data) {
Ok(data) => BocRepr::decode_ext(data.as_slice(), &mut Cell::empty_context()),
Err(_) => Err(BocReprError::InvalidBoc(de::Error::UnknownBocTag)),
}
}
decode_hex_impl::<T>(data.as_ref())
}

/// Decodes a `base64` encoded BOC into an object
/// using an empty cell context.
#[cfg(any(feature = "base64", test))]
Expand Down
21 changes: 21 additions & 0 deletions src/models/currency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::num::{Tokens, VarUint248};
/// Amounts collection.
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[must_use]
pub struct CurrencyCollection {
/// Amount in native currency.
pub tokens: Tokens,
Expand Down Expand Up @@ -138,6 +139,7 @@ impl AugDictExtra for CurrencyCollection {
/// Dictionary with amounts for multiple currencies.
#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[must_use]
#[repr(transparent)]
pub struct ExtraCurrencyCollection(Dict<u32, VarUint248>);

Expand Down Expand Up @@ -222,3 +224,22 @@ impl ExactSize for ExtraCurrencyCollection {
self.0.exact_size()
}
}

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

fn _cc_must_use() -> anyhow::Result<()> {
#[expect(unused_must_use)]
{
CurrencyCollection::new(10).checked_add(&CurrencyCollection::ZERO)?;
}

#[expect(unused_must_use)]
{
ExtraCurrencyCollection::new().checked_add(&ExtraCurrencyCollection::new())?;
}

Ok(())
}
}
45 changes: 45 additions & 0 deletions src/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ macro_rules! impl_var_uints {

/// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred.
#[inline]
#[must_use]
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
match self.0.checked_add(rhs.0) {
Some(value) if value <= Self::MAX.0 => Some($ident(value)),
Expand All @@ -393,6 +394,7 @@ macro_rules! impl_var_uints {

/// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred.
#[inline]
#[must_use]
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
match self.0.checked_sub(rhs.0) {
Some(value) if value <= Self::MAX.0 => Some($ident(value)),
Expand All @@ -402,6 +404,7 @@ macro_rules! impl_var_uints {

/// Checked integer multiplication. Computes `self * rhs`, returning `None` if overflow occurred.
#[inline]
#[must_use]
pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
match self.0.checked_mul(rhs.0) {
Some(value) if value <= Self::MAX.0 => Some($ident(value)),
Expand All @@ -412,6 +415,7 @@ macro_rules! impl_var_uints {
/// Checked integer division. Computes `self / rhs`, returning None if `rhs == 0`
/// or overflow occurred.
#[inline]
#[must_use]
pub const fn checked_div(self, rhs: Self) -> Option<Self> {
match self.0.checked_div(rhs.0) {
Some(value) if value <= Self::MAX.0 => Some($ident(value)),
Expand Down Expand Up @@ -642,6 +646,7 @@ macro_rules! impl_small_uints {

/// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred.
#[inline]
#[must_use]
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
match self.0.checked_add(rhs.0) {
Some(value) if value <= Self::MAX.0 => Some($ident(value)),
Expand All @@ -651,6 +656,7 @@ macro_rules! impl_small_uints {

/// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred.
#[inline]
#[must_use]
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
match self.0.checked_sub(rhs.0) {
Some(value) if value <= Self::MAX.0 => Some($ident(value)),
Expand All @@ -660,6 +666,7 @@ macro_rules! impl_small_uints {

/// Checked integer multiplication. Computes `self * rhs`, returning `None` if overflow occurred.
#[inline]
#[must_use]
pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
match self.0.checked_mul(rhs.0) {
Some(value) if value <= Self::MAX.0 => Some($ident(value)),
Expand All @@ -670,6 +677,7 @@ macro_rules! impl_small_uints {
/// Checked integer division. Computes `self / rhs`, returning None if `rhs == 0`
/// or overflow occurred.
#[inline]
#[must_use]
pub const fn checked_div(self, rhs: Self) -> Option<Self> {
match self.0.checked_div(rhs.0) {
Some(value) if value <= Self::MAX.0 => Some($ident(value)),
Expand Down Expand Up @@ -1055,4 +1063,41 @@ mod tests {
fn tokens_deserialization() {
impl_deserialization_tests!(Tokens, 120, 0xabcdef89abcdefdeadbeeffafacafe);
}

fn _num_must_use() {
#[expect(unused_must_use)]
{
Uint9::new(10).checked_add(Uint9::ZERO);
}

#[expect(unused_must_use)]
{
Uint12::new(10).checked_add(Uint12::ZERO);
}

#[expect(unused_must_use)]
{
Uint15::new(10).checked_add(Uint15::ZERO);
}

#[expect(unused_must_use)]
{
VarUint24::new(10).checked_add(VarUint24::ZERO);
}

#[expect(unused_must_use)]
{
VarUint56::new(10).checked_add(VarUint56::ZERO);
}

#[expect(unused_must_use)]
{
Tokens::new(10).checked_add(Tokens::ZERO);
}

#[expect(unused_must_use)]
{
VarUint248::new(10).checked_add(&VarUint248::ZERO);
}
}
}
3 changes: 3 additions & 0 deletions src/num/varuint248.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ impl VarUint248 {

/// Checked integer addition. Computes `self + rhs`,
/// returning `None` if overflow occurred.
#[must_use]
pub const fn checked_add(&self, rhs: &Self) -> Option<Self> {
let (lo, carry_lo) = self.low().overflowing_add(*rhs.low());
let (hi, carry_c) = self.high().overflowing_add(carry_lo as _);
Expand All @@ -117,6 +118,7 @@ impl VarUint248 {

/// Checked integer subtraction. Computes `self - rhs`,
/// returning `None` if overflow occurred.
#[must_use]
pub const fn checked_sub(&self, rhs: &Self) -> Option<Self> {
let (lo, carry_lo) = self.low().overflowing_sub(*rhs.low());
let (hi, carry_c) = self.high().overflowing_sub(carry_lo as _);
Expand All @@ -131,6 +133,7 @@ impl VarUint248 {

/// Checked integer multiplication. Computes `self * rhs`,
/// returning `None` if overflow occurred.
#[must_use]
pub fn checked_mul(&self, rhs: &Self) -> Option<Self> {
let mut res = umulddi3(self.low(), rhs.low());

Expand Down

0 comments on commit 42d60f5

Please sign in to comment.