From 827c5fa31762592fd849ec9ead0accdce6c8c7fa Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Jul 2026 21:49:07 +0000 Subject: [PATCH] docs(dev/guide): `command-extra` owned-builder convention Describe the `CommandExtra` owned-builder style that the integration tests use to construct `std::process::Command` values, and note that it does not apply to production code, which spawns no subprocesses. Cover the three method prefixes: `with_*` sets or adds, `without_*` removes, and `with_no_*` clears. Add a Quick Reference bullet to the AI instruction template and a dedicated section to CONTRIBUTING.md, then regenerate the AI instruction files from the template. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_015avap4WtjnrRCLcXwMYdCr --- .github/copilot-instructions.md | 1 + AGENTS.md | 1 + CLAUDE.md | 1 + CONTRIBUTING.md | 25 +++++++++++++++++++++++++ template/ai-instructions/shared.md | 1 + 5 files changed, 29 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 69660f88..cfbc548a 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -10,6 +10,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Use descriptive names for generics, such as `Size` and `Report`. Do not use single letters. - Use descriptive names for variables and closure parameters. Single letters are permitted only in these cases: (1) conventional names like `n` for count or `f` for formatter; (2) comparison closures like `|a, b|`; (3) trivial single-expression closures; (4) fold accumulators; and (5) index variables `i`/`j`/`k` in short closures or index-based loops. Single letters are never permitted in `let` bindings, including test fixtures, nor in multi-line functions or closures. - Use `pipe-trait` to chain through unary functions such as constructors, `Some`, `Ok`, and free functions. Use it to flatten nested calls and to continue method chains. Do not use it for simple standalone calls; prefer `foo(value)` over `value.pipe(foo)`. +- In test code, use the `command-extra` crate (the `CommandExtra` trait) when building `std::process::Command`. Call `.with_arg(...)`, `.with_env(...)`, `.without_env(...)`, `.with_no_env()`, and similar methods rather than `.arg(...)`, `.env(...)`, `.env_remove(...)`, and `.env_clear()`, so construction remains a single owned expression chain. - Prefer `where` clauses when a type has multiple trait bounds. - For error types, only derive `Display` and `Error` from `derive_more` when each is actually needed. Not all displayable types are errors. - Minimize `unwrap()` in non-test code. Use proper error handling instead. diff --git a/AGENTS.md b/AGENTS.md index 69660f88..cfbc548a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,6 +10,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Use descriptive names for generics, such as `Size` and `Report`. Do not use single letters. - Use descriptive names for variables and closure parameters. Single letters are permitted only in these cases: (1) conventional names like `n` for count or `f` for formatter; (2) comparison closures like `|a, b|`; (3) trivial single-expression closures; (4) fold accumulators; and (5) index variables `i`/`j`/`k` in short closures or index-based loops. Single letters are never permitted in `let` bindings, including test fixtures, nor in multi-line functions or closures. - Use `pipe-trait` to chain through unary functions such as constructors, `Some`, `Ok`, and free functions. Use it to flatten nested calls and to continue method chains. Do not use it for simple standalone calls; prefer `foo(value)` over `value.pipe(foo)`. +- In test code, use the `command-extra` crate (the `CommandExtra` trait) when building `std::process::Command`. Call `.with_arg(...)`, `.with_env(...)`, `.without_env(...)`, `.with_no_env()`, and similar methods rather than `.arg(...)`, `.env(...)`, `.env_remove(...)`, and `.env_clear()`, so construction remains a single owned expression chain. - Prefer `where` clauses when a type has multiple trait bounds. - For error types, only derive `Display` and `Error` from `derive_more` when each is actually needed. Not all displayable types are errors. - Minimize `unwrap()` in non-test code. Use proper error handling instead. diff --git a/CLAUDE.md b/CLAUDE.md index 2fe28b4b..e20b6cc6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,6 +10,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Use descriptive names for generics, such as `Size` and `Report`. Do not use single letters. - Use descriptive names for variables and closure parameters. Single letters are permitted only in these cases: (1) conventional names like `n` for count or `f` for formatter; (2) comparison closures like `|a, b|`; (3) trivial single-expression closures; (4) fold accumulators; and (5) index variables `i`/`j`/`k` in short closures or index-based loops. Single letters are never permitted in `let` bindings, including test fixtures, nor in multi-line functions or closures. - Use `pipe-trait` to chain through unary functions such as constructors, `Some`, `Ok`, and free functions. Use it to flatten nested calls and to continue method chains. Do not use it for simple standalone calls; prefer `foo(value)` over `value.pipe(foo)`. +- In test code, use the `command-extra` crate (the `CommandExtra` trait) when building `std::process::Command`. Call `.with_arg(...)`, `.with_env(...)`, `.without_env(...)`, `.with_no_env()`, and similar methods rather than `.arg(...)`, `.env(...)`, `.env_remove(...)`, and `.env_clear()`, so construction remains a single owned expression chain. - Prefer `where` clauses when a type has multiple trait bounds. - For error types, only derive `Display` and `Error` from `derive_more` when each is actually needed. Not all displayable types are errors. - Minimize `unwrap()` in non-test code. Use proper error handling instead. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2a2a2415..77ff9238 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -330,6 +330,31 @@ report.summarize().pipe(Some) entry.file_name().pipe(OsStringDisplay::from).pipe(Some) ``` +### Using `command-extra` + +The integration tests build `std::process::Command` values with the [`command-extra`](https://docs.rs/command-extra) crate, which offers a chainable, owned style. Import it as `use command_extra::CommandExtra;`. Production code does not spawn subprocesses, so this convention applies only to the tests. + +The standard `Command` builder methods, such as `arg`, `env`, and `current_dir`, take `&mut self` and return `&mut Command`. This makes them unsuitable for method chains that end in an owned value. The `CommandExtra` extension trait provides owned counterparts that take ownership and return an owned `Command`, enabling fluent one-expression construction: + +```rust +// Good: fully chainable, owned style +let output = Command::new(PDU) + .with_current_dir(&workspace) + .with_arg("--json-output") + .with_stdin(Stdio::null()) + .with_stdout(Stdio::piped()) + .output() + .expect("spawn pdu"); + +// Bad: the borrowing builders force a separate mutable binding +let mut command = Command::new(PDU); +command.current_dir(&workspace); +command.arg("--json-output"); +let output = command.output().expect("spawn pdu"); +``` + +The trait provides an owned counterpart for each standard builder method, and the names follow three prefix patterns. Methods that add or set a value keep the `with_*` prefix: `with_arg`, `with_args`, `with_current_dir`, `with_env`, `with_stdin`, `with_stdout`, and `with_stderr`. Removal takes the `without_*` prefix, so `without_env` is the counterpart of `env_remove`. Clearing takes the `with_no_*` prefix, so `with_no_env` is the counterpart of `env_clear`. + ### Pattern Matching When mapping enum variants to values, prefer the concise wrapping style: diff --git a/template/ai-instructions/shared.md b/template/ai-instructions/shared.md index 69660f88..cfbc548a 100644 --- a/template/ai-instructions/shared.md +++ b/template/ai-instructions/shared.md @@ -10,6 +10,7 @@ Read and follow the CONTRIBUTING.md file in this repository for all code style c - Use descriptive names for generics, such as `Size` and `Report`. Do not use single letters. - Use descriptive names for variables and closure parameters. Single letters are permitted only in these cases: (1) conventional names like `n` for count or `f` for formatter; (2) comparison closures like `|a, b|`; (3) trivial single-expression closures; (4) fold accumulators; and (5) index variables `i`/`j`/`k` in short closures or index-based loops. Single letters are never permitted in `let` bindings, including test fixtures, nor in multi-line functions or closures. - Use `pipe-trait` to chain through unary functions such as constructors, `Some`, `Ok`, and free functions. Use it to flatten nested calls and to continue method chains. Do not use it for simple standalone calls; prefer `foo(value)` over `value.pipe(foo)`. +- In test code, use the `command-extra` crate (the `CommandExtra` trait) when building `std::process::Command`. Call `.with_arg(...)`, `.with_env(...)`, `.without_env(...)`, `.with_no_env()`, and similar methods rather than `.arg(...)`, `.env(...)`, `.env_remove(...)`, and `.env_clear()`, so construction remains a single owned expression chain. - Prefer `where` clauses when a type has multiple trait bounds. - For error types, only derive `Display` and `Error` from `derive_more` when each is actually needed. Not all displayable types are errors. - Minimize `unwrap()` in non-test code. Use proper error handling instead.