packages feed

dhrun-1.0.1: src/Dhrun/Types/Yaml.hs

{-# language DeriveAnyClass #-}
{-# language FlexibleInstances #-}
{-# language FlexibleContexts #-}
{-# language DeriveGeneric #-}

{-|
Module      : Dhrun.AesonTypes
Description : types
Copyright   : (c) Valentin Reis, 2018
License     : MIT
Maintainer  : fre@freux.fr
-}

module Dhrun.Types.Yaml
  ( decodeCfgFile
  , decodeCfg
  , encodeCfg
  , encodeCmd
  )
where

import qualified Dhrun.Types.Dhall                   as DT
import           Protolude
import           Dhall
import           Data.Yaml
import           Data.Aeson
import           System.IO.Error

data CheckParse = CheckParse {
    avoids   :: Maybe [Text]
  , wants    :: Maybe [Text]
  } deriving (Eq, Show, Generic, ToJSON, Interpret)
instance FromJSON CheckParse where
    parseJSON = genericParseJSON defaultOptions { omitNothingFields  = True }
instance FromJSON (DT.FileCheck CheckParse) where
    parseJSON = genericParseJSON defaultOptions { omitNothingFields  = True }

data Cfg = Cfg
  { cmds      :: [Cmd],
    workdir   :: Maybe Text,
    verbose   :: Maybe Bool,
    cleaning  :: Maybe Bool,
    pre       :: Maybe [Text],
    post      :: Maybe [Text]
  } deriving (Eq, Show, Generic, FromJSON, ToJSON)

data Cmd = Cmd {
    name        :: Text
  , exitcode    :: Maybe Integer
  , args        :: Maybe [Text]
  , vars        :: Maybe [DT.EnvVar]
  , passvars    :: Maybe [Text]
  , timeout     :: Maybe Natural
  , out         :: DT.FileCheck CheckParse
  , err         :: DT.FileCheck CheckParse
  , postchecks  :: Maybe [DT.FileCheck CheckParse]
  , otherwd     :: Maybe Text
  } deriving (Eq, Show, Generic, ToJSON, Interpret)
instance FromJSON Cmd where
    parseJSON = genericParseJSON defaultOptions { omitNothingFields  = True }

fromInternalCheck :: DT.Check -> CheckParse
fromInternalCheck c = CheckParse {..}
 where
  avoids = case DT.avoids c of
    [] -> Nothing
    l  -> Just l
  wants = case DT.wants c of
    [] -> Nothing
    l  -> Just l

toInternalCheck :: CheckParse -> DT.Check
toInternalCheck cp =
  DT.Check {wants = fromMaybe [] (wants cp), avoids = fromMaybe [] (avoids cp)}

toInternalCmd :: Cmd -> DT.Cmd
toInternalCmd c = DT.Cmd
  { name       = name c
  , exitcode   = exitcode c
  , args       = fromMaybe [] $ args c
  , vars       = fromMaybe [] $ vars c
  , passvars   = fromMaybe [] $ passvars c
  , out        = toInternalCheck <$> out c
  , err        = toInternalCheck <$> err c
  , postchecks = case postchecks c of
    (Just l) -> (toInternalCheck <$>) <$> l
    Nothing  -> []
  , timeout    = timeout c
  , otherwd = otherwd c
  }

toInternal :: Cfg -> DT.Cfg
toInternal d = DT.Cfg
  { cmds     = toInternalCmd <$> cmds d
  , verbose  = fromMaybe False (verbose d)
  , cleaning = fromMaybe False (cleaning d)
  , pre      = fromMaybe [] (pre d)
  , post     = fromMaybe [] (post d)
  , workdir  = fromMaybe "./" (workdir d)
  }

fromInternalCmd :: DT.Cmd -> Cmd
fromInternalCmd c = Cmd {..}
 where
  name    = DT.name c
  exitcode = DT.exitcode c
  out     = fromInternalCheck <$> DT.out c
  err     = fromInternalCheck <$> DT.err c
  timeout = DT.timeout c
  otherwd   = DT.otherwd c
  args    = case DT.args c of
    [] -> Nothing
    l  -> Just l
  vars = case DT.vars c of
    [] -> Nothing
    l  -> Just l
  passvars = case DT.passvars c of
    [] -> Nothing
    l  -> Just l
  postchecks = case DT.postchecks c of
    [] -> Nothing
    l  -> Just ((fromInternalCheck <$>) <$> l)

fromInternal :: DT.Cfg -> Cfg
fromInternal d = Cfg {..}
 where
  workdir = case DT.workdir d of
    "./" -> Nothing
    w    -> Just w
  cmds     = fromInternalCmd <$> DT.cmds d
  verbose  = if DT.verbose d then Just True else Nothing
  cleaning = if DT.cleaning d then Just True else Nothing
  pre      = case DT.pre d of
    [] -> Nothing
    l  -> Just l
  post = case DT.post d of
    [] -> Nothing
    l  -> Just l

decodeCfgFile :: (MonadIO m) => Text -> m DT.Cfg
decodeCfgFile fn = liftIO $ try (decodeFileEither (toS fn)) >>= \case
  Left  e          -> throwError e
  Right (Left  pa) -> throwError $ userError $ "parse fail:" <> show pa
  Right (Right a ) -> return $ toInternal a

decodeCfg :: ByteString -> Either ParseException DT.Cfg
decodeCfg fn = toInternal <$> decodeEither' fn

encodeCfg :: DT.Cfg -> ByteString
encodeCfg = Data.Yaml.encode . fromInternal

encodeCmd :: DT.Cmd -> ByteString
encodeCmd = Data.Yaml.encode . fromInternalCmd