packages feed

parochial-0.2.0.0: src/Parochial/Options.hs

{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE DeriveGeneric         #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE StandaloneDeriving    #-}
{-# LANGUAGE TypeOperators         #-}


module Parochial.Options (
    Config(..)
  , getAndMkTargetPath
  , getState
  , parseCmdOptions
  ) where

import           Protolude hiding (state)

import qualified Data.Text as T

import           Distribution.Simple.Flag
import           Distribution.Simple.Utils hiding (findFile)
import           Distribution.Simple.Configure

import           System.Directory
import           System.FilePath

import           System.FilePattern.Directory

import           Options.Generic

import           Parochial.Types


data Config w
     = Haddock
       { target    :: w ::: Maybe FilePath  <?> "Target directory"
       , project   :: w ::: Maybe Text      <?> "The name of the project. Default to project name derived from $CWD"
       , state     :: w ::: Maybe FilePath  <?> "The state file"
       }
     | Hoogle
       { target    :: w ::: Maybe FilePath  <?> "Target directory"
       , project   :: w ::: Maybe Text      <?> "The name of the project. Default to project name derived from $CWD"
       , state     :: w ::: Maybe FilePath  <?> "The state file"
       }
     deriving (Generic)


instance ParseRecord (Config Wrapped)
deriving instance Show (Config Unwrapped)


setupConfigFile :: FilePath
setupConfigFile = "setup-config"


defaultDistDir :: FilePath
defaultDistDir = "dist-newstyle"


getCurrentProject :: Maybe Text -> IO Text
getCurrentProject = maybe (unhide . toS . takeFileName <$> getCurrentDirectory) pure


unhide :: Text -> Text
unhide = T.dropWhile ('.' ==)


targetBaseName :: FilePath
targetBaseName = "parochial"


defaultPath :: Target
defaultPath = "/srv" </> targetBaseName


-- | What the target directory should be. This checks the --target option and if set uses
--   is otherwise it uses @bestTarget@ to work out what to do.
defaultTarget :: Maybe FilePath -> IO Target
defaultTarget = maybe bestTarget pure


getAndMkTargetPath :: Maybe Text -> Maybe FilePath -> IO Target
getAndMkTargetPath p t = getTarget >>= mkTargetPath
  where
    getTarget = (</>) <$> defaultTarget t <*> (toS <$> getCurrentProject p)
    mkTargetPath p' = createDirectoryIfMissing True p' >> pure p'


-- | Take the value from either the --state option or find the setup-config itself.
getState :: Maybe FilePath -> IO FilePath
getState = maybe findSetupConfig pure


-- | Try and find the setup-config file. This is *very* primative at the moment and
--   will simply search for the first path returned by **/x/**/setup-config
findSetupConfig :: IO FilePath
findSetupConfig = do
  d <- dist
  findS d >>= maybe (dieNoVerbosity ("Can't find: " <> setupConfigFile)) (pure . (d </>))

  where
    dist :: IO FilePath
    dist = findDistPref "." (Flag defaultDistDir)

    findS :: FilePath -> IO (Maybe FilePath)
    findS d = head <$> getDirectoryFiles d ["**/x/**" </> setupConfigFile]


parseCmdOptions :: MonadIO m => m (Config Unwrapped)
parseCmdOptions = unwrapRecord "Generate project specific haddocks"


-- | Check if defaultTarget exists and if not check that the parent directory is writable.
--   If the parent directory is writable then create it otherwise use $HOME/.parochial.
--   I'm not sure if this is the best place but I'm not sure where to put it! $HOME/.local/srv
--   would be best but this isn't covered by any standard I know off and therefore really
--   confusing to everyone else. <general heavy sigh>
bestTarget :: IO Target
bestTarget = doesDefaultTargetExist >>= boolWithDef (isParentWritable >>=  boolWithDef homeTarget)
  where
    doesDefaultTargetExist = doesDirectoryExist defaultPath
    isParentWritable = writable <$> getPermissions (takeDirectory defaultPath)


-- | If the second argument is True then return the @defaultPath@ otherwise evaluate the
--   second argument.
boolWithDef :: Applicative f => f Target -> Bool -> f Target
boolWithDef = flip bool (pure defaultPath)


homeTarget :: IO Target
homeTarget = getXdgDirectory XdgData targetBaseName