lambdabot-4.3: src/Lambdabot/File.hs
module Lambdabot.File
( findLBFile
, findOrCreateLBFile
, outputDir
) where
import Lambdabot.Config.Core
import Lambdabot.Monad
import Lambdabot.Util
import Control.Applicative
import Control.Monad
import Paths_lambdabot
import System.Directory
import System.FilePath
-- | Constants.
lambdabot :: FilePath
lambdabot = ".lambdabot"
stateDir :: LB FilePath
stateDir = (lambdabot </>) <$> getConfig outputDir
maybeFileExists :: FilePath -> IO (Maybe FilePath)
maybeFileExists path = do
b <- doesFileExist path
return $! if b then Just path else Nothing
-- | For a given file, look locally under State/. That is, suppose one is
-- running out of a Lambdabot darcs repository in /home/cale/lambdabot. Then
--
-- > lookLocally "fact" ~> "/home/cale/lambdabot/State/fact"
lookLocally :: FilePath -> LB (Maybe String)
lookLocally file = do
local <- getConfig outputDir
io $ maybeFileExists (local </> file)
-- | For a given file, look at the home directory. By default, we stash files in
-- ~/.lambdabot. So, running Lambdabot normally would let us do:
--
-- > lookHome "fact" ~> "/home/cale/lambdabot/State/fact"
--
-- (Note that for convenience we preserve the "State/foo" address pattern.)
lookHome :: FilePath -> LB (Maybe String)
lookHome f = do
home <- io getHomeDirectory
state <- stateDir
io (maybeFileExists $ home </> state </> f)
-- | Do ~/.lambdabot & ~/.lambdabot/State exist?
isHome :: LB Bool
isHome = do
home <- io getHomeDirectory
state <- stateDir
io . fmap and . mapM (doesDirectoryExist . (home </>)) $ [lambdabot, state]
-- | Create ~/.lambdabot and ~/.lambdabot/State
mkdirL :: LB ()
mkdirL = do
home <- io getHomeDirectory
state <- stateDir
io . mapM_ (createDirectory . (home </>)) $ [lambdabot, state]
-- | Ask Cabal for the read-only copy of a file, and copy it into ~/.lambdabot/State.
-- if there isn't a read-only copy, create an empty file.
cpDataToHome :: FilePath -> LB ()
cpDataToHome f = do
local <- getConfig outputDir
state <- stateDir
rofile <- io (getDataFileName (local </> f))
home <- io getHomeDirectory
-- cp /.../lambdabot-4.foo/State/foo ~/.lambdabot/State/foo
let outFile = home </> state </> f
exists <- io (doesFileExist rofile)
if exists
then io (copyFile rofile outFile)
else io (writeFile outFile "")
-- | Try to find a pre-existing file, searching first in ./State and then in
-- ~/.lambdabot/State
findLBFile :: FilePath -> LB (Maybe String)
findLBFile f = do
first <- lookLocally f
case first of
-- With any luck we can exit quickly
Just a -> return (Just a)
-- OK, we didn't get lucky with local, so
-- hopefully it's in ~/.lambdabot
Nothing -> lookHome f
-- | Complicated. If a file exists locally, we return that. If a file exists in
-- ~/lambdabot/State, we return that. If neither the file nor ~/lambdabot/State
-- exist, we create the directories and then copy the file into it if a template
-- exists, or create an empty file if it does not.
-- Note that the return type is simple so we can just do a binding and stuff it
-- into the conventional functions easily; unfortunately, this removes
-- error-checking, as an error is now just \"\".
findOrCreateLBFile :: FilePath -> LB String
findOrCreateLBFile f = do
mbFile <- findLBFile f
case mbFile of
Just file -> return file
-- Uh oh. We didn't find it locally, nor did we
-- find it in ~/.lambdabot/State. So now we
-- need to make ~/.lambdabot/State and copy it in.
Nothing -> do
exists <- isHome
when (not exists) mkdirL
cpDataToHome f
-- With the file copied/created,
-- a second attempt should work.
g <- lookHome f
case g of
Just a -> return a
Nothing -> do
home <- io getHomeDirectory
state <- stateDir
fail $ "findOrCreateLBFile: couldn't find file "
++ f ++ " in " ++ home </> state