slug

package
v0.2.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 1, 2026 License: MIT Imports: 2 Imported by: 0

Documentation

Overview

Package slug derives URL-safe plan slugs from human titles. The derivation is the single source of truth shared by the v5 migration backfill in internal/db and the runtime CreatePlan path in internal/store — keeping the algorithm in one place means a v5 DB migrated from v4 and a v5 DB created fresh agree on the slug for any given title.

Algorithm (Derive):

  1. Lowercase the input.
  2. Map every Unicode codepoint that is not ASCII letter/digit to '-' (so em-dashes, fancy punctuation, and non-ASCII letters all become a separator). We do not transliterate — "café" becomes "caf" rather than "cafe". The goal is a deterministic ASCII slug, not faithful Unicode preservation.
  3. Collapse runs of '-' and trim leading/trailing '-'.
  4. If the result is empty (title was punctuation-only), fall back to the literal "plan".
  5. If the result is all digits (e.g. "42"), append "-plan" so the slug never looks like a numeric id at a routing layer that might parse it as one.
  6. Cap the result at slugMaxLen runes. If truncation lands inside a word, prefer to cut at the last '-' within the cap so the slug stays composed of whole pieces. Strip any trailing '-' left over.

DeriveUnique wraps Derive with a deterministic "-N" dedup loop, where the existence check is injected so the same helper drives both the migration backfill (SELECT against the in-flight tx) and runtime inserts (SELECT against the live DB).

Index

Constants

This section is empty.

Variables

View Source
var ErrTooManyCollisions = fmt.Errorf("slug: more than %d collisions", maxCollisions)

ErrTooManyCollisions surfaces when DeriveUnique exhausts the dedup budget. Callers should wrap it with store.ErrInvalid (or its own equivalent) so the CLI envelope reports code:"invalid".

Functions

func Derive

func Derive(title string) string

Derive is the pure title→slug transform. Deterministic, no I/O. Callers that need uniqueness across a table use DeriveUnique.

func DeriveUnique

func DeriveUnique(title string, exists ExistsFunc) (string, error)

DeriveUnique computes the base slug from title, then walks the dedup sequence base, base-2, base-3, …, base-maxCollisions, asking exists at each step. Returns the first slug for which exists is false. Wraps ErrTooManyCollisions when every candidate is taken so the CLI/migration can surface a code:"invalid" envelope rather than silently corrupting the unique index.

Length contract: the dedup suffix can extend the slug beyond slugMaxLen by up to len("-100") = 4 bytes. We choose to honor uniqueness over the cap because the cap is cosmetic and uniqueness is correctness — the unique index would reject anything else.

Types

type ExistsFunc

type ExistsFunc func(slug string) (bool, error)

ExistsFunc returns (true, nil) when slug is already taken, (false, nil) when free, or (_, err) on lookup failure. Injected so the same dedup loop drives the migration tx and the runtime store.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL