    -- | Manage lambdabot's state files. There are three relevant directories:
    --
    -- * local: @./State/@ (configurable, see `outputDir`)
    -- * home:  @~/.lambdabot/State/@
    -- * data:  relative to the data directory of the @lambdabot@ package.
    --
    -- Files are stored locally if the directory exists; otherwise, in the home
    -- directory. When reading a state file, and the file exists in the data
    -- directory but nowhere else, then it is picked up from the data directory.

    module Lambdabot.File
        ( stateDir
        , findLBFileForReading
        , findLBFileForWriting
        , findOrCreateLBFile
        , findLBFile -- deprecated
        , outputDir
        ) where

    import Lambdabot.Config
    import Lambdabot.Config.Core
    import Lambdabot.Monad
    import Lambdabot.Util

    import Control.Applicative
    import Control.Monad
    import System.Directory
    import System.FilePath

    lambdabot :: FilePath
    lambdabot :: FilePath
    lambdabot = FilePath
    ".lambdabot"

    -- | Locate state directory. Returns the local directory if it exists,
    -- and the home directory otherwise.
    stateDir :: LB FilePath
    stateDir :: LB FilePath
    stateDir = do
        -- look locally
        output <- Config FilePath -> LB FilePath
    forall a. Config a -> LB a
    forall (m :: * -> *) a. MonadConfig m => Config a -> m a
    getConfig Config FilePath
    outputDir
        b <- io $ doesDirectoryExist output
        if b then return output else homeDir

    homeDir :: LB FilePath
    homeDir :: LB FilePath
    homeDir = do
        output <- Config FilePath -> LB FilePath
    forall a. Config a -> LB a
    forall (m :: * -> *) a. MonadConfig m => Config a -> m a
    getConfig Config FilePath
    outputDir
        home <- io getHomeDirectory
        return $ home </> lambdabot </> output

    -- | Look for the file in the local, home, and data directories.
    findLBFileForReading :: FilePath -> LB (Maybe FilePath)
    findLBFileForReading :: FilePath -> LB (Maybe FilePath)
    findLBFileForReading FilePath
    f = do
        state <- LB FilePath
    stateDir
        home  <- homeDir
        output <- getConfig outputDir
        rodir <- getConfig dataDir
        findFirstFile [state </> f, home </> f, rodir </> output </> f]

    -- | Return file name for writing state. The file will reside in the
    -- state directory (`stateDir`), and `findLBFileForWriting` ensures that
    -- the state directory exists.
    findLBFileForWriting :: FilePath -> LB FilePath
    findLBFileForWriting :: FilePath -> LB FilePath
    findLBFileForWriting FilePath
    f = do
        state <- LB FilePath
    stateDir
        -- ensure that the directory exists
        io $ createDirectoryIfMissing True state
        success <- io $ doesDirectoryExist state
        when (not success) $ fail $ concat ["Unable to create directory ", state]
        return $ state </> f

    findFirstFile :: [FilePath] -> LB (Maybe FilePath)
    findFirstFile :: [FilePath] -> LB (Maybe FilePath)
    findFirstFile [] = Maybe FilePath -> LB (Maybe FilePath)
    forall a. a -> LB a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Maybe FilePath
    forall a. Maybe a
    Nothing
    findFirstFile (FilePath
    path:[FilePath]
    ps) = do
        b <- IO Bool -> LB Bool
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO Bool -> LB Bool) -> IO Bool -> LB Bool
    forall a b. (a -> b) -> a -> b
    $ FilePath -> IO Bool
    doesFileExist FilePath
    path
        if b then return (Just path) else findFirstFile ps

    {-# DEPRECATED findLBFile
     "Use `findLBFileForReading` or `findLBFileForWriting` instead" #-}
    -- | Try to find a pre-existing file, searching first in the local or home
    -- directory (but not in the data directory)
    findLBFile :: FilePath -> LB (Maybe String)
    findLBFile :: FilePath -> LB (Maybe FilePath)
    findLBFile FilePath
    f = do
        state <- LB FilePath
    stateDir
        home  <- homeDir
        findFirstFile [state </> f, home </> f]

    -- | This returns the same file name as `findLBFileForWriting`.
    -- If the file does not exist, it is either copied from the data (or home)
    -- directory, if a copy is found there; otherwise, an empty file is
    -- created instead.
    findOrCreateLBFile :: FilePath -> LB String
    findOrCreateLBFile :: FilePath -> LB FilePath
    findOrCreateLBFile FilePath
    f = do
        outFile <- FilePath -> LB FilePath
    findLBFileForWriting FilePath
    f
        b <- io $ doesFileExist outFile
        when (not b) $ do
            -- the file does not exist; populate it from home or data directory
            b <- findLBFileForReading f
            case b of
                Maybe FilePath
    Nothing      -> IO () -> LB ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO () -> LB ()) -> IO () -> LB ()
    forall a b. (a -> b) -> a -> b
    $ FilePath -> FilePath -> IO ()
    writeFile FilePath
    outFile FilePath
    ""
                Just FilePath
    roFile  -> IO () -> LB ()
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    io (IO () -> LB ()) -> IO () -> LB ()
    forall a b. (a -> b) -> a -> b
    $ FilePath -> FilePath -> IO ()
    copyFile FilePath
    roFile FilePath
    outFile
        return outFile