packages feed

randfile-0.1.0.0: Main.hs

import Prelude hiding (catch)

import Stat

import Control.Applicative
import Control.Exception
import Control.Monad.CryptoRandom
import Control.Monad.Trans.Class
import Control.Monad.Trans.Error
import Crypto.Random
import System.Directory
import System.Environment
import System.Exit
import System.FilePath
import System.IO

-- | Handle an IOException by returning the given value instead.
onIOException :: a -> IO a -> IO a
onIOException fallback_result action =
        action `catch` handler fallback_result
    where
        handler :: a -> IOException -> IO a
        handler x _ = return x

notSpecial :: FilePath -> Bool
notSpecial name = name /= "." && name /= ".."

listFiles :: FilePath -> IO [FilePath]
listFiles dir = onIOException []
              $ map (dir </>) . filter notSpecial <$> getDirectoryContents dir

randFile :: (CryptoRandomGen g, Error e, ContainsGenError e)
         => FilePath
         -> CRandT g e IO (Maybe FilePath)
randFile dir = lift (listFiles dir) >>= pickFromList where
    pickFromList list =
        if null list
            then return Nothing
            else do
                let len = length list
                idx  <- getCRandomR (0, len-1)
                let path = list !! idx

                st <- lift (stat path)
                case st of
                    File      -> return (Just path)
                    Directory -> do
                        r <- randFile path
                        case r of
                            Just _  -> return r
                            Nothing -> pickFromList (list `without` idx)
                    Other     -> pickFromList (list `without` idx)
    without list idx = as ++ bs where
        (as, _:bs) = splitAt idx list

-- | Like 'runCRandT', but discard the generator and throw the error
-- (if necessary).
runCRandT_ :: CRandT g GenError IO a -> g -> IO a
runCRandT_ action gen = do
    res <- runCRandT action gen
    case res of
        Left e       -> throwIO $ userError $ show e
        Right (a, _) -> return a

main :: IO ()
main = do
    g <- newGenIO :: IO SystemRandom
    file <- runCRandT_ (randFile ".") g
    case file of
        Just f  -> putStrLn $ makeRelative "." $ f
        Nothing -> do
            progName <- getProgName
            hPutStrLn stderr $
                progName ++ ": Current directory does not contain any files"
            exitFailure