{-# 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