diff --git a/changelog.d/20260414_000000_add_tryfrom_tryinto_from_byte_to_linkreference.md b/changelog.d/20260414_000000_add_tryfrom_tryinto_from_byte_to_linkreference.md new file mode 100644 index 0000000..07b88c8 --- /dev/null +++ b/changelog.d/20260414_000000_add_tryfrom_tryinto_from_byte_to_linkreference.md @@ -0,0 +1,9 @@ +--- +bump: minor +--- + +### Added +- `TryFrom` and `TryInto` bounds (with `Error: Debug`) for all 12 integer types on `LinkReference` +- `from_byte(n: u8) -> Self` default method on `LinkReference` for creating small constants from `u8` values +- `Sized` bound on `LinkReference` +- Explicit `u128` support as a `LinkReference` type (enables referencing very large link address spaces) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3926fad..43edbb8 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "autocfg" @@ -19,7 +19,7 @@ dependencies = [ [[package]] name = "platform-num" -version = "0.5.0" +version = "0.6.0" dependencies = [ "num-traits", ] diff --git a/rust/src/imp.rs b/rust/src/imp.rs index dbc5e74..bb59315 100644 --- a/rust/src/imp.rs +++ b/rust/src/imp.rs @@ -141,10 +141,10 @@ for_each_integer_type!(max_value_impl); /// A composite trait for types that can be used as link identifiers. /// /// Combines [`Number`], `Unsigned`, [`ToSigned`], [`MaxValue`], -/// `FromPrimitive`, `Debug`, `Display`, `Hash`, `Send`, `Sync`, -/// and `'static`. +/// `FromPrimitive`, `TryFrom`/`TryInto` for all integer types, +/// `Debug`, `Display`, `Hash`, `Send`, `Sync`, and `'static`. /// -/// Implemented for `u8`, `u16`, `u32`, `u64`, and `usize`. +/// Implemented for `u8`, `u16`, `u32`, `u64`, `u128`, and `usize`. /// /// # Design note /// @@ -166,25 +166,83 @@ for_each_integer_type!(max_value_impl); /// ``` #[rustfmt::skip] pub trait LinkReference: -Number +Sized ++ Number + Unsigned + ToSigned + MaxValue + FromPrimitive ++ TryFrom ++ TryFrom ++ TryFrom ++ TryFrom ++ TryFrom ++ TryFrom ++ TryFrom ++ TryFrom ++ TryFrom ++ TryFrom ++ TryFrom ++ TryFrom ++ TryInto ++ TryInto ++ TryInto ++ TryInto ++ TryInto ++ TryInto ++ TryInto ++ TryInto ++ TryInto ++ TryInto ++ TryInto ++ TryInto + Debug + Display + Hash + Send + Sync -+ 'static {} ++ 'static +{ + /// Creates a value of this type from a `u8`, panicking on overflow. + fn from_byte(n: u8) -> Self { + Self::try_from(n).unwrap_or_else(|e| { + panic!("LinkReference::from_byte({n}) failed: {e:?}") + }) + } +} #[rustfmt::skip] impl< - All: Number + All: Sized + + Number + Unsigned + ToSigned + MaxValue + FromPrimitive + + TryFrom + + TryFrom + + TryFrom + + TryFrom + + TryFrom + + TryFrom + + TryFrom + + TryFrom + + TryFrom + + TryFrom + + TryFrom + + TryFrom + + TryInto + + TryInto + + TryInto + + TryInto + + TryInto + + TryInto + + TryInto + + TryInto + + TryInto + + TryInto + + TryInto + + TryInto + Debug + Display + Hash diff --git a/rust/src/lib.rs b/rust/src/lib.rs index d2f676a..b57ad59 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -15,7 +15,7 @@ //! | [`SignedNumber`] | Extends [`Number`] with signed operations (`Signed + FromPrimitive`) | //! | [`ToSigned`] | Converts an unsigned type to its signed counterpart (e.g. `u32` → `i32`) | //! | [`MaxValue`] | Provides a `MAX` associated constant for every primitive integer type | -//! | [`LinkReference`] | Composite trait for link identifiers — unsigned, hashable, displayable, thread-safe | +//! | [`LinkReference`] | Composite trait for link identifiers — unsigned, hashable, displayable, thread-safe, with `TryFrom`/`TryInto` for all integer types | //! //! ## Example //! diff --git a/rust/tests/traits.rs b/rust/tests/traits.rs index d2b5950..98583fa 100644 --- a/rust/tests/traits.rs +++ b/rust/tests/traits.rs @@ -338,6 +338,12 @@ fn test_link_reference_for_u64() { assert_link_reference(0u64); } +#[test] +fn test_link_reference_for_u128() { + fn assert_link_reference(_val: T) {} + assert_link_reference(0u128); +} + #[test] fn test_link_reference_for_usize() { fn assert_link_reference(_val: T) {} @@ -423,6 +429,7 @@ fn test_link_reference_can_be_converted_to_signed() { assert_eq!(use_link_reference(42u16), 42i16); assert_eq!(use_link_reference(42u32), 42i32); assert_eq!(use_link_reference(42u64), 42i64); + assert_eq!(use_link_reference(42u128), 42i128); assert_eq!(use_link_reference(42usize), 42isize); } @@ -435,6 +442,7 @@ fn test_link_reference_has_max_value() { assert_eq!(get_max::(), u16::MAX); assert_eq!(get_max::(), u32::MAX); assert_eq!(get_max::(), u64::MAX); + assert_eq!(get_max::(), u128::MAX); assert_eq!(get_max::(), usize::MAX); } @@ -556,6 +564,7 @@ fn test_link_reference_is_send_sync() { assert_send_sync::(); assert_send_sync::(); assert_send_sync::(); + assert_send_sync::(); assert_send_sync::(); } @@ -589,3 +598,239 @@ fn test_link_reference_from_primitive() { assert_eq!(link_from_u64::(255), Some(255u8)); assert_eq!(link_from_u64::(256), None); // overflow } + +// ========================================== +// Tests for LinkReference - TryFrom bounds +// ========================================== + +#[test] +fn test_link_reference_try_from_byte() { + fn try_from_byte(val: u8) -> T { + T::try_from(val).unwrap() + } + assert_eq!(try_from_byte::(42), 42u8); + assert_eq!(try_from_byte::(42), 42u16); + assert_eq!(try_from_byte::(42), 42u32); + assert_eq!(try_from_byte::(42), 42u64); + assert_eq!(try_from_byte::(42), 42u128); + assert_eq!(try_from_byte::(42), 42usize); +} + +#[test] +fn test_link_reference_try_from_i8() { + fn try_from_i8(val: i8) -> Option { + T::try_from(val).ok() + } + assert_eq!(try_from_i8::(42), Some(42u8)); + assert_eq!(try_from_i8::(42), Some(42u32)); + assert_eq!(try_from_i8::(-1), None); +} + +#[test] +fn test_link_reference_try_from_u16() { + fn try_from_u16(val: u16) -> Option { + T::try_from(val).ok() + } + assert_eq!(try_from_u16::(255), Some(255u8)); + assert_eq!(try_from_u16::(256), None); + assert_eq!(try_from_u16::(1000), Some(1000u32)); +} + +#[test] +fn test_link_reference_try_from_u64() { + fn try_from_u64(val: u64) -> Option { + T::try_from(val).ok() + } + assert_eq!(try_from_u64::(255), Some(255u8)); + assert_eq!(try_from_u64::(256), None); + assert_eq!(try_from_u64::(u64::MAX), Some(u64::MAX)); +} + +#[test] +fn test_link_reference_try_from_i128() { + fn try_from_i128(val: i128) -> Option { + T::try_from(val).ok() + } + assert_eq!(try_from_i128::(42), Some(42u32)); + assert_eq!(try_from_i128::(-1), None); + assert_eq!(try_from_i128::(i128::MAX), None); +} + +#[test] +fn test_link_reference_try_from_usize() { + fn try_from_usize(val: usize) -> Option { + T::try_from(val).ok() + } + assert_eq!(try_from_usize::(100), Some(100u8)); + assert_eq!(try_from_usize::(256), None); + assert_eq!(try_from_usize::(12345), Some(12345usize)); +} + +// ========================================== +// Tests for LinkReference - TryInto bounds +// ========================================== + +#[test] +fn test_link_reference_try_into_u8() { + fn try_into_u8(val: T) -> Option { + val.try_into().ok() + } + assert_eq!(try_into_u8(255u16), Some(255u8)); + assert_eq!(try_into_u8(256u16), None); + assert_eq!(try_into_u8(42u64), Some(42u8)); +} + +#[test] +fn test_link_reference_try_into_i64() { + fn try_into_i64(val: T) -> Option { + val.try_into().ok() + } + assert_eq!(try_into_i64(42u32), Some(42i64)); + assert_eq!(try_into_i64(u64::MAX), None); +} + +#[test] +fn test_link_reference_try_into_usize() { + fn try_into_usize(val: T) -> Option { + val.try_into().ok() + } + assert_eq!(try_into_usize(42u8), Some(42usize)); + assert_eq!(try_into_usize(1000u32), Some(1000usize)); +} + +#[test] +fn test_link_reference_try_into_i128() { + fn try_into_i128(val: T) -> Option { + val.try_into().ok() + } + assert_eq!(try_into_i128(42u8), Some(42i128)); + assert_eq!(try_into_i128(u64::MAX), Some(u64::MAX as i128)); +} + +// ========================================== +// Tests for LinkReference - u128 large values +// ========================================== + +#[test] +fn test_link_reference_u128_large_values() { + let max: u128 = u128::MAX; + assert_eq!(max, 340282366920938463463374607431768211455u128); + + let val: u128 = u128::from_byte(255); + assert_eq!(val, 255u128); +} + +#[test] +fn test_link_reference_u128_try_from_all_integer_types() { + assert_eq!(u128::try_from(127i8).unwrap(), 127u128); + assert_eq!(u128::from(255u8), 255u128); + assert_eq!(u128::try_from(32767i16).unwrap(), 32767u128); + assert_eq!(u128::from(65535u16), 65535u128); + assert_eq!(u128::try_from(2147483647i32).unwrap(), 2147483647u128); + assert_eq!(u128::from(4294967295u32), 4294967295u128); + assert_eq!(u128::try_from(i64::MAX).unwrap(), i64::MAX as u128); + assert_eq!(u128::from(u64::MAX), u64::MAX as u128); + assert_eq!(u128::try_from(i128::MAX).unwrap(), i128::MAX as u128); + assert!(u128::try_from(-1i8).is_err()); + assert!(u128::try_from(-1i128).is_err()); +} + +#[test] +fn test_link_reference_u128_try_into_smaller_types() { + let val: u128 = 42; + let as_u8: Result = val.try_into(); + assert_eq!(as_u8.unwrap(), 42u8); + + let as_u64: Result = val.try_into(); + assert_eq!(as_u64.unwrap(), 42u64); + + let big: u128 = u128::MAX; + let as_u64: Result = big.try_into(); + assert!(as_u64.is_err()); +} + +#[test] +fn test_link_reference_u128_from_byte_all_range() { + for n in 0..=255u8 { + assert_eq!(u128::from_byte(n), n as u128); + } +} + +// ========================================== +// Tests for LinkReference - from_byte method +// ========================================== + +#[test] +fn test_link_reference_from_byte() { + fn make_val(n: u8) -> T { + T::from_byte(n) + } + assert_eq!(make_val::(0), 0u8); + assert_eq!(make_val::(255), 255u8); + assert_eq!(make_val::(42), 42u16); + assert_eq!(make_val::(100), 100u32); + assert_eq!(make_val::(1), 1u64); + assert_eq!(make_val::(200), 200u128); + assert_eq!(make_val::(0), 0usize); +} + +#[test] +fn test_link_reference_from_byte_all_u8_range_for_u16() { + for n in 0..=255u8 { + assert_eq!(u16::from_byte(n), n as u16); + } +} + +#[test] +fn test_link_reference_from_byte_zero() { + assert_eq!(u8::from_byte(0), 0u8); + assert_eq!(u16::from_byte(0), 0u16); + assert_eq!(u32::from_byte(0), 0u32); + assert_eq!(u64::from_byte(0), 0u64); + assert_eq!(u128::from_byte(0), 0u128); + assert_eq!(usize::from_byte(0), 0usize); +} + +#[test] +fn test_link_reference_from_byte_one() { + assert_eq!(u8::from_byte(1), 1u8); + assert_eq!(u16::from_byte(1), 1u16); + assert_eq!(u32::from_byte(1), 1u32); + assert_eq!(u64::from_byte(1), 1u64); + assert_eq!(u128::from_byte(1), 1u128); + assert_eq!(usize::from_byte(1), 1usize); +} + +// ========================================== +// Tests for LinkReference - Sized bound +// ========================================== + +#[test] +fn test_link_reference_is_sized() { + fn assert_sized() {} + assert_sized::(); + assert_sized::(); + assert_sized::(); + assert_sized::(); + assert_sized::(); + assert_sized::(); +} + +// ========================================== +// Tests for LinkReference - Error: Debug bound +// ========================================== + +#[test] +fn test_link_reference_try_from_error_is_debug() { + fn check_error_debug() { + if let Err(e) = T::try_from(-1i8) { + let _ = format!("{e:?}"); + } + } + check_error_debug::(); + check_error_debug::(); + check_error_debug::(); + check_error_debug::(); + check_error_debug::(); + check_error_debug::(); +}