UUID in Purescript
UUID in Purescript
I ran into a situation the other day where UUID was needed. Sadly, the UUID module in pursuit depends on an npm package. So, I rolled my own.
I’d be happy to hear of any holes in my implementation and any other critiques anyone has to offer.
Here’s the code:
module UUID where import Prelude import Data.Either (Either(..)) import Data.Int.Bits ((.|.)) import Data.Maybe (Maybe(..)) import Data.String (joinWith, length) import Data.String.Regex (regex, test) import Data.String.Regex.Flags (noFlags) import Effect (Effect) import Data.Int (floor, hexadecimal, toNumber, toStringAs) import Effect.Random (random) import Data.Array (replicate) newtype UUID = UUID String instance showUUID :: Show UUID where show (UUID uuid) = uuid derive instance eqUUID :: Eq UUID derive instance ordUUID :: Ord UUID randomInt :: Int -> Int -> Effect Int randomInt min max = do r <- random pure $ floor $ r * toNumber (max - min + 1) + toNumber min padStart :: Int -> String -> String padStart targetLength str = let paddingLength = max 0 (targetLength - length str) padding = replicate paddingLength "0" in joinWith "" padding <> str parseUUID :: String -> Maybe UUID parseUUID str = case regex "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" noFlags of Left _ -> Nothing Right r -> if test r str then Just $ UUID str else Nothing uuidToString :: UUID -> String uuidToString (UUID uuid) = uuid emptyUUID :: UUID emptyUUID = UUID "00000000-0000-0000-0000-000000000000" -- | Generate a UUID v4 genUUID :: Effect UUID genUUID = do -- Generate random 16-bit integers for smaller chunks r1 <- randomInt 0 0xFFFF -- First half of time_low r2 <- randomInt 0 0xFFFF -- Second half of time_low r3 <- randomInt 0 0xFFFF -- time_mid r4 <- randomInt 0 0x0FFF -- time_hi (12 bits for randomness) r5 <- randomInt 0 0x3FFF -- clock_seq (14 bits for randomness) r6 <- randomInt 0 0xFFFF -- First part of node r7 <- randomInt 0 0xFFFF -- Second part of node r8 <- randomInt 0 0xFFFF -- Third part of node -- Set the version (4) and variant (10) let versioned = r4 .|. 0x4000 -- Set version to 4 (binary OR with 0100 0000 0000 0000) variant = r5 .|. 0x8000 -- Set variant to 10xx (binary OR with 1000 0000 0000 0000) -- Convert to hex and pad as needed let hex1 = padStart 4 (toHex r1) <> padStart 4 (toHex r2) -- time_low hex2 = padStart 4 (toHex r3) -- time_mid hex3 = padStart 4 (toHex versioned) -- time_hi_and_version hex4 = padStart 4 (toHex variant) -- clock_seq hex5 = padStart 4 (toHex r6) <> padStart 4 (toHex r7) <> padStart 4 (toHex r8) -- node uuid = joinWith "-" [hex1, hex2, hex3, hex4, hex5] pure $ UUID uuid where toHex = toStringAs hexadecimal