Files
net-deertopia/publisher/src/net/deertopia/publisher/slug.clj
Madeleine Sydney Ślaga d4ff27ada4
All checks were successful
build / build (push) Successful in 5s
feat: very primitive publisher
2026-03-21 20:37:16 -06:00

65 lines
1.8 KiB
Clojure

(ns net.deertopia.publisher.slug
(:require [clojure.spec.alpha :as s]
[spec-tools.core :as st])
(:import (java.nio ByteBuffer)
(java.util Base64 UUID)))
(defrecord Slug [slug-string]
Object
(toString [this]
(:slug-string this)))
(defn from-string [s]
(try (let [decoder (Base64/getUrlDecoder)]
(when (= 16 (count (.decode decoder s)))
(Slug. s)))
;; really stupid
(catch IllegalArgumentException _
nil)))
(defn to-string [s]
(str s))
(defn- coerce-to-uuid [string-or-uuid]
(cond (string? string-or-uuid) (UUID/fromString string-or-uuid)
(uuid? string-or-uuid) string-or-uuid))
(defn- uuid->bytes [string-or-uuid]
(let [uuid (coerce-to-uuid string-or-uuid)]
(.array (doto (ByteBuffer/wrap (byte-array 16))
(.putLong (.getMostSignificantBits uuid))
(.putLong (.getLeastSignificantBits uuid))))))
(defn- bytes->uuid [bytes]
(when (= (count bytes) 16)
(let [bb (ByteBuffer/wrap bytes)
high (.getLong bb)
low (.getLong bb)]
(UUID. high low))))
(defn from-uuid [string-or-uuid]
(let [uuid (coerce-to-uuid string-or-uuid)
encoder (.withoutPadding (Base64/getUrlEncoder))]
(Slug. (.encodeToString encoder (uuid->bytes uuid)))))
(defn to-uuid [slug]
(let [decoder (Base64/getUrlDecoder)]
(bytes->uuid (.decode decoder (str slug)))))
(comment
(let [uuid #uuid "f9eab66e-7773-4b87-b854-0bfc8f563809"
slug (from-uuid uuid)
round-tripped (to-uuid slug)]
{:uuid uuid, :slug slug, :round-tripped round-tripped}))
(defn make-slug [string]
(assert (try (to-uuid string)
(catch Throwable _
nil))
"invalid slug")
(->Slug string))
(s/def ::slug
(s/conformer #(or (some-> % from-string)
::s/invalid)))