packages feed

binary-io-0.4.0: lib/Data/Binary/IO.hs

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}

-- | Read and write values of types that implement 'Binary.Binary' from and to 'Handle's
--
-- This module homes the unlifted API variant. For proper documentation check out the equally named
-- functions in "Data.Binary.IO.Lifted"
--
module Data.Binary.IO
  ( -- * Reader
    Lifted.ReaderError (..)

  , Reader (..)
  , newReader
  , newReaderWith

    -- * Writer
  , Writer (..)
  , newWriter
  , newWriterWith

    -- * Pipe
  , newPipe

    -- * Duplex
  , Duplex (..)
  , newDuplex
  , newDuplexWith

    -- * Classes
  , CanGet
  , runGet
  , read
  , isEmpty

  , CanPut
  , runPut
  , write
  )
where

import           Data.Bifunctor (bimap)
import qualified Data.Binary as Binary
import qualified Data.Binary.Get as Binary.Get
import qualified Data.Binary.IO.Lifted as Lifted
import qualified Data.ByteString as ByteString
import           Prelude hiding (read)
import           System.IO (Handle)

-- * Reader

-- | Alias for 'Lifted.Reader' 'IO'
--
-- @since 0.0.1
newtype Reader = Reader
  { unReader :: Lifted.Reader IO }

instance Lifted.CanGet Reader IO where
  runGet = runGet . unReader

-- | Unlifted version of 'Lifted.newReader'
--
-- @since 0.0.1
newReader
  :: Handle -- ^ Handle that will be read from
  -> IO Reader
newReader handle =
  Reader <$> Lifted.newReader handle

-- | Unlifted version of 'Lifted.newReaderWith'.
--
-- @since 0.1.1
newReaderWith
  :: IO ByteString.ByteString -- ^ Chunk producer
  -> IO Reader
newReaderWith get =
  Reader <$> Lifted.newReaderWith get

-- * Writer

-- | @since 0.0.1
newtype Writer = Writer
  { unWriter :: Lifted.Writer IO }

instance Lifted.CanPut Writer IO where
  runPut = runPut . unWriter

-- | Unlifted version of 'Lifted.newWriter'
--
-- @since 0.0.1
newWriter
  :: Handle -- ^ Handle that will be written to
  -> Writer
newWriter =
  Writer . Lifted.newWriter

-- | Unlifted version of 'Lifted.newWriterWith'
--
-- @since 0.1.1
newWriterWith
  :: (ByteString.ByteString -> IO ()) -- ^ Chunk handler
  -> Writer
newWriterWith =
  Writer . Lifted.newWriterWith

-- * Pipe

-- | Unlifted version of 'Lifted.newPipe'
--
-- @since 0.2.0
newPipe :: IO (Reader, Writer)
newPipe = bimap Reader Writer <$> Lifted.newPipe

-- * Duplex

-- | @since 0.0.1
data Duplex = Duplex
  { duplexWriter :: !Writer
  , duplexReader :: !Reader
  }

instance Lifted.CanGet Duplex IO where
  runGet = runGet . duplexReader

instance Lifted.CanPut Duplex IO where
  runPut = runPut . duplexWriter

-- | Unlifted version of 'Lifted.newDuplex'
--
-- @since 0.0.1
newDuplex
  :: Handle -- ^ Handle that will be read from and written to
  -> IO Duplex
newDuplex handle =
  Duplex (newWriter handle) <$> newReader handle

-- | Unlifted version of 'Lifted.newDuplexWith'
--
-- @since 0.1.1
newDuplexWith
  :: IO ByteString.ByteString
  -> (ByteString.ByteString -> IO ())
  -> IO Duplex
newDuplexWith get push =
  Duplex (newWriterWith push) <$> newReaderWith get

-- * Classes

-- | Alias for 'Lifted.CanGet' @r@ 'IO'
--
-- @since 0.0.1
type CanGet r = Lifted.CanGet r IO

-- | Unlifted version of 'Lifted.runGet'
--
-- @since 0.0.1
runGet
  :: CanGet r
  => r -- ^ Reader / source
  -> Binary.Get a -- ^ Operation to execute
  -> IO a
runGet =
  Lifted.runGet

-- | Unlifted version of 'Lifted.read'
--
-- @since 0.0.1
read
  :: (CanGet r, Binary.Binary a)
  => r -- ^ Read source
  -> IO a
read reader =
  runGet reader Binary.get

-- | Unlifted version of 'Lifted.isEmpty'
--
-- @since 0.3.0
isEmpty :: CanGet r => r -> IO Bool
isEmpty reader = runGet reader Binary.Get.isEmpty

-- | Alias for 'Lifted.CanPut' @w@ 'IO'
--
-- @since 0.0.1
type CanPut w = Lifted.CanPut w IO

-- | Unlifted version of 'Lifted.runPut'
--
-- @since 0.0.1
runPut
  :: CanPut w
  => w -- ^ Writer / target
  -> Binary.Put -- ^ Operation to execute
  -> IO ()
runPut = Lifted.runPut

-- | Unlifted version of 'Lifted.write'
--
-- @since 0.0.1
write
  :: (CanPut w, Binary.Binary a)
  => w -- ^ Write target
  -> a -- ^ Value to be written
  -> IO ()
write =
  Lifted.write