packages feed

haskell-awk-1.2.0.1: src/System/Console/Hawk/Context/Dir.hs

{-# LANGUAGE CPP, PackageImports #-}
-- | About the directory in which the context is persited.
module System.Console.Hawk.Context.Dir
  ( createDefaultContextDir
  , findContextFromCurrDirOrDefault
  , checkContextDir
  ) where

import Prelude hiding (fail)

import Control.Monad hiding (fail)
#if MIN_VERSION_base(4,12,0)
import Control.Monad.Fail (fail)
#else
import Prelude (fail)
#endif
import "mtl" Control.Monad.Trans
import System.Directory
import System.FilePath

import Control.Monad.Trans.Uncertain
import System.Console.Hawk.Context.Paths
import System.Console.Hawk.UserPrelude.Defaults
import System.Directory.Extra


-- | Create a default context
createDefaultContextDir :: FilePath -> UncertainT IO ()
createDefaultContextDir contextDir = do
    _ <- checkContextDir contextDir
    liftIO $ do
      createDirectoryIfMissing True contextDir
      preludeExists <- doesFileExist preludeFile
      unless preludeExists $ writeFile preludeFile defaultPrelude
  where
    paths = mkContextPaths contextDir
    preludeFile = originalPreludePath paths

-- | Find a project context
findContext :: FilePath -> IO (Maybe FilePath)
findContext startDir =
    foldM (maybe validDirOrNothing (const . return . Just)) Nothing possibleContextDirs
  where
    mkHawkPath = (</> ".hawk")
    possibleContextDirs = map mkHawkPath (ancestors startDir)

    validDirOrNothing dir = do
      dirExists <- doesDirectoryExist dir
      if dirExists
       then do
         permissions <- getPermissions dir
         if writable permissions && searchable permissions
           then do
             prelude <- findFile [dir] "prelude.hs"
             case prelude of
               Nothing -> return Nothing
               Just f -> do
                 preludePermissions <- getPermissions f
                 if readable preludePermissions
                   then return $ Just dir
                   else return Nothing
           else return Nothing
       else return Nothing

-- | Find a project context starting from the current working directory
findContextFromCurrDir :: IO (Maybe FilePath)
findContextFromCurrDir = getCurrentDirectory >>= findContext

-- | Find a project context or return the default
findContextFromCurrDirOrDefault :: IO FilePath
findContextFromCurrDirOrDefault = do
    maybeProjectContextDir <- findContextFromCurrDir
    case maybeProjectContextDir of
      Nothing -> getDefaultContextDir
      Just projectContextDir -> return projectContextDir

-- | Check if a directory is a valid context and return true if the directory
-- doesn't exist and the parent has the right permissions
checkContextDir :: FilePath -> UncertainT IO Bool
checkContextDir dir = do
    fileExists <- liftIO $ doesFileExist dir
    when fileExists $ fail $ concat [
       "context directory '",dir,"' cannot be"
      ,"created because a file with the same"
      ,"name exists"]
    dirExists <- liftIO $ doesDirectoryExist dir
    if dirExists
      then do
        permissions <- liftIO $ getPermissions dir
        unless (writable permissions) $ fail $ concat [
           "cannot use '",dir,"' as context directory because it is not "
          ,"writable"]
        unless (searchable permissions) $ fail $ concat [
           "cannot use '",dir,"' as context directory because it is not "
          ,"searchable"]
        return False
      else do
        -- if the directory doesn't exist then its parent must be writable
        -- and searchable
        let parent = case takeDirectory dir of {"" -> ".";p -> p}
        permissions <- liftIO $ getPermissions parent
        unless (writable permissions) $ fail $ concat[
           "cannot create context directory '",dir,"' because the parent "
          ," directory is not writable (",show permissions,")"]
        unless (searchable permissions) $ fail $ concat[
           "cannot create context directory '",dir,"' because the parent "
          ," directory is not searchable (",show permissions,")"]
        warn $ concat ["directory '",dir,"' doesn't exist, creating a "
                      ,"default one"]
        return True

getDefaultContextDir :: IO FilePath
getDefaultContextDir = do
    home <- getHomeDirectory
    return $ home </> ".hawk"