glugify/slugger

A stateful slugger that guarantees unique slugs across a batch of inputs — the building block for tables of contents, static site generators and CMS imports, where two posts titled “Hello” must not share a URL.

The state is an immutable value threaded through each call, so it works naturally in folds and works identically on the Erlang and JavaScript targets:

import glugify/slugger

let s = slugger.new()
let #(s, a) = slugger.slug(s, "Hello World")
let #(s, b) = slugger.slug(s, "Hello World")
let #(_, c) = slugger.slug(s, "Hello World")
// a -> "hello-world"
// b -> "hello-world-1"
// c -> "hello-world-2"

Types

Tracks which slugs have been handed out. Create one with new and thread it through slug or slug_with calls.

pub opaque type Slugger

Values

pub fn new() -> Slugger

Creates a fresh slugger with no slugs taken.

pub fn slug(slugger: Slugger, text: String) -> #(Slugger, String)

Slugifies text with the default configuration and makes the result unique against everything this slugger has produced before, by appending -1, -2, … to duplicates.

Unlike github-slugger, a suffixed slug never collides with a slug produced from genuinely suffixed input: "foo", "foo", "foo-1" yields "foo", "foo-1", "foo-1-1".

Empty results (e.g. from symbol-only input) bypass uniqueness and are returned as-is.

Examples

let #(slugger, first) = slug(new(), "My Post")
let #(_, second) = slug(slugger, "My Post")
// first -> "my-post"
// second -> "my-post-1"
pub fn slug_with(
  slugger: Slugger,
  text: String,
  config: config.Config,
) -> Result(#(Slugger, String), errors.SlugifyError)

Like slug, but slugifies with a custom configuration.

Examples

import glugify/config

let cfg = config.default() |> config.with_separator("_")
slug_with(new(), "My Post", cfg)
// -> Ok(#(slugger, "my_post"))
Search Document