// TEMPRS — ENGINEERING REPORT

Stack-based temporary file manager in Rust · v2.9.12 · clap CLI · fs2 flock-protected master record · null-byte delimited storage · atomic renames

>_EXECUTIVE SUMMARY

temprs is a single-binary stack-based temporary file manager written in Rust. The CLI is built on clap; persistence runs through a null-byte delimited master record with atomic renames and flock-protected exclusive access. Every operation that takes an INDEX also accepts a tag name (@name) — resolution falls through numeric parse to tag lookup. Two binaries (tp and temprs) ship from one Cargo target. 4,767 production Rust lines + 57,945 test Rust lines + 5,644 tests (5,059 unit + 585 integration) back the surface: stack mutation (push, pop, shift, unshift, insert, remove, move, swap, duplicate, reverse, sort), inspection (head, tail, wc, size, path, info), transformation (find-and-replace, grep, concat, diff), and editor handoff. CI runs check + test + fmt + clippy + doc + release build on every push.

4,767
Production Rust Lines
5,644
Tests (Verified Passing)
2.9.12
Crates.io Version
2
Binaries (tp+temprs)
5
Direct Dependencies
99
Transitive Crates
290
Git Commits

Source distribution — 62,712 Rust lines total

4,767 production / 57,945 test · 92.4% test code

Production source: src/lib.rs, src/main.rs, src/util/{consts,utils,mod}.rs, src/model/{mod,app,state/mod}.rs, and the CLI definition in src/model/opts.rs (lines 1–259). Test source: tests/integration.rs + 7 contract/edge test files + inline test modules in src/model/opts.rs, src/model/apply_permutation_tests.rs, src/model/state/tests.rs.


~SOURCE LAYOUT

Module-level breakdown of the Rust source tree. opts.rs dominates the line count because the inline test module holds 4,449 combinatorial CLI argument tests — the production CLI definition itself is the first 259 lines of that file.

File Lines Tests Role
src/main.rs9Binary entrypoint (shared by tp + temprs)
src/lib.rs10Library root; re-exports model + util
src/util/mod.rs4Util module declaration
src/util/consts.rs91887Flag names, error strings, banner, separators (inline tests for string contracts)
src/util/utils.rs2,204174Stack ops, master-record I/O, flock, filesystem helpers (inline unit tests)
src/model/mod.rs9Model module declaration
src/model/app.rs1,1026Top-level dispatch — matches parsed flags to handlers (smoke tests)
src/model/state/mod.rs252State struct — in-memory mirror of the master record
src/model/state/tests.rs1,980181State invariants — serialization round-trips, name uniqueness, edge cases
src/model/opts.rs (CLI def)259Production clap definition + cyberpunk help template
src/model/opts.rs (tests)46,8664,449Combinatorial flag parsing tests — long form, short form, value parsing, mutual exclusion
src/model/apply_permutation_tests.rs1,531162Stack mutation permutation suite — move/swap/sort/reverse correctness
tests/integration.rs5,900530End-to-end CLI tests — spawn the binary, pipe stdin, assert stdout/stderr/files
tests/edge_cases.rs42823Edge-case CLI scenarios — malformed input, overflow, exotic UTF-8
tests/contract_input_output_master.rs2679Master record input/output contract pins
tests/contract_algebra_round4.rs2566Stack-algebra round-trip contract pins
tests/contract_name_invariants.rs1938@name resolution invariants
tests/tag_alignment_after_mutation.rs2143Name/path parallel-array sync across shift, pop, rev, remove
tests/master_record_tag_roundtrip.rs1163Multi-tag master-record round-trip alignment
tests/expire_invalid_hours.rs1943--expire rejects negative / NaN / inf hours
benches/benchmarks.rs144Criterion benchmarks — stack ops, master record round-trips
TOTAL Rust62,8565,644All cargo test functions pass on every push to main

Note: the test-attribute grep yields 4,449 + 162 + 181 + 174 + 87 + 6 + 530 + 23 + 9 + 6 + 8 + 3 + 3 + 3 = 5,644 across inline + tests/ files.


