phino-0.0.84: src/Random.hs
-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com
-- SPDX-License-Identifier: MIT
module Random (randomString) where
import Control.Monad (replicateM)
import Data.Char (intToDigit)
import Data.IORef (IORef, modifyIORef', newIORef, readIORef)
import Data.Set (Set)
import qualified Data.Set as Set
import GHC.IO (unsafePerformIO)
import System.Random (randomRIO)
strings :: IORef (Set String)
{-# NOINLINE strings #-}
strings = unsafePerformIO (newIORef Set.empty)
generate :: String -> IO String
generate [] = pure []
generate ('%' : ch : rest) = do
rep <- case ch of
'x' -> replicateM 8 $ do
v <- randomRIO (0, 15)
pure (intToDigit v)
'd' -> show <$> randomRIO (0 :: Int, 9999)
_ -> pure ['%', ch]
next <- generate rest
pure (rep ++ next)
generate (ch : rest) = do
rest' <- generate rest
pure (ch : rest')
regenerate :: String -> Set String -> IO String
regenerate pat set = do
next <- generate pat
if next `Set.member` set
then regenerate pat set
else do
modifyIORef' strings (Set.insert next)
pure next
randomString :: String -> IO String
randomString pat
| randomized pat = readIORef strings >>= regenerate pat
| otherwise = generate pat
where
randomized :: String -> Bool
randomized [] = False
randomized ('%' : ch : rest) = ch == 'd' || ch == 'x' || randomized rest
randomized (_ : rest) = randomized rest