//! Shared helpers for the workspace's integration-test suites.
//!
//! Several crates' `tests/*.rs` files need the same handful of path
//! computations and the same fixture-corpus filter. Before this crate
//! they were copy-pasted per file and drifted out of sync. This crate
//! is the single source; integration tests depend on it as a
//! `dev-dependency` and `use ailang_test_support::*`.
//!
//! Path helpers resolve relative to *this* crate's `CARGO_MANIFEST_DIR`
//! (`crates/ailang-test-support`), so they are independent of which
//! crate's test binary calls them.

use std::path::PathBuf;

/// Absolute path to the workspace root (the directory containing
/// `crates/` and `examples/`).
pub fn workspace_root() -> PathBuf {
    let crate_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    crate_dir
        .parent()
        .unwrap()
        .parent()
        .unwrap()
        .to_path_buf()
}

/// [`workspace_root`] with symlinks resolved and the path normalized
/// (`std::fs::canonicalize`). Used by the tsan/race tests that feed the
/// root into native build/link steps where a symlinked checkout would
/// otherwise leak a non-canonical path.
pub fn canonical_workspace_root() -> PathBuf {
    std::fs::canonicalize(workspace_root()).unwrap()
}

/// Absolute path to the `examples/` fixture directory at the workspace
/// root.
pub fn examples_dir() -> PathBuf {
    workspace_root().join("examples")
}

/// Fixtures under `examples/` that intentionally do NOT parse — the
/// `#55` cutover reject corpus. Negative fixtures whose whole point is
/// that the parser rejects them, so they have no AST and any corpus
/// scan must skip them.
///
/// - `bare_slot_reject.ail`: a bare fn-type slot with no mode. The
///   binary-`ParamMode` parser rejects it ("fn-type slot requires a
///   mode: write (own T) or (borrow T)").
pub const NON_PARSEABLE_FIXTURES: &[&str] = &["bare_slot_reject.ail"];

/// All parseable `.ail` fixtures under `examples/`, sorted, with the
/// [`NON_PARSEABLE_FIXTURES`] reject corpus filtered out.
pub fn list_ail_fixtures() -> Vec<PathBuf> {
    let dir = examples_dir();
    let mut paths: Vec<PathBuf> = std::fs::read_dir(&dir)
        .unwrap_or_else(|e| panic!("read_dir({}): {e}", dir.display()))
        .filter_map(|entry| entry.ok())
        .map(|e| e.path())
        .filter(|p| {
            p.file_name()
                .and_then(|n| n.to_str())
                .map(|n| n.ends_with(".ail") && !NON_PARSEABLE_FIXTURES.contains(&n))
                .unwrap_or(false)
        })
        .collect();
    paths.sort();
    paths
}
