| 43 | """Return a callable that runs cargo metadata + parallel rustfmt.""" |
| 44 | |
| 45 | def run() -> tuple[bool, str]: |
| 46 | ncpus = os.cpu_count() or 8 |
| 47 | |
| 48 | result = subprocess.run( |
| 49 | ["cargo", "metadata", "--no-deps", "--format-version=1"], |
| 50 | capture_output=True, |
| 51 | text=True, |
| 52 | ) |
| 53 | if result.returncode != 0: |
| 54 | return False, result.stderr.strip() |
| 55 | |
| 56 | meta = json.loads(result.stdout) |
| 57 | kinds = {"lib", "bin", "bench", "test", "example", "proc-macro", "custom-build"} |
| 58 | paths = [ |
| 59 | t["src_path"] |
| 60 | for pkg in meta["packages"] |
| 61 | for t in pkg["targets"] |
| 62 | if kinds & set(t["kind"]) |
| 63 | ] |
| 64 | if not paths: |
| 65 | return True, "" |
| 66 | |
| 67 | # Split into batches and run rustfmt in parallel. |
| 68 | batch_size = math.ceil(len(paths) / ncpus) |
| 69 | batches = [paths[i : i + batch_size] for i in range(0, len(paths), batch_size)] |
| 70 | |
| 71 | cmd_base = ["rustfmt", "--config", "error_on_line_overflow=true"] |
| 72 | if check: |
| 73 | cmd_base.append("--check") |
| 74 | |
| 75 | procs = [ |
| 76 | subprocess.Popen( |
| 77 | cmd_base + batch, |
| 78 | stdout=subprocess.PIPE, |
| 79 | stderr=subprocess.STDOUT, |
| 80 | ) |
| 81 | for batch in batches |
| 82 | ] |
| 83 | |
| 84 | all_output = [] |
| 85 | all_ok = True |
| 86 | for proc in procs: |
| 87 | stdout, _ = proc.communicate() |
| 88 | if proc.returncode != 0: |
| 89 | all_ok = False |
| 90 | out = stdout.decode("utf-8").strip() |
| 91 | if out: |
| 92 | all_output.append(out) |
| 93 | |
| 94 | return all_ok, "\n".join(all_output) |
| 95 | |
| 96 | return run |
| 97 | |