{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}

-- |
-- Module: BDCS.Utils.Process
-- Copyright: (c) 2018 Red Hat, Inc.
-- License: LGPL
--
-- Maintainer: https://github.com/weldr
-- Stability: alpha
-- Portability: portable
--
-- Functions for running external commands with logging.

module BDCS.Utils.Process(callProcessLogged)
 where

import qualified Control.Exception.Lifted as CEL
import           Control.Monad(unless)
import           Control.Monad.IO.Class(liftIO)
import           Control.Monad.Logger(MonadLoggerIO, logInfoN, logErrorN)
import           Control.Monad.Trans.Control(MonadBaseControl)
import           Data.String.Conversions(cs)
import qualified Data.Text as T
import           GHC.IO.Exception(IOErrorType(..))
import           System.IO.Error(mkIOError)
import           System.Process.Typed(ExitCodeException(..), proc, readProcess_)

callProcessLogged :: (MonadBaseControl IO m, MonadLoggerIO m) => String -> [String] -> m ()
callProcessLogged cmd args =
    doit `CEL.catches` [CEL.Handler (\(e :: ExitCodeException) -> handleProcessFailed e),
                        CEL.Handler (\(e :: CEL.SomeException) -> handleOtherErrors e)]
 where
    handleProcessFailed :: (MonadBaseControl IO m, MonadLoggerIO m) => ExitCodeException -> m ()
    handleProcessFailed ExitCodeException{..} = do
        logErrorN $ cs eceStderr
        liftIO $ ioError (mkIOError OtherError (cmd ++ unwords args ++ " (" ++ show eceExitCode ++ ")")
                                    Nothing Nothing)

    handleOtherErrors :: (MonadBaseControl IO m, MonadLoggerIO m, CEL.Exception e) => e -> m ()
    handleOtherErrors e = do
        logErrorN $ cs $ show e
        liftIO $ ioError (mkIOError OtherError (cmd ++ unwords args ++ " (" ++ show e ++ ")")
                                    Nothing Nothing)

    doit :: (MonadBaseControl IO m, MonadLoggerIO m) => m ()
    doit = do
        logInfoN $ T.intercalate " " $ T.pack cmd : map T.pack args
        (stdout, _) <- liftIO $ readProcess_ (proc cmd args)

        unless (T.null $ T.strip $ cs stdout) $
            logInfoN $ cs stdout
