#[cfg(feature = "decimal")]
use rust_decimal::Decimal;
pub trait IntegerDigits {
fn digit_count(self) -> usize;
}
macro_rules! impl_unsigned_digit_count {
($($t:ty),*) => {
$(
impl IntegerDigits for $t {
#[inline]
fn digit_count(self) -> usize {
if self == 0 {
1
} else {
self.ilog10() as usize + 1
}
}
}
)*
};
}
macro_rules! impl_signed_digit_count {
($($t:ty),*) => {
$(
impl IntegerDigits for $t {
#[inline]
fn digit_count(self) -> usize {
if self == 0 {
1
} else {
let prefix = if self < 0 { 1 } else { 0 };
self.unsigned_abs().ilog10() as usize + 1 + prefix
}
}
}
)*
};
}
impl_unsigned_digit_count!(u8, u16, u32, u64, u128, usize);
impl_signed_digit_count!(i8, i16, i32, i64, i128, isize);
impl<T: IntegerDigits> IntegerDigits for Option<T> {
#[inline]
fn digit_count(self) -> usize {
match self {
Some(n) => n.digit_count(),
None => 0,
}
}
}
#[inline]
pub fn digit_count<T: IntegerDigits>(n: T) -> usize {
n.digit_count()
}
#[cfg(feature = "decimal")]
impl IntegerDigits for Decimal {
#[inline]
fn digit_count(self) -> usize {
let scale = self.scale() as usize;
let mantissa = self.mantissa(); let abs_mantissa = mantissa.unsigned_abs();
let sign_len = if mantissa < 0 { 1 } else { 0 };
if scale == 0 {
if abs_mantissa == 0 {
1
} else {
abs_mantissa.ilog10() as usize + 1 + sign_len
}
} else {
let mantissa_digits = if abs_mantissa == 0 {
1
} else {
abs_mantissa.ilog10() as usize + 1
};
let visual_digits = std::cmp::max(mantissa_digits, scale + 1);
visual_digits + 1 + sign_len
}
}
}
#[cfg(test)]
mod count_tests {
use super::*;
#[cfg(feature = "decimal")]
use rust_decimal_macros::dec;
#[test]
fn test_digit_count_method_syntax() {
assert_eq!((-100i32).digit_count(), 4);
assert_eq!(100i32.digit_count(), 3);
assert_eq!(0i32.digit_count(), 1);
assert_eq!(i32::MIN.digit_count(), 11);
assert_eq!(5000u16.digit_count(), 4);
assert_eq!(0u64.digit_count(), 1);
let val = Some(12345);
assert_eq!(val.digit_count(), 5);
assert_eq!(None::<i32>.digit_count(), 0);
}
#[test]
fn test_digit_count_various_types() {
let values: Vec<i64> = vec![-10, 0, 10, 1000, 12345];
let results: Vec<usize> = values.into_iter().map(|n| n.digit_count()).collect();
assert_eq!(results, vec![3, 1, 2, 4, 5]);
}
#[test]
fn test_helper_function() {
assert_eq!(digit_count(123), 3);
assert_eq!(digit_count(-123), 4);
assert_eq!(digit_count(Some(12345)), 5);
assert_eq!(digit_count(None::<i32>), 0);
}
#[cfg(feature = "decimal")]
#[test]
fn test_decimal_digit_count() {
assert_eq!(dec!(123).digit_count(), 3);
assert_eq!(dec!(-123).digit_count(), 4);
assert_eq!(dec!(0).digit_count(), 1);
assert_eq!(dec!(12.34).digit_count(), 5);
assert_eq!(dec!(-12.34).digit_count(), 6);
assert_eq!(dec!(0.5).digit_count(), 3);
assert_eq!(dec!(-0.5).digit_count(), 4);
assert_eq!(dec!(0.001).digit_count(), 5);
assert_eq!(dec!(-0.001).digit_count(), 6);
let d = Decimal::new(100, 2);
assert_eq!(d.digit_count(), 4);
let zero_with_scale = Decimal::new(0, 3);
assert_eq!(zero_with_scale.digit_count(), 5); }
#[cfg(feature = "decimal")]
#[test]
fn test_option_decimal_digit_count() {
let val_some = Some(dec!(123.45));
let val_none: Option<Decimal> = None;
assert_eq!(val_some.digit_count(), 6);
assert_eq!(val_none.digit_count(), 0);
}
#[cfg(feature = "decimal")]
#[test]
fn test_helper_function_with_decimal() {
assert_eq!(digit_count(dec!(10.10)), 5);
assert_eq!(digit_count(Some(dec!(-1.1))), 4);
}
}