hie-bios-0.3.0: src/HIE/Bios/Config.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
-- | Logic and datatypes for parsing @hie.yaml@ files.
module HIE.Bios.Config(
readConfig,
Config(..),
CradleConfig(..),
CradleType(..)
) where
import qualified Data.Text as T
import qualified Data.Vector as V
import qualified Data.HashMap.Strict as Map
import Data.Foldable (foldrM)
import Data.Yaml
data CradleConfig =
CradleConfig
{ cradleDependencies :: [FilePath]
-- ^ Dependencies of a cradle.
-- Dependencies are expected to be relative to the root directory.
-- The given files are not required to exist.
, cradleType :: CradleType
-- ^ Type of the cradle to use. Actions to obtain
-- compiler flags from are dependant on this field.
}
deriving (Show, Eq)
data CradleType
= Cabal { component :: Maybe String }
| CabalMulti [ (FilePath, String) ]
| Stack { component :: Maybe String }
| StackMulti [ (FilePath, String) ]
-- Bazel and Obelisk used to be supported but bit-rotted and no users have complained.
-- They can be added back if a user
-- | Bazel
-- | Obelisk
| Bios
{ prog :: FilePath
-- ^ Path to program that retrieves options to compile a file
, depsProg :: Maybe FilePath
-- ^ Optional Path to program to obtain cradle dependencies.
-- Each cradle dependency is to be expected to be on a separate line
-- and relative to the root dir of the cradle, not relative
-- to the location of this program.
}
| Direct { arguments :: [String] }
| None
| Multi [ (FilePath, CradleConfig) ]
deriving (Show, Eq)
instance FromJSON CradleType where
parseJSON (Object o) = parseCradleType o
parseJSON _ = fail "Not a known cradle type. Possible are: cabal, stack, bios, direct, default, none, multi"
parseCradleType :: Object -> Parser CradleType
parseCradleType o
| Just val <- Map.lookup "cabal" o = parseCabal val
| Just val <- Map.lookup "stack" o = parseStack val
-- | Just _val <- Map.lookup "bazel" o = return Bazel
-- | Just _val <- Map.lookup "obelisk" o = return Obelisk
| Just val <- Map.lookup "bios" o = parseBios val
| Just val <- Map.lookup "direct" o = parseDirect val
| Just _val <- Map.lookup "none" o = return None
| Just val <- Map.lookup "multi" o = parseMulti val
parseCradleType o = fail $ "Unknown cradle type: " ++ show o
parseStackOrCabal
:: (Maybe String -> CradleType)
-> ([(FilePath, String)] -> CradleType)
-> Value
-> Parser CradleType
parseStackOrCabal singleConstructor _ (Object x)
| Map.size x == 1, Just (String stackComponent) <- Map.lookup "component" x
= return $ singleConstructor $ Just $ T.unpack stackComponent
| Map.null x
= return $ singleConstructor Nothing
| otherwise
= fail "Not a valid Configuration type, following keys are allowed: component"
parseStackOrCabal _ multiConstructor (Array x) = do
let parseOne e
| Object v <- e
, Just (String prefix) <- Map.lookup "path" v
, Just (String comp) <- Map.lookup "component" v
, Map.size v == 2
= return (T.unpack prefix, T.unpack comp)
| otherwise
= fail "Expected an object with path and component keys"
xs <- foldrM (\v cs -> (: cs) <$> parseOne v) [] x
return $ multiConstructor xs
parseStackOrCabal singleConstructor _ Null = return $ singleConstructor Nothing
parseStackOrCabal _ _ _ = fail "Configuration is expected to be an object."
parseStack :: Value -> Parser CradleType
parseStack = parseStackOrCabal Stack StackMulti
parseCabal :: Value -> Parser CradleType
parseCabal = parseStackOrCabal Cabal CabalMulti
parseBios :: Value -> Parser CradleType
parseBios (Object x)
| 2 == Map.size x
, Just (String biosProgram) <- Map.lookup "program" x
, Just (String biosDepsProgram) <- Map.lookup "dependency-program" x
= return $ Bios (T.unpack biosProgram) (Just (T.unpack biosDepsProgram))
| 1 == Map.size x
, Just (String biosProgram) <- Map.lookup "program" x
= return $ Bios (T.unpack biosProgram) Nothing
| otherwise
= fail "Not a valid Bios Configuration type, following keys are allowed: program, dependency-program"
parseBios _ = fail "Bios Configuration is expected to be an object."
parseDirect :: Value -> Parser CradleType
parseDirect (Object x)
| Map.size x == 1
, Just (Array v) <- Map.lookup "arguments" x
= return $ Direct [T.unpack s | String s <- V.toList v]
| otherwise
= fail "Not a valid Direct Configuration type, following keys are allowed: arguments"
parseDirect _ = fail "Direct Configuration is expected to be an object."
parseMulti :: Value -> Parser CradleType
parseMulti (Array x)
= Multi <$> mapM parsePath (V.toList x)
parseMulti _ = fail "Multi Configuration is expected to be an array."
parsePath :: Value -> Parser (FilePath, CradleConfig)
parsePath (Object v)
| Just (String path) <- Map.lookup "path" v
, Just c <- Map.lookup "config" v
= (T.unpack path,) <$> parseJSON c
parsePath o = fail ("Multi component is expected to be an object." ++ show o)
-- | Configuration that can be used to load a 'Cradle'.
-- A configuration has roughly the following form:
--
-- @
-- cradle:
-- cabal:
-- component: "lib:hie-bios"
-- @
newtype Config = Config { cradle :: CradleConfig }
deriving (Show, Eq)
instance FromJSON CradleConfig where
parseJSON (Object val) = do
crd <- val .: "cradle"
crdDeps <- case Map.size val of
1 -> return []
2 -> val .: "dependencies"
_ -> fail "Unknown key, following keys are allowed: cradle, dependencies"
return $ CradleConfig { cradleType = crd
, cradleDependencies = crdDeps
}
parseJSON _ = fail "Expected a cradle: key containing the preferences, possible values: cradle, dependencies"
instance FromJSON Config where
parseJSON o = Config <$> parseJSON o
-- | Decode given file to a 'Config' value.
-- If the contents of the file is not a valid 'Config',
-- an 'Control.Exception.IOException' is thrown.
readConfig :: FilePath -> IO Config
readConfig = decodeFileThrow