$DEPENDENCIES

Five direct runtime dependencies, one dev dependency. The lock file resolves to 99 transitive crates. Selection criteria: foundational, audit-friendly, durable — no flavor-of-the-month wrappers.

CrateVersionRoleWhy
clap4.6CLI parserDe facto standard for Rust CLIs; derives long/short, mutual exclusion, value validation, custom help template
fs20.4File lockingFileExt::lock_exclusive / unlock — the master record's concurrency guard
log0.4Logging facadeDecouples library logging from the binary's choice of backend
simple_logger5.2Log backend (active)The runtime logger: State::new calls simple_logger::init_with_level(Info) — zero-config, on by default
env_logger0.11Log backend (declared)Declared in Cargo.toml for an env-driven backend; the binary's default init path uses simple_logger
criterion (dev)0.8Benchmark harnessStatistical benchmarking with regression detection — gated to [dev-dependencies]

&STORAGE FORMAT

The persistent state of the stack lives in a single master record file. The format is null-byte delimited so any filename — including ones with newlines, tabs, spaces, or other shell-hostile characters — round-trips losslessly. Writes are atomic; reads are flock-protected.

Field delimiter

\0 separates fields within a record (filename, optional tag).

Record delimiter

\0\0 separates records. Stack order = file order.

Atomic writes

Write to temprs-stack.tmp, rename() to temprs-stack. A crash leaves the previous master intact.

Exclusive locking

fs2::FileExt::lock_exclusive on temprs-stack.lock wraps every read-modify-write. Concurrent shells serialize.

Auto-recovery

Empty or malformed records on read are silently skipped and dropped on the next write.

Storage root

$TMPDIR/temprs by default; override with TEMPRS_DIR. The master record (temprs-stack) lives alongside the tempfile<timestamp> payloads.

Purge safety

tp -c refuses to clear unless the directory basename contains temprs — a guard against TEMPRS_DIR=$HOME wiping unrelated files.


%DISPATCH & RESOLUTION

One code path drives both binaries. main() constructs a TempApp and calls run(); run() reads the parsed clap matches and dispatches each flag in turn. Index/name resolution is a single fall-through function shared by every operation that takes an INDEX.

Entry point

src/main.rsTempApp::new() then app.run(). tp and temprs are the same binary built from one target.

Startup (State::new)

Init logger (simple_logger, Info), resolve TEMPRS_DIR (default $TMPDIR/temprs), create the directory if absent, then take an exclusive flock on temprs-stack.lock.

CLI definition

src/model/opts.rs (lines 1–259) — the clap Command with grouped cyberpunk help template, short + long forms, value names, and allow_hyphen_values so negative indices parse.

Dispatch

TempApp::run in src/model/app.rs — a flat sequence of get_flag / get_one / get_many checks, each calling the matching handler.

Index/name resolution

resolve_idxparse::<i32>() first (negatives mapped via util_transform_idx), then a name lookup by position() over the parallel names vector. Unresolved arguments terminate with Invalid specified index.

Parallel arrays

The stack is two aligned vectors — file paths and optional tags — kept in sync across every mutation. tests/tag_alignment_after_mutation.rs pins that sync across shift, pop, rev, and remove.


@CAPABILITY SURFACE

Grouped by behavior. Every operation that takes INDEX also accepts a tag name — the resolver tries numeric parse first, then falls through to tag lookup. Negative indices count from top of stack.

I/O

Push stdin or load a file; read by index or tag; verbose mode also tees to stdout.

  • tp — push stdin to top
  • tp FILE — load file to top
  • tp -i N — write stdin into N
  • tp -o N — read N to stdout
  • tp -A N — append stdin to N
  • tp -v — verbose (also tee to stdout)
  • tp -q — quiet (suppress create output)

Stack mutation

Position-based operations with the full Perl/Ruby array vocabulary.

  • -p pop, -u unshift, -s shift
  • -a N insert at N, -r N remove
  • -M src dst move
  • -S a b swap
  • -x N duplicate to top

Bulk

