-- | 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 = ".lambdabot"
-- | Locate state directory. Returns the local directory if it exists,
-- and the home directory otherwise.
stateDir :: LB FilePath
stateDir = do
-- look locally
output <- getConfig outputDir
b <- io $ doesDirectoryExist output
if b then return output else homeDir
homeDir :: LB FilePath
homeDir = do
output <- getConfig 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 f = do
state <- 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 f = do
state <- 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 [] = return Nothing
findFirstFile (path:ps) = do
b <- io $ doesFileExist 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 f = do
state <- 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 f = do
outFile <- findLBFileForWriting 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
Nothing -> io $ writeFile outFile ""
Just roFile -> io $ copyFile roFile outFile
return outFile