//! The logic that gets executed before building the binary and tests.
//! We use it to auto-generate the Wasm spectests for each of the
//! available compilers.
//!
//! Please try to keep this file as clean as possible.

use std::env;
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use test_generator::test_directory_module;
use test_generator::{Testsuite, test_directory, wasi_processor, wast_processor, with_test_module};

fn main() -> anyhow::Result<()> {
    println!(
        "cargo:rustc-env=CFG_TARGET_OS={}",
        std::env::var("CARGO_CFG_TARGET_OS")
            .expect("CARGO_CFG_TARGET_OS must be provided by cargo")
    );
    println!(
        "cargo:rustc-env=CFG_TARGET_ARCH={}",
        std::env::var("CARGO_CFG_TARGET_ARCH")
            .expect("CARGO_CFG_TARGET_ARCH must be provided by cargo")
    );
    println!(
        "cargo:rustc-env=CFG_TARGET_ENV={}",
        std::env::var("CARGO_CFG_TARGET_ENV")
            .expect("CARGO_CFG_TARGET_ENV must be provided by cargo")
    );

    // As rerun-if-changed doesn't support globs, we use another crate
    // to check changes in directories.
    build_deps::rerun_if_changed_paths("tests/wasi-wast/wasi/unstable/*")
        .expect("Can't get directory");
    build_deps::rerun_if_changed_paths("tests/wasi-wast/wasi/snapshot1/*")
        .expect("Can't get directory");
    build_deps::rerun_if_changed_paths("tests/wasi-wast/wasi/nightly-2022-10-18/*")
        .expect("Can't get directory");
    build_deps::rerun_if_changed_paths("tests/wast/spec/proposals/*").expect("Can't get directory");

    let out_dir = PathBuf::from(
        env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"),
    );

    // Spectests test generation
    {
        let mut spectests = Testsuite {
            buffer: String::new(),
            path: vec![],
        };

        with_test_module(&mut spectests, "spec", |spectests| {
            let _spec_tests = test_directory(spectests, "tests/wast/spec", wast_processor)?;
            Ok(())
        })?;
        with_test_module(&mut spectests, "wasmer", |spectests| {
            let _spec_tests = test_directory(spectests, "tests/wast/wasmer", wast_processor)?;
            test_directory_module(
                spectests,
                "tests/wast/spec/proposals/wide-arithmetic",
                wast_processor,
            )?;
            Ok(())
        })?;

        let spectests_output = out_dir.join("generated_spectests.rs");
        fs::write(&spectests_output, spectests.buffer)?;

        // Write out our auto-generated tests and opportunistically format them with
        // `rustfmt` if it's installed.
        // Note: We need drop because we don't want to run `unwrap` or `expect` as
        // the command might fail, but we don't care about it's result.
        drop(Command::new("rustfmt").arg(&spectests_output).status());
    }

    // Wasitest test generation
    {
        let mut wasitests = Testsuite {
            buffer: String::new(),
            path: vec![],
        };

        with_test_module(&mut wasitests, "wasitests", |wasitests| {
            for wasi_version in &["unstable", "snapshot1", "nightly_2022_10_18"] {
                with_test_module(wasitests, wasi_version, |wasitests| {
                    for (wasi_filesystem_test_name, wasi_filesystem_kind) in &[
                        ("host_fs", "WasiFileSystemKind::Host"),
                        ("mem_fs", "WasiFileSystemKind::InMemory"),
                        ("tmp_fs", "WasiFileSystemKind::Tmp"),
                        ("passthru_fs", "WasiFileSystemKind::PassthruMemory"),
                        ("union_fs", "WasiFileSystemKind::UnionHostMemory"),
                        ("root_fs", "WasiFileSystemKind::RootFileSystemBuilder"),
                    ] {
                        with_test_module(wasitests, wasi_filesystem_test_name, |wasitests| {
                            test_directory(
                                wasitests,
                                format!("tests/wasi-wast/wasi/{wasi_version}"),
                                |out, path| wasi_processor(out, path, wasi_filesystem_kind),
                            )
                        })?;
                    }

                    Ok(())
                })?;
            }

            Ok(())
        })?;

        let wasitests_output = out_dir.join("generated_wasitests.rs");
        fs::write(&wasitests_output, wasitests.buffer)?;

        drop(Command::new("rustfmt").arg(&wasitests_output).status());
    }

    Ok(())
}
