cpu-local 0.2.0

Architecture CPU-local register capability for TGOSKits
Documentation
use std::{fs, path::PathBuf};

fn crate_root() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}

fn rust_sources() -> String {
    let root = crate_root().join("src");
    let mut pending = vec![root];
    let mut source = String::new();
    while let Some(path) = pending.pop() {
        for entry in fs::read_dir(path).unwrap() {
            let entry = entry.unwrap();
            let path = entry.path();
            if path.is_dir() {
                pending.push(path);
            } else if path.extension().is_some_and(|extension| extension == "rs") {
                source.push_str(&fs::read_to_string(path).unwrap());
            }
        }
    }
    source
}

#[test]
fn cpu_local_has_no_versioned_or_trait_ffi_surface() {
    let source = rust_sources();
    for forbidden in [
        "CPU_LOCAL_ABI_VERSION",
        "CpuBindingV1",
        "CpuBindingResultV1",
        "CpuLocalStatus",
        "CpuLocalPlatformV1",
        "RegisterModeV1",
        "HostLevelV1",
        "cpu-local_0_1",
        "pub mod raw",
    ] {
        assert!(
            !source.contains(forbidden),
            "cpu-local still contains obsolete surface {forbidden}"
        );
    }

    let manifest = fs::read_to_string(crate_root().join("Cargo.toml")).unwrap();
    assert!(!manifest.contains("trait-ffi"));
    assert!(manifest.contains("thiserror.workspace = true"));
}

#[test]
fn public_errors_use_thiserror_without_manual_trait_impls() {
    let source = rust_sources();
    assert!(source.contains("thiserror::Error"));
    assert!(!source.contains("impl fmt::Display for Cpu"));
    assert!(!source.contains("impl core::error::Error for Cpu"));
    assert!(!source.contains("impl fmt::Display for ContextSwitchError"));
    assert!(!source.contains("impl core::error::Error for ContextSwitchError"));
}

#[test]
fn pin_is_scoped_and_cannot_be_forged_directly() {
    let pin = fs::read_to_string(crate_root().join("src/pin.rs")).unwrap();
    assert!(pin.contains("pub struct CpuPin<'scope>"));
    assert!(pin.contains("pub struct ExclusiveCpu<'pin>"));
    assert!(pin.contains("pub unsafe fn with_cpu_pin"));
    assert!(!pin.contains("CpuPin::new_unchecked"));
    assert!(!pin.contains("pub const unsafe fn new_unchecked"));
}

#[test]
fn preemption_handoff_is_linear_and_scheduler_neutral() {
    let preempt = fs::read_to_string(crate_root().join("src/preempt.rs")).unwrap();
    for declaration in [
        "pub struct PreemptionToken",
        "pub struct PendingPreemption",
        "pub enum PreemptionExit",
        "pub fn enter_preemption() -> PreemptionToken",
        "pub fn handoff_preemption_after_context_switch(",
        "pub fn finish_preemption(token: PreemptionToken) -> PreemptionExit",
        "pub fn release_initial_context_preemption(",
    ] {
        assert!(preempt.contains(declaration), "missing `{declaration}`");
    }
    assert!(preempt.matches("#[must_use").count() >= 3);
    assert!(!preempt.contains("scheduler"));
    assert!(!preempt.contains("baton"));
    assert!(!preempt.contains("RuntimeThreadCookie"));
}