ejectest 0.2.0

Extract inline #[cfg(test)] mod tests into separate _tests.rs files.
Documentation

ejectest

CI crates.io License: MIT

Extract inline #[cfg(test)] mod tests { ... } into separate _tests.rs files.

Why?

Inline tests are convenient — until your files grow too large. Manually moving tests to a separate file means editing the source and creating a new test file with the right module path. That's busywork. ejectest does it in one command.

Usage

ejectest apply src/lib.rs           # extract tests into src/lib_tests.rs
ejectest apply src/                 # eject every inline module under a tree
ejectest apply --dry-run src/       # preview without writing files
ejectest check src/                 # CI gate: fail if any inline test module remains
ejectest --help                     # show all options

apply takes a file or a directory. Given a directory it walks the tree (recursively, honouring .gitignore), ejecting every file with an inline #[cfg(test)] mod tests { ... } block and skipping files already external or without a test module. Re-running on an ejected tree is a no-op, so the one-time migration and any later sweep are a single command.

check scans a file or directory (recursively, honouring .gitignore) and exits non-zero when any file still carries an inline #[cfg(test)] mod tests { ... } block — the cargo fmt --check idiom for the sibling-test-file convention.

Both subcommands accept --format <text|json>. JSON output has the same structure for a single file and for a directory tree:

ejectest check --format json src/
# {"files":[{"path":"src/lib.rs","status":"inline"}],"summary":{"total":1,"inline":1,"external":0,"no_tests":0}}

Install

cargo install ejectest

Or download a pre-built binary from the latest release.

Nix flake

Add ejectest as a flake input and include it in your dev shell:

{
  inputs = {
    ejectest = {
      url = "github:mlavrinenko/ejectest";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    # ... other inputs
  };

  outputs = { ejectest, nixpkgs, flake-utils, ... }:
    flake-utils.lib.eachDefaultSystem (system: {
      devShells.default = nixpkgs.legacyPackages.${system}.mkShell {
        nativeBuildInputs = [
          ejectest.packages.${system}.default
        ];
      };
    });
}

Or run it directly without installing:

nix run github:mlavrinenko/ejectest -- apply src/lib.rs

Library usage

Add to your Cargo.toml with default features disabled:

ejectest = { version = "0.2", default-features = false }
let result = ejectest::eject_tests(&source, "lib")?;
// result.modified_source  — source with tests replaced by a #[path] stub
// result.test_content     — extracted test file contents
// result.test_file_name   — e.g. "lib_tests.rs"

// Read-only detection (powers `ejectest check`):
match ejectest::classify_source(&source) {
    ejectest::Classification::Inline => { /* would be ejected */ }
    ejectest::Classification::External => { /* already a #[path] module */ }
    ejectest::Classification::NoTests => {}
}

Contributing

See CONTRIBUTING.md for development setup and coding conventions.

License

MIT