module RawFilePath.Process.Utility
    ( callProcess
    , readProcessWithExitCode
    ) where

-- base modules

import RawFilePath.Import

-- extra modules

import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as LB
import qualified Data.ByteString.Builder as B

-- local modules

import RawFilePath.Process.Common
import RawFilePath.Process.Basic

-- | Create a new process with the given configuration, and wait for it to
-- finish.
callProcess :: ProcessConf stdin stdout stderr -> IO ExitCode
callProcess conf = start >>= waitForProcess
  where
    start = startProcess conf
        { cfgStdin = NoStream
        , cfgStdout = NoStream
        , cfgStderr = NoStream
        }

-- | Fork an external process, read its standard output and standard error
-- strictly, blocking until the process terminates, and return them with the
-- process exit code.
readProcessWithExitCode
    :: ProcessConf stdin stdout stderr
    -> IO (ExitCode, ByteString, ByteString)
readProcessWithExitCode conf = do
    process <- startProcess conf
        { cfgStdin = NoStream
        , cfgStdout = CreatePipe
        , cfgStderr = CreatePipe
        }
    stdoutB <- hGetAll (processStdout process)
    stderrB <- hGetAll (processStderr process)
    exitCode <- waitForProcess process
    return (exitCode, stdoutB, stderrB)

-- utility functions

-- Read from Handle until IOError
hGetAll :: Handle -> IO ByteString
hGetAll h = LB.toStrict . B.toLazyByteString <$> hGetAll' mempty h
  where
    hGetAll' acc h' = tryIOError (B.hGetContents h) >>= \ case
        Left _ -> return acc
        Right b -> hGetAll' (acc <> B.byteString b) h'