refactor: Centralize the /u/{username}/{slug} permalink scheme in a domain permalinks module #35

Closed
opened 2026-07-03 00:09:54 +00:00 by rosa · 0 comments
Owner

Problem

The /u/{username}/{slug} URL layout is shared knowledge across ~15 modules. The domain crate owns a builder (post_source_url, crates/domain/src/services.rs:474) and a parser (resolve_post_url, crates/domain/src/services.rs:760), but the web crate re-derives the scheme by hand-formatting strings:

  • crates/web/src/handlers/posts.rs:54,156,192
  • crates/web/src/handlers/users.rs:507,532,555,606,630
  • crates/web/src/handlers/micropub.rs:57
  • crates/web/src/handlers/indieauth.rs:37,104,221
  • crates/web/src/pages.rs, components.rs, layouts.rs (maud templates)

Two costs:

  1. Changing the URL scheme is a shotgun edit across two crates.
  2. Hand-formatting is only safe because UserName/Slug newtypes never need URL-escaping — an invariant nothing states or enforces.

Proposal

Add a small permalinks module in vernier-domain that owns the scheme:

  • profile_path(&UserName) -> String
  • post_path(&UserName, &Slug) -> String
  • absolute-URL variants taking the base Url
  • the existing parser (resolve_post_url) moves here as its inverse

Web handlers, maud templates, and the feed builders call it; routes.rs stays the only place with Axum route templates. A deep module in the small-interface sense: a few one-line functions whose value is centralizing an invariant currently enforced by convention.

Non-goals

  • No change to the URL scheme itself.
  • routes.rs route templates stay as-is (Axum needs its own syntax).
## Problem The `/u/{username}/{slug}` URL layout is shared knowledge across ~15 modules. The domain crate owns a builder (`post_source_url`, `crates/domain/src/services.rs:474`) and a parser (`resolve_post_url`, `crates/domain/src/services.rs:760`), but the web crate re-derives the scheme by hand-formatting strings: - `crates/web/src/handlers/posts.rs:54,156,192` - `crates/web/src/handlers/users.rs:507,532,555,606,630` - `crates/web/src/handlers/micropub.rs:57` - `crates/web/src/handlers/indieauth.rs:37,104,221` - `crates/web/src/pages.rs`, `components.rs`, `layouts.rs` (maud templates) Two costs: 1. Changing the URL scheme is a shotgun edit across two crates. 2. Hand-formatting is only safe because `UserName`/`Slug` newtypes never need URL-escaping — an invariant nothing states or enforces. ## Proposal Add a small `permalinks` module in `vernier-domain` that owns the scheme: - `profile_path(&UserName) -> String` - `post_path(&UserName, &Slug) -> String` - absolute-URL variants taking the base `Url` - the existing parser (`resolve_post_url`) moves here as its inverse Web handlers, maud templates, and the feed builders call it; `routes.rs` stays the only place with Axum route templates. A deep module in the small-interface sense: a few one-line functions whose value is centralizing an invariant currently enforced by convention. ## Non-goals - No change to the URL scheme itself. - `routes.rs` route templates stay as-is (Axum needs its own syntax).
rosa added reference refactor/permalinks-module 2026-07-03 00:27:42 +00:00
rosa closed this issue 2026-07-03 00:29:16 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
rosa/vernier#35
No description provided.