{-
BSD3 credits to Merijn Verstraaten
-}

module System.CaptureStdout (captureStdout) where

import Control.Exception (SomeException, bracket, try)
import Data.Text
import Data.Text.IO (hGetContents)
import GHC.IO.Handle (hDuplicate, hDuplicateTo)
import System.IO (Handle, SeekMode (..), hFlush, hSeek, stdout)
import System.IO.Temp (withSystemTempFile)

captureStdout :: String -> IO () -> IO Text
captureStdout tmp act = withSystemTempFile tmp $ \_ hnd -> do
  let redirect :: IO Handle
      redirect = do
        hFlush stdout
        hDuplicate stdout <* hDuplicateTo hnd stdout

      undo :: Handle -> IO ()
      undo h = hFlush stdout >> hDuplicateTo h stdout

  _ <- bracket redirect undo $ \_ -> try act :: IO (Either SomeException ())

  hSeek hnd AbsoluteSeek 0
  hGetContents hnd