{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Config
( ConfigFile
, readConfig
, readProjectConfig
, Ini.iniValueL
) where
import Control.Monad (forM)
import Data.Ini.Config.Bidir ((.=), (.=?), (&))
import qualified Data.Ini.Config.Bidir as Ini
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Data.Monoid ((<>))
import qualified Distribution.ParseUtils as Cabal
import qualified System.Directory as Sys
import System.FilePath ((</>))
import qualified System.FilePath as Sys
import qualified System.Exit as Sys
import qualified System.Environment.XDG.BaseDir as Sys
import Text.Read (readMaybe)
import Types
import Util
type ConfigFile = Ini.Ini Config
defaultConfig :: IO Config
defaultConfig = do
_configInstallPath <- Sys.getUserDataDir ("hatch" </> "install")
let _configCurrentCompiler = Nothing
return Config { .. }
configSpec :: Ini.IniSpec Config ()
configSpec = do
Ini.section "hatch" $ do
configInstallPath .= Ini.field "path" Ini.string
& Ini.optional
configCurrentCompiler .=? Ini.field "current" versionField
versionField :: Ini.FieldValue Compiler
versionField = Ini.FieldValue { .. }
where
fvParse t
| Just ver <- T.stripPrefix "ghc-" t
, [x,y,z] <- T.splitOn "." ver
, Just x' <- readMaybe (T.unpack x)
, Just y' <- readMaybe (T.unpack y)
, Just z' <- readMaybe (T.unpack z)
= Right (Compiler (x', y', z'))
| otherwise = Left ("Bad GHC version: " ++ show t)
fvEmit = T.pack . compilerString
locateConfig :: FilePath -> IO (Maybe FilePath)
locateConfig filename = do
xdgLocs <- Sys.getAllConfigFiles "hatch" filename
let confLocations = ["./" <> filename] ++
xdgLocs ++
["/etc/hatch/" <> filename]
results <- forM confLocations (\fp -> (,) fp <$> Sys.doesFileExist fp)
case filter snd results of
[] -> return Nothing
((fp, _):_) -> return (Just fp)
readProjectConfig :: IO ([Cabal.Field])
readProjectConfig = Sys.getCurrentDirectory >>= go
where go "/" = return []
go path = do
exists <- Sys.doesFileExist (path </> ".hatch")
if exists
then do
content <- readFile (path </> ".hatch")
case Cabal.readFields content of
Cabal.ParseOk _ rs -> return rs
_ -> return []
else go (Sys.takeDirectory path)
readConfig :: IO (Ini.Ini Config)
readConfig = do
def <- defaultConfig
let ini = Ini.ini def configSpec
confLocation <- locateConfig "config.ini"
print confLocation
case confLocation of
Nothing -> return ini
Just fp -> do
content <- T.readFile fp
case Ini.parseIni content ini of
Left err -> do
printErr err
Sys.die err
Right x -> return x