Whole-stack transforms and housekeeping.

  • --rev reverse
  • --sort name|size|mtime
  • -c purge all
  • --expire HOURS purge by mtime (fractional)

Listing

Five list modes; pick the verbosity you need.

  • -l names only
  • -L names + contents
  • -n numbered names
  • -N numbered + contents
  • -k stack size

Inspection

Read partial content or metadata without consuming the file.

  • --head N [LINES]
  • --tail N [LINES]
  • --wc N, --size N
  • --path N — raw FS path for $(...) substitution
  • -I N — metadata block

Search & transform

In-place edits and cross-stack search.

  • -g PAT — grep all tempfiles (exit 0/1)
  • --replace N PAT REPL — in-place find/replace, prints replacement count

Combine & compare

Treat the stack as a working set; combine or diff entries.

  • -C a b c — concat to stdout in given order
  • -D a b — unified diff of two tempfiles

Tagging

Optional string aliases. Travel with files through every mutation.

  • -w NAME — tag on creation
  • -R src NAME — rename
  • Names display as @name in listings

Editor

Direct handoff to $EDITOR.

  • -e N — open INDEX in $EDITOR
  • Fallback: vi
  • Works with -1 for top of stack

!TEST POSTURE

Test suites cover three layers: argument parsing, in-memory state invariants, and end-to-end CLI behavior. Aggregate: 5,644 tests pass in cargo test, all green in CI.

SuiteFileTestsCoverage
CLI parsingsrc/model/opts.rs4,449Combinatorial — every flag, long + short, value validation, mutual exclusion
Stack permutationssrc/model/apply_permutation_tests.rs162Move/swap/sort/reverse correctness across every starting permutation
State invariantssrc/model/state/tests.rs181Serialization round-trips, name uniqueness, malformed-record recovery
Integrationtests/integration.rs530End-to-end — spawn binary, pipe stdin, assert stdout/stderr/exit/files
Inline (consts + utils + app)src/util + src/model/app.rs267String contracts, stack op smoke tests, master-record I/O, flock
Edge casestests/edge_cases.rs23Malformed input, overflow, exotic UTF-8 boundary conditions
Contract pinstests/contract_*.rs + tag/master/expire32Master-record I/O, stack-algebra, @name invariants, parallel-array alignment, --expire input validation
TOTALcargo test5,644Wall-clock: ~3.4s (lib 0.35s + integration 3.01s); CI gate on every push to main

#SHIP SURFACE

What you get when you cargo install temprs or unpack a release artifact.

Binaries

tp — short form. temprs — long form. Identical builds from one Cargo target; LTO + panic=abort + strip=true + codegen-units=1 for release.

zsh completion

completions/_temprs — dynamic resolution of stack indices, filenames, and @name tags. Tab-completion sees the live stack, not a static word list.

man pages

Two groff manuals under man/man1/: temprs.1 (320 lines, short reference, man temprs) and temprsall.1 (653 lines, full manual, man temprsall). Install to a directory on $MANPATH.

Library crate

temprs is also published as a library (src/lib.rs) — downstream crates can use temprs::* for state, dispatch, and stack ops without forking the binary.

CI workflow

.github/workflows/ci.yml runs check + test + fmt + clippy + doc + release build on every push to main and every pull request. Runnable manually via workflow dispatch.

Benchmarks

benches/benchmarks.rs — criterion harness over stack ops and master-record round-trips; run with cargo bench.


vVERSION HISTORY

Current crate: temprs 2.9.12. 290 commits on main. Last five commits:

HashSubject
eacc0c1e40docs: expand + reconcile index/report/reference
8443bc0305docs: reconcile stale references and counts with current code
d1d80f31a7docs: refresh engineering report/index HTML stats to current source of truth
4938e65dcbtests: master-record: parallel-array alignment across multi-tag round-trips
d0f25ce2a7docs: refresh stale counts — CI job list adds Docs gates + Polish gates

Full history: github.com/MenkeTechnologies/temprs/commits/main


>>> JACK IN. PUSH YOUR DATA. OWN YOUR TEMP FILES. <<<