packages feed

craft-0.0.0.1: src/Craft/Run/Internal.hs

module Craft.Run.Internal where

import           Conduit                 as C
import           Control.Monad.Logger    (LoggingT, askLoggerIO, logDebugNS,
                                          runLoggingT)
import qualified Control.Monad.Trans     as Trans
import qualified Data.Conduit.List       as CL
import           Data.Conduit.Process    (sourceProcessWithStreams)
import           Data.Conduit.Text       as CT
import           Data.Monoid             ((<>))
import qualified Data.Text               as T
import           System.Exit
import           System.Process
import qualified System.Process.ListLike as SPLL

import           Craft.Types


isSuccessCode :: ExitCode -> Bool
isSuccessCode ExitSuccess     = True
isSuccessCode (ExitFailure _) = False


execProc :: CreateProcess -> LoggingT IO ExecResult
execProc p = do
  logDebugNS "exec|process" $ T.pack $ showProc p
  (exit', stdoutRaw, stderrRaw) <- Trans.lift $ SPLL.readCreateProcessWithExitCode p "" {- stdin -}
  let stdout' = trimNL stdoutRaw
  let stderr' = trimNL stderrRaw
  return $ case exit' of
    ExitSuccess      -> ExecSucc $ SuccResult stdout' stderr' p
    ExitFailure code -> ExecFail $ FailResult code stdout' stderr' p


execProc_ :: String -> CreateProcess -> LoggingT IO ()
execProc_ src p = do
  let p' = p { std_in  = CreatePipe
             , std_out = CreatePipe
             , std_err = CreatePipe
             }
  logDebugNS "exec_|process" $ T.pack $ showProc p

  logger <- askLoggerIO
  let src' = "exec_|" <> T.pack src
      srcOut = src' <> "|stdout"
      srcErr = src' <> "|stderr"

  (ec, _, _) <- Trans.lift $ sourceProcessWithStreams
                               p'
                               (CL.sourceNull)
                               (pipeConsumer logger srcOut)
                               (pipeConsumer logger srcErr)
  case ec of
    ExitFailure n -> $craftError $ "exec_ `" ++ src ++ "` failed with code: " ++ show n
    ExitSuccess   -> return ()
 where
   pipeConsumer logger s = decodeUtf8C =$= CT.lines =$ awaitForever (\txt ->
     (`runLoggingT` logger) (logDebugNS s txt))


-- | Remove a single trailing newline character from the end of the String
trimNL :: String -> String
trimNL = reverse . rmNL . reverse
 where
  rmNL []        = []
  rmNL ('\n':xs) = rmCR xs
  rmNL xs        = xs
  rmCR []        = []
  rmCR ('\r':xs) = xs
  rmCR xs        = xs