{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ConstraintKinds #-}

{-# OPTIONS_HADDOCK show-extensions #-}

{- |

Class using type families to represent abstract connection types
that (in addition to the usual network operations)
can be upgraded from insecure to secure.

Any instance must specify:

*   @'Params'@, the type of parameters that can be used to
    open a connection. (This might be a just a tuple containing
    just hostname and port, say, or might be some more complicated
    structure.)

*   @'Cstrt'@, the constraints the methods require. For instance,
    a 'Connection' using real network operations would likely
    have 'MonadIO' as a constraint.

*   Implementations for 'open', 'close', 'send', 'recv' and 'upgrade'. 
    These are typically very thin wrappers around the operations
    provided by a network library.
-}

module Network.Mail.Assumpta.Connection
  where

import Control.Monad.Catch
import Data.ByteString ( ByteString )
import Data.Constraint

-- | Class for abstract connections over some
-- transport channel @c@.
class Connection c where
  -- | Constraints an implementation must satisfy.
  -- (e.g., 'MonadIO' for network-based connections.)
  type Cstrt c :: (* -> *) -> Constraint
  -- | Parameters used to create a connection --
  -- e.g. hostname, port, TLS settings, etc.
  type Params c :: *

  open    :: (Cstrt c) m => Params c -> m c         -- ^ open a connection
  close   :: (Cstrt c) m => c -> m ()               -- ^ close a connection
  send    :: (Cstrt c) m => c -> ByteString -> m () -- ^ send a bytestring
  recv    :: (Cstrt c) m => c -> m ByteString       -- ^ receive a bytestring
  upgrade :: (Cstrt c) m => c -> m ()               -- ^ upgrade security


-- | @withConnection p a@
--
-- 'bracket' for 'Connection's.
-- Given some parameters @p@ for opening a connection: 
-- open a connection, run some action @a@ with it, then
-- close.
withConnection ::
  (MonadMask m, Cstrt c m, Connection c) =>
  Params c -> (c -> m b) -> m b
withConnection params =
    bracket acquire release
  where
    acquire   = open params
    release   = close