From 35b107968888ad41255ea364999abdfb764f602b Mon Sep 17 00:00:00 2001 From: Daniel Lehmann Date: Sun, 25 Aug 2024 00:15:56 -0700 Subject: [PATCH] Add more const constructors and various convenience functions to Number for generic conversions In particular: - new_u8, new_u16, ..., new_u128 which allow creating an arbitrary int without type conversion, e.g. `u5::new_u32(123)` - new_() which allows any Number argument to be passed through generics - as_() which easily converts any Number to another - as_u8(), as_u16() for more control (and to implement the others) --- src/lib.rs | 200 ++++++++++++++++++++++++++++++++++++++++++++++++- tests/tests.rs | 50 +++++++++++++ 2 files changed, 249 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 8880f46..601a66d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,10 @@ impl Display for TryNewError { #[cfg_attr(feature = "const_convert_and_const_trait_impl", const_trait)] pub trait Number: Sized { - type UnderlyingType: Debug + type UnderlyingType: Copy + + Clone + + Number + + Debug + From + TryFrom + TryFrom @@ -57,11 +60,32 @@ pub trait Number: Sized { /// Maximum value that can be represented by this type const MAX: Self; + /// Creates a number from the given value, throwing an error if the value is too large fn new(value: Self::UnderlyingType) -> Self; + /// Creates a number from the given value, return None if the value is too large fn try_new(value: Self::UnderlyingType) -> Result; fn value(self) -> Self::UnderlyingType; + + fn new_(value: T) -> Self; + + fn masked_new(value: T) -> Self; + + fn as_u8(&self) -> u8; + + fn as_u16(&self) -> u16; + + fn as_u32(&self) -> u32; + + fn as_u64(&self) -> u64; + + fn as_u128(&self) -> u128; + + #[inline] + fn as_(self) -> T { + T::masked_new(self) + } } #[cfg(feature = "const_convert_and_const_trait_impl")] @@ -82,6 +106,39 @@ macro_rules! impl_number_native { #[inline] fn value(self) -> Self::UnderlyingType { self } + + #[inline] + fn new_(value: T) -> Self { + match Self::BITS { + 8 => value.as_u8() as Self, + 16 => value.as_u16() as Self, + 32 => value.as_u32() as Self, + 64 => value.as_u64() as Self, + 128 => value.as_u128() as Self, + _ => panic!("Unhandled Number type") + } + } + + #[inline] + fn masked_new(value: T) -> Self { + // Primitive types don't need masking + Self::new_(value) + } + + #[inline] + fn as_u8(&self) -> u8 { *self as u8 } + + #[inline] + fn as_u16(&self) -> u16 { *self as u16 } + + #[inline] + fn as_u32(&self) -> u32 { *self as u32 } + + #[inline] + fn as_u64(&self) -> u64 { *self as u64 } + + #[inline] + fn as_u128(&self) -> u128 { *self as u128 } } )+ }; @@ -105,6 +162,39 @@ macro_rules! impl_number_native { #[inline] fn value(self) -> Self::UnderlyingType { self } + + #[inline] + fn new_(value: T) -> Self { + match Self::BITS { + 8 => value.as_u8() as Self, + 16 => value.as_u16() as Self, + 32 => value.as_u32() as Self, + 64 => value.as_u64() as Self, + 128 => value.as_u128() as Self, + _ => panic!("Unhandled Number type") + } + } + + #[inline] + fn masked_new(value: T) -> Self { + // Primitive types don't need masking + Self::new_(value) + } + + #[inline] + fn as_u8(&self) -> u8 { *self as u8 } + + #[inline] + fn as_u16(&self) -> u16 { *self as u16 } + + #[inline] + fn as_u32(&self) -> u32 { *self as u32 } + + #[inline] + fn as_u64(&self) -> u64 { *self as u64 } + + #[inline] + fn as_u128(&self) -> u128 { *self as u128 } } )+ }; @@ -192,6 +282,31 @@ macro_rules! uint_impl_num { fn value(self) -> $type { self.value } + + #[inline] + fn as_u8(&self) -> u8 { + self.value as u8 + } + + #[inline] + fn as_u16(&self) -> u16 { + self.value as u16 + } + + #[inline] + fn as_u32(&self) -> u32 { + self.value as u32 + } + + #[inline] + fn as_u64(&self) -> u64 { + self.value as u64 + } + + #[inline] + fn as_u128(&self) -> u128 { + self.value as u128 + } } )+ }; @@ -228,6 +343,39 @@ macro_rules! uint_impl_num { Self { value } } + #[inline] + fn new_(value: T) -> Self { + Self::new(Self::UnderlyingType::new_(value)) + } + + fn masked_new(value: T) -> Self { + if Self::BITS < T::BITS { + Self { value: Self::UnderlyingType::masked_new(value.as_::() & Self::MASK) } + } else { + Self { value: Self::UnderlyingType::masked_new(value) } + } + } + + fn as_u8(&self) -> u8 { + self.value as _ + } + + fn as_u16(&self) -> u16 { + self.value as _ + } + + fn as_u32(&self) -> u32 { + self.value as _ + } + + fn as_u64(&self) -> u64 { + self.value as _ + } + + fn as_u128(&self) -> u128 { + self.value as _ + } + #[inline] fn value(self) -> $type { self.value @@ -251,6 +399,56 @@ macro_rules! uint_impl { Self { value } } + /// Creates an instance. Panics if the given value is outside of the valid range + #[inline] + pub const fn new_u8(value: u8) -> Self { + let value = value as $type; + if Self::BITS < 8 { + assert!(value <= Self::MAX.value); + } + Self { value } + } + + /// Creates an instance. Panics if the given value is outside of the valid range + #[inline] + pub const fn new_u16(value: u16) -> Self { + let value = value as $type; + if Self::BITS < 16 { + assert!(value <= Self::MAX.value); + } + Self { value } + } + + /// Creates an instance. Panics if the given value is outside of the valid range + #[inline] + pub const fn new_u32(value: u32) -> Self { + let value = value as $type; + if Self::BITS < 32 { + assert!(value <= Self::MAX.value); + } + Self { value } + } + + /// Creates an instance. Panics if the given value is outside of the valid range + #[inline] + pub const fn new_u64(value: u64) -> Self { + let value = value as $type; + if Self::BITS < 64 { + assert!(value <= Self::MAX.value); + } + Self { value } + } + + /// Creates an instance. Panics if the given value is outside of the valid range + #[inline] + pub const fn new_u128(value: u128) -> Self { + let value = value as $type; + if Self::BITS < 128 { + assert!(value <= Self::MAX.value); + } + Self { value } + } + /// Creates an instance or an error if the given value is outside of the valid range #[inline] pub const fn try_new(value: $type) -> Result { diff --git a/tests/tests.rs b/tests/tests.rs index e674586..bc201dd 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -2047,3 +2047,53 @@ fn schemars() { u8.schema.number = u9.schema.number.clone(); assert_eq!(u8, u9); } + +#[test] +fn new_and_as_specific_types() { + let a = u6::new(42); + let b = u6::new_u8(42); + let c = u6::new_u16(42); + let d = u6::new_u32(42); + let e = u6::new_u64(42); + let f = u6::new_u128(42); + + assert_eq!(a.as_u8(), 42); + assert_eq!(a.as_u16(), 42); + assert_eq!(a.as_u32(), 42); + assert_eq!(a.as_u64(), 42); + assert_eq!(a.as_u128(), 42); + assert_eq!(b.as_u128(), 42); + assert_eq!(c.as_u128(), 42); + assert_eq!(d.as_u128(), 42); + assert_eq!(e.as_u128(), 42); + assert_eq!(f.as_u128(), 42); +} + +#[test] +fn new_flexible() { + let a = u10::new(1000); + let b = u11::new_(a); + + assert_eq!(a.as_u32(), 1000); + assert_eq!(b.as_u32(), 1000); +} + +#[test] +#[should_panic] +fn new_flexible_catches_out_of_bounds() { + let a = u10::new(1000); + let _b = u9::new_(a); +} + +#[test] +fn new_masked() { + let a = u16::new(1000); + let b = u9::masked_new(a); + assert_eq!(b.as_u32(), 488); +} + +#[test] +fn as_flexible() { + let a: u32 = u14::new(123).as_(); + assert_eq!(a, 123u32); +}