Skip to content

refactor(server): validate the namespace once and split the sweep out of the store - #35

Merged
BryanFRD merged 1 commit into
mainfrom
refactor/request-invariants
Aug 14, 2026
Merged

refactor(server): validate the namespace once and split the sweep out of the store#35
BryanFRD merged 1 commit into
mainfrom
refactor/request-invariants

Conversation

@BryanFRD

Copy link
Copy Markdown
Contributor

No behaviour change. Same 30 tests, same e2e, unchanged — which is the point: if any of this altered what the server does, they would say so.

The namespace was validated twice

The middleware parsed {org}/{repo}, validated it, and threw it away. Every handler then re-read the path parameters and called Namespace::new again, so the same validation ran twice per request and each new handler had to remember to do it — a rule enforced by discipline rather than by the compiler.

The middleware now puts the validated Namespace in the request extensions and handlers take it as Extension<Namespace>. A handler cannot run without one, because the only thing that constructs it is the middleware that guards those routes. retain loses three lines, download loses its whole body prologue.

Namespace also drops its lifetime parameter and owns its two strings. The borrow existed to avoid two allocations per request, which is not a real saving next to streaming multi-gigabyte objects, and it was threaded through five modules. It gained a Display (org/repo), which is what config, github and cache were each formatting by hand — the GitHub URL is now {api_url}/repos/{ns}.

The store was doing two jobs

storage.rs had grown to 230 lines covering both the transfer path (write, verify, read) and collection (walk, age, delete, prune). Those change for different reasons and will keep diverging — #8, #26 and #32 all land on the maintenance side.

Split into storage/mod.rs (146 lines) and storage/sweep.rs (100), with SweepReport moving next to the code that produces it. A child module reaches the parent's private fields, so LocalStore keeps its encapsulation and the split costs nothing at the call site.

Left alone deliberately

Moving the signal handling out of main.rs would have collided with #33, which is open and touches exactly those lines. Not worth a conflict for a 30-line file.

Copilot AI lite review requested due to automatic review settings August 14, 2026 12:17
@BryanFRD
BryanFRD enabled auto-merge (squash) August 14, 2026 12:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@ferrfleet ferrfleet Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the diff end to end. Clean mechanical refactor, matches the description:

  • authorize middleware now inserts the validated Namespace into request extensions; every handler that needs it (batch, upload, download, verify, retain) sits behind route_layer(authorize) on the objects router, so Extension<Namespace> can never be missing at runtime.
  • Namespace dropping its lifetime and gaining Display is applied consistently — checked all former Namespace<'_> call sites (auth.rs, cache.rs, github.rs, config.rs, storage, routes.rs) and none were missed.
  • upload/download's Path((.., oid)): Path<(String, String, String)> is valid and still extracts the right positional segment now that org/repo come from the extension instead.
  • storage/sweep.rs reaching LocalStore's private fields from a child module is legitimate (module-tree privacy), so the split costs no encapsulation as claimed.

Nothing blocking. No nits either — this is a tidy, low-risk refactor.

@BryanFRD
BryanFRD merged commit 71b1ac3 into main Aug 14, 2026
12 checks passed
@BryanFRD
BryanFRD deleted the refactor/request-invariants branch August 14, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants