Skip to content

Split api/links/routes.rs into landing.rs, qr.rs, and slimmer routes.rs#116

Open
saltyskip wants to merge 2 commits intomainfrom
split-links-routes
Open

Split api/links/routes.rs into landing.rs, qr.rs, and slimmer routes.rs#116
saltyskip wants to merge 2 commits intomainfrom
split-links-routes

Conversation

@saltyskip
Copy link
Copy Markdown
Owner

Summary

api/links/routes.rs was 2249 lines — roughly 5× the median routes file in the codebase. A new contributor (or AI agent) opening it had to scroll past ~370 lines of HTML rendering and ~265 lines of QR styling code before reaching the actual route handlers, even when their task only touched link CRUD. This PR splits the file into three focused siblings.

What moved

New file Lines Contains
api/links/landing.rs ~520 LandingPageContext, render_smart_landing_page, plus rendering-only helpers (social_preview_from_metadata, js_escape, build_agent_panel, action_to_schema_type)
api/links/qr.rs ~365 QrOutputFormat, render_link_qr (the orchestrator), QrRenderOptions, LogoImage, all parse_* validators, fetch_logo, render_qr
api/links/routes.rs ~1400 Route handlers + helpers genuinely shared across multiple flows

What stayed in routes.rs

  • All route handlers (link CRUD, resolve, attribution, timeseries)
  • do_resolve (used by both resolve handlers)
  • check_link_resolvable (used by resolve and attribution paths)
  • Platform / detect_platform (used across resolve, attribution, landing)
  • compute_link_status, lookup_tenant_domain, canonical_link_url, build_rift_meta (used by JSON resolve and landing)
  • link_error_to_response, is_valid_link_id
  • html_escape, urlencoding — used by both routes.rs and the new modules

Visibility model

Same pub(crate) pattern we used for the attribution slice's helper reuse: types and helpers crossing module boundaries within the crate are pub(crate). This includes:

  • Platform, Platform::as_str (for detect_platform callers)
  • html_escape, urlencoding, canonical_link_url (used by landing)
  • LandingPageContext (constructed in routes, consumed by landing)
  • QrOutputFormat, render_link_qr (called from the route handler)

Test plan

  • cargo fmt -- --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test — 107 lib + 145 integration tests pass
  • Pure code motion. No behavior change. URLs unchanged. OpenAPI unchanged.

Note on stacking

This branches off main (which doesn't yet have #115). When #115 (the attribution slice extraction) lands, routes.rs will lose the three attribution handlers, taking it to ~1100 lines. Both PRs are independent code motion in the same file but in non-overlapping regions; rebase is mechanical either way.

🤖 Generated with Claude Code

`api/links/routes.rs` was 2249 lines — about 5x the median routes file.
A new contributor (or AI agent) opening it had to load ~370 lines of
HTML rendering and ~265 lines of QR styling before reaching the actual
route handlers, even when their task only touched link CRUD.

Three sibling modules now share the file's responsibilities:

- `api/links/landing.rs` (~520 lines) — `LandingPageContext`,
  `render_smart_landing_page`, and the rendering-only helpers
  (`social_preview_from_metadata`, `js_escape`, `build_agent_panel`,
  `action_to_schema_type`).
- `api/links/qr.rs` (~365 lines) — `QrOutputFormat`, `render_link_qr`
  (the orchestrator), `QrRenderOptions`, `LogoImage`, all the
  `parse_*` validators, `fetch_logo`, and `render_qr`.
- `api/links/routes.rs` (~1400 lines) — handlers for link CRUD,
  resolve, attribution, timeseries, plus the helpers genuinely shared
  across multiple flows (`html_escape`, `urlencoding`,
  `canonical_link_url`, `compute_link_status`, `lookup_tenant_domain`,
  `build_rift_meta`, `check_link_resolvable`, `do_resolve`, `Platform`,
  `detect_platform`).

Helpers used by both routes.rs and the new modules are marked
`pub(crate)`: `html_escape`, `urlencoding`, `canonical_link_url`,
`Platform`. Same approach as the attribution slice's reuse of
`check_link_resolvable` etc.

Pure code motion. No behavior change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
rift Ready Ready Preview, Comment May 1, 2026 5:57pm

Request Review

I left `action_to_schema_type` and `social_preview_from_metadata` at
the top of `landing.rs` when I created the file — both private
helpers above the public `render_smart_landing_page`. That violates
the stepdown rule we already codified.

Reordered: `LandingPageContext` + `render_smart_landing_page` now sit
at the top, all four private helpers (`action_to_schema_type`,
`social_preview_from_metadata`, `js_escape`, `build_agent_panel`)
collected in a `// ── Helpers ──` section at the bottom.

A reader opening the file now sees the file's reason to exist first
(the pub(crate) entry point) and descends into helpers as they read.

Caught by review, not by the architecture test — the test's enforced
file list doesn't yet include `landing.rs`. Worth a follow-up to
flip that to a denylist so new helper files are caught automatically.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant