platform-data 3.0.0

Data types and traits for the LinksPlatform
Documentation
use platform_data::{LinkReference, LinksConstants};

#[test]
fn test_external_range_starts_after_continue_u32() {
    let constants = LinksConstants::<u32>::external();

    assert!(!constants.is_external(constants.r#continue));
    assert!(!constants.is_reference(constants.r#continue));
    assert_eq!(constants.r#continue, 2_147_483_647);
    assert_eq!(constants.internal_range, 1..=2_147_483_641);
    assert_eq!(constants.external_range, Some(2_147_483_648..=u32::MAX));
}

fn assert_external_boundaries<T: LinkReference>() {
    let half = T::MAX / T::from_byte(2);
    let first_external = half + T::from_byte(1);
    for constants in [
        LinksConstants::<T>::external(),
        LinksConstants::via_only_external(true),
        LinksConstants::via_external(T::from_byte(5), true),
    ] {
        assert_eq!(constants.r#continue, half);
        assert_eq!(
            constants.internal_range,
            T::from_byte(1)..=half - T::from_byte(6)
        );
        assert_eq!(constants.external_range, Some(first_external..=T::MAX));
        assert!(!constants.is_reference(constants.null));
        for service in [
            constants.r#continue,
            constants.r#break,
            constants.skip,
            constants.any,
            constants.itself,
            constants.error,
        ] {
            assert!(!constants.is_internal(service));
            assert!(!constants.is_external(service));
            assert!(!constants.is_reference(service));
        }
        for internal in [
            *constants.internal_range.start(),
            *constants.internal_range.end(),
        ] {
            assert!(constants.is_internal(internal));
            assert!(!constants.is_external(internal));
            assert!(constants.is_reference(internal));
        }
        for external in [first_external, T::MAX] {
            assert!(!constants.is_internal(external));
            assert!(constants.is_external(external));
            assert!(constants.is_reference(external));
        }
    }
}

#[test]
fn test_external_boundaries_for_all_unsigned_address_types() {
    assert_external_boundaries::<u8>();
    assert_external_boundaries::<u16>();
    assert_external_boundaries::<u32>();
    assert_external_boundaries::<u64>();
    assert_external_boundaries::<u128>();
    assert_external_boundaries::<usize>();
}

#[test]
#[should_panic(
    expected = "external range must not overlap the internal range or reserved service values"
)]
fn test_full_new_rejects_external_overlap_with_continue() {
    LinksConstants::<u32>::full_new(2, 1..=100, Some(100..=200));
}

#[test]
fn test_via_ranges_rejects_overlapping_external_ranges() {
    for external in [1..=10, 20..=30, 94..=99, 95..=100, 1..=200] {
        assert!(
            std::panic::catch_unwind(|| {
                LinksConstants::<u32>::via_ranges(10..=100, Some(external.clone()))
            })
            .is_err(),
            "accepted overlapping external range {external:?}"
        );
    }
}

#[test]
fn test_via_ranges_preserves_disjoint_and_empty_external_ranges() {
    for external in [
        None,
        Some(1..=9),
        Some(101..=200),
        Some(std::ops::RangeInclusive::new(200, 101)),
    ] {
        let constants = LinksConstants::<u32>::via_ranges(10..=100, external.clone());
        assert_eq!(constants.internal_range, 10..=94);
        assert_eq!(constants.r#continue, 100);
        assert_eq!(constants.external_range, external);
        for service in [95, 96, 97, 98, 99, 100] {
            assert!(!constants.is_reference(service));
        }
    }
}

#[test]
fn test_new_creates_internal_constants() {
    let constants: LinksConstants<u64> = LinksConstants::new();
    // new() should return internal constants (no external range)
    assert!(constants.external_range.is_none());
    assert!(constants.internal_range.start() <= constants.internal_range.end());
}

#[test]
fn test_default_creates_internal_constants() {
    let constants: LinksConstants<u64> = LinksConstants::default();
    // default() should behave like new()
    assert!(constants.external_range.is_none());
}

#[test]
fn test_internal_creates_internal_only_constants() {
    let constants: LinksConstants<u64> = LinksConstants::internal();
    assert!(constants.external_range.is_none());
    // Internal range should span from 1 to MAX
    assert_eq!(*constants.internal_range.start(), 1);
}

#[test]
fn test_external_creates_constants_with_external_range() {
    let constants: LinksConstants<u64> = LinksConstants::external();
    assert!(constants.external_range.is_some());
    let external_range = constants.external_range.as_ref().unwrap();
    assert!(external_range.start() <= external_range.end());
}

#[test]
fn test_via_only_external_false() {
    let constants: LinksConstants<u64> = LinksConstants::via_only_external(false);
    assert!(constants.external_range.is_none());
}

#[test]
fn test_via_only_external_true() {
    let constants: LinksConstants<u64> = LinksConstants::via_only_external(true);
    assert!(constants.external_range.is_some());
}

#[test]
fn test_via_external_with_custom_target_part() {
    let constants: LinksConstants<u64> = LinksConstants::via_external(5, true);
    assert_eq!(constants.target_part, 5);
    assert!(constants.external_range.is_some());
}

#[test]
fn test_via_ranges_with_custom_ranges() {
    let internal = 1u64..=1000;
    let external = Some(1001u64..=2000);
    let constants: LinksConstants<u64> = LinksConstants::via_ranges(internal.clone(), external);
    assert_eq!(*constants.internal_range.start(), *internal.start());
    assert!(constants.external_range.is_some());
}

#[test]
fn test_via_ranges_without_external() {
    let internal = 1u64..=1000;
    let constants: LinksConstants<u64> = LinksConstants::via_ranges(internal, None);
    assert!(constants.external_range.is_none());
}

#[test]
fn test_full_new_sets_all_fields() {
    let constants: LinksConstants<u64> = LinksConstants::full_new(3, 1..=100, Some(101..=200));
    assert_eq!(constants.index_part, 0);
    assert_eq!(constants.source_part, 1);
    assert_eq!(constants.target_part, 3);
    assert_eq!(constants.null, 0);
    // continue is the end of internal range
    assert_eq!(constants.r#continue, 100);
    // break is end - 1
    assert_eq!(constants.r#break, 99);
    // skip is end - 2
    assert_eq!(constants.skip, 98);
    // any is end - 3
    assert_eq!(constants.any, 97);
    // itself is end - 4
    assert_eq!(constants.itself, 96);
    // error is end - 5
    assert_eq!(constants.error, 95);
    // internal_range is adjusted
    assert_eq!(*constants.internal_range.start(), 1);
    assert_eq!(*constants.internal_range.end(), 94); // end - 6
    assert!(constants.external_range.is_some());
}

#[test]
fn test_is_internal_for_address_in_internal_range() {
    let constants: LinksConstants<u64> = LinksConstants::new();
    // Address within internal range should return true
    assert!(constants.is_internal(1));
    assert!(constants.is_internal(100));
}

#[test]
fn test_is_internal_for_address_outside_range() {
    let constants: LinksConstants<u64> = LinksConstants::full_new(2, 10..=100, None);
    // Address outside internal range should return false
    assert!(!constants.is_internal(5)); // below range
}

#[test]
fn test_is_external_with_no_external_range() {
    let constants: LinksConstants<u64> = LinksConstants::internal();
    // Should return false when no external range is defined
    assert!(!constants.is_external(100));
}

#[test]
fn test_is_external_with_external_range() {
    let constants: LinksConstants<u64> = LinksConstants::external();
    let external_range = constants.external_range.as_ref().unwrap();
    // Address within external range should return true
    assert!(constants.is_external(*external_range.start()));
}

#[test]
fn test_is_reference_internal() {
    let constants: LinksConstants<u64> = LinksConstants::new();
    // Internal address should be a reference
    assert!(constants.is_reference(1));
}

#[test]
fn test_is_reference_external() {
    let constants: LinksConstants<u64> = LinksConstants::external();
    let external_range = constants.external_range.as_ref().unwrap();
    // External address should be a reference
    assert!(constants.is_reference(*external_range.start()));
}

#[test]
fn test_is_reference_neither() {
    let constants: LinksConstants<u64> = LinksConstants::full_new(2, 10..=100, None);
    // Address outside both ranges should not be a reference
    assert!(!constants.is_reference(5));
}

#[test]
fn test_constants_clone() {
    let constants: LinksConstants<u64> = LinksConstants::external();
    let cloned = constants.clone();
    assert_eq!(constants, cloned);
}

#[test]
fn test_constants_debug() {
    let constants: LinksConstants<u64> = LinksConstants::new();
    let debug_str = format!("{constants:?}");
    assert!(debug_str.contains("LinksConstants"));
}

#[test]
fn test_with_different_types() {
    // Test with u8
    let constants_u8: LinksConstants<u8> = LinksConstants::new();
    assert!(constants_u8.is_internal(1));

    // Test with u16
    let constants_u16: LinksConstants<u16> = LinksConstants::new();
    assert!(constants_u16.is_internal(1));

    // Test with u32
    let constants_u32: LinksConstants<u32> = LinksConstants::new();
    assert!(constants_u32.is_internal(1));

    // Test with usize
    let constants_usize: LinksConstants<usize> = LinksConstants::new();
    assert!(constants_usize.is_internal(1));
}

#[test]
fn test_via_external_false() {
    // Test via_external with external=false
    let constants: LinksConstants<u64> = LinksConstants::via_external(7, false);
    assert_eq!(constants.target_part, 7);
    assert!(constants.external_range.is_none());
}

#[test]
fn test_default_internal_external_ranges() {
    // Test with external mode to exercise default_external path
    let constants: LinksConstants<u64> = LinksConstants::via_external(2, true);
    // Should have external range
    assert!(constants.external_range.is_some());
    // Internal range should be limited
    assert!(*constants.internal_range.end() < u64::MAX);
}