{-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns #-}
module Di.Handle
( stderr
, handle
, blob
, LineRenderer(LineRendererUtf8)
, BlobRenderer(BlobRenderer)
) where
import qualified Control.Monad.Catch as Ex
import Control.Monad.IO.Class (MonadIO(liftIO))
import qualified Data.ByteString.Builder as BB
import Data.Monoid ((<>))
import qualified System.IO as IO
#ifdef VERSION_unix
import qualified System.Posix.Terminal
import qualified System.Posix.IO
#endif
import Di.Core (Log)
data LineRenderer level path msg
= LineRendererUtf8 !(Bool -> Log level path msg -> BB.Builder)
data BlobRenderer level path msg
= BlobRenderer !(Log level path msg -> BB.Builder)
handle
:: (MonadIO m, MonadIO n)
=> Maybe Bool
-> IO.Handle
-> LineRenderer level path msg
-> m (Log level path msg -> n ())
handle ywantColors h (LineRendererUtf8 render0) = liftIO $ do
wantColors <- maybe (isTty h) pure ywantColors
let !render1 = render0 wantColors
!newline = BB.char7 '\n'
render2 = \log' -> render1 log' <> newline
blob h (BlobRenderer render2)
blob
:: (MonadIO m, MonadIO n)
=> IO.Handle
-> BlobRenderer level path msg
-> m (Log level path msg -> n ())
blob h (BlobRenderer render) = liftIO $ do
IO.hSetBinaryMode h True
pure $ \log_ ->
liftIO (Ex.finally (BB.hPutBuilder h (render log_))
(IO.hFlush h))
stderr
:: (MonadIO m, MonadIO n)
=> LineRenderer level path msg
-> m (Log level path msg -> n ())
stderr = handle Nothing IO.stderr
isTty :: IO.Handle -> IO Bool
#ifdef VERSION_unix
isTty h
| h == IO.stderr = q System.Posix.IO.stdError
| h == IO.stdout = q System.Posix.IO.stdOutput
| h == IO.stdin = q System.Posix.IO.stdInput
| otherwise = pure False
where q = System.Posix.Terminal.queryTerminal
#else
isTty _ = pure False
#endif