{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
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]
, cradleType :: CradleType
}
deriving (Show, Eq)
data CradleType
= Cabal { component :: Maybe String }
| CabalMulti [ (FilePath, String) ]
| Stack { component :: Maybe String }
| StackMulti [ (FilePath, String) ]
| Bios
{ prog :: FilePath
, depsProg :: Maybe FilePath
}
| 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 "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)
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
readConfig :: FilePath -> IO Config
readConfig = decodeFileThrow