src/Distribution/Utils/IOData.hs

    {-# LANGUAGE GADTs #-}
    {-# LANGUAGE RankNTypes #-}
    -- | @since 2.2.0
    module Distribution.Utils.IOData
        ( -- * 'IOData' & 'IODataMode' type
          IOData (..)
        , IODataMode (..)
        , KnownIODataMode (..)
        , withIOData
        , null
        , hPutContents
        ) where

    import qualified Data.ByteString.Lazy as LBS
    import           Distribution.Compat.Prelude hiding (null)
    import qualified Prelude
    import qualified System.IO

    -- | Represents either textual or binary data passed via I/O functions
    -- which support binary/text mode
    --
    -- @since 2.2
    data IOData
        = IODataText String
        -- ^ How Text gets encoded is usually locale-dependent.
        | IODataBinary LBS.ByteString
        -- ^ Raw binary which gets read/written in binary mode.

    withIOData :: IOData -> (forall mode. IODataMode mode -> mode -> r) -> r
    withIOData (IODataText str) k   = k IODataModeText str
    withIOData (IODataBinary lbs) k = k IODataModeBinary lbs

    -- | Test whether 'IOData' is empty
    null :: IOData -> Bool
    null (IODataText s)   = Prelude.null s
    null (IODataBinary b) = LBS.null b

    instance NFData IOData where
        rnf (IODataText s)     = rnf s
        rnf (IODataBinary lbs) = rnf lbs

    -- | @since 2.2
    class NFData mode => KnownIODataMode mode where
        -- | 'IOData' Wrapper for 'System.IO.hGetContents'
        --
        -- __Note__: This operation uses lazy I/O. Use 'NFData' to force all
        -- data to be read and consequently the internal file handle to be
        -- closed.
        --
        hGetIODataContents :: System.IO.Handle -> Prelude.IO mode

        toIOData   :: mode -> IOData
        iodataMode :: IODataMode mode

    -- | @since 3.2
    data IODataMode mode where
        IODataModeText   :: IODataMode String
        IODataModeBinary :: IODataMode LBS.ByteString

    instance a ~ Char => KnownIODataMode [a] where
        hGetIODataContents h = do
            System.IO.hSetBinaryMode h False
            System.IO.hGetContents h

        toIOData = IODataText
        iodataMode = IODataModeText

    instance KnownIODataMode LBS.ByteString where
        hGetIODataContents h = do
            System.IO.hSetBinaryMode h True
            LBS.hGetContents h

        toIOData = IODataBinary
        iodataMode = IODataModeBinary

    -- | 'IOData' Wrapper for 'System.IO.hPutStr' and 'System.IO.hClose'
    --
    -- This is the dual operation ot 'hGetIODataContents',
    -- and consequently the handle is closed with `hClose`.
    --
    -- /Note:/ this performs lazy-IO.
    --
    -- @since 2.2
    hPutContents :: System.IO.Handle -> IOData -> Prelude.IO ()
    hPutContents h (IODataText c) = do
        System.IO.hSetBinaryMode h False
        System.IO.hPutStr h c
        System.IO.hClose h
    hPutContents h (IODataBinary c) = do
        System.IO.hSetBinaryMode h True
        LBS.hPutStr h c
        System.IO.hClose h