refactor: Centralize the permalink scheme in a domain permalinks module #42

Merged
rosa merged 4 commits from refactor/permalinks-module into main 2026-07-03 00:41:31 +00:00
Owner

Closes #35.

The /u/{username}/{slug} URL layout was shared knowledge across ~15 modules: domain services owned a builder (post_source_url) and a parser (resolve_post_url), while the web crate re-derived the scheme by hand-formatting strings in handlers, pages, components, and layouts.

Changes

  • New vernier_domain::permalinks module as the scheme's single owner: profile_path, post_path, post_edit_path, post_delete_path, tag_path, absolute-URL variants (profile_url, post_url), and parse_post_url — plus a module doc stating the invariant that makes interpolation safe (UserName/Slug/Tag constructors admit only unreserved characters).
  • The parser speaks ResolvePostError (the existing shared resolution vocabulary) instead of MicropubError; resolve_owned_post converts at the boundary via the existing From impl, and the private resolve_post_url in services is deleted.
  • Every hand-formatted /u/... site in posts.rs, users.rs, micropub.rs, indieauth.rs, pages.rs, components.rs, and layouts.rs now calls the module.
  • Unit tests: round-trip through post_url/parse_post_url, path shapes, and rejection of off-site, non-post, and unparseable URLs.

The only remaining /u/ literals are the Axum route templates in routes.rs (the issue's stated non-goal — Axum needs its own syntax).

Verification

mise run ci passes: clippy -D warnings, fmt, sqlx prepare check, and all tests (34 app + 7 webmention integration tests among them), so the rendered pages, redirects, feed URLs, and IndieAuth me URLs are covered end to end.

Closes #35. The `/u/{username}/{slug}` URL layout was shared knowledge across ~15 modules: domain services owned a builder (`post_source_url`) and a parser (`resolve_post_url`), while the web crate re-derived the scheme by hand-formatting strings in handlers, pages, components, and layouts. ## Changes - New `vernier_domain::permalinks` module as the scheme's single owner: `profile_path`, `post_path`, `post_edit_path`, `post_delete_path`, `tag_path`, absolute-URL variants (`profile_url`, `post_url`), and `parse_post_url` — plus a module doc stating the invariant that makes interpolation safe (`UserName`/`Slug`/`Tag` constructors admit only unreserved characters). - The parser speaks `ResolvePostError` (the existing shared resolution vocabulary) instead of `MicropubError`; `resolve_owned_post` converts at the boundary via the existing `From` impl, and the private `resolve_post_url` in services is deleted. - Every hand-formatted `/u/...` site in `posts.rs`, `users.rs`, `micropub.rs`, `indieauth.rs`, `pages.rs`, `components.rs`, and `layouts.rs` now calls the module. - Unit tests: round-trip through `post_url`/`parse_post_url`, path shapes, and rejection of off-site, non-post, and unparseable URLs. The only remaining `/u/` literals are the Axum route templates in `routes.rs` (the issue's stated non-goal — Axum needs its own syntax). ## Verification `mise run ci` passes: clippy `-D warnings`, fmt, sqlx prepare check, and all tests (34 app + 7 webmention integration tests among them), so the rendered pages, redirects, feed URLs, and IndieAuth `me` URLs are covered end to end.
refactor: Centralize the permalink scheme in a domain permalinks module
Some checks failed
ci/woodpecker/push/test Pipeline was canceled
ci/woodpecker/pr/test Pipeline was canceled
1ce5b21471
The /u/{username}/{slug} URL layout was shared knowledge across ~15
modules: domain services owned a builder and a parser, while the web
crate re-derived the scheme by hand-formatting strings in handlers,
pages, components, and layouts.

Add vernier_domain::permalinks as the scheme's single owner: profile,
post, edit, delete, and tag paths, absolute-URL variants, and the
parser (now speaking ResolvePostError, the existing shared resolution
vocabulary). Convert every hand-formatted site to call it; the only
remaining /u/ literals are the Axum route templates in routes.rs.

Closes #35
rosa force-pushed refactor/permalinks-module from 1ce5b21471
Some checks failed
ci/woodpecker/push/test Pipeline was canceled
ci/woodpecker/pr/test Pipeline was canceled
to 5d8f3fa323
All checks were successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
2026-07-03 00:29:16 +00:00
Compare
rosa merged commit 9a3ebffb76 into main 2026-07-03 00:41:31 +00:00
rosa deleted branch refactor/permalinks-module 2026-07-03 00:41:31 +00:00
Sign in to join this conversation.
No reviewers
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!42
No description provided.