{-# LANGUAGE DeriveFunctor         #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude     #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE TypeOperators         #-}
-- | DSL/interpreter model for the HTTP client
module Imm.HTTP where

-- {{{ Imports
import           Imm.Error
import           Imm.Logger
import           Imm.Prelude
import           Imm.Pretty

import           Control.Monad.Trans.Free

import           URI.ByteString
-- }}}

-- * Types

-- | HTTP client DSL
data HttpClientF next
  = Get URI (Either SomeException LByteString -> next)
  deriving(Functor)

-- | HTTP client interpreter
newtype CoHttpClientF m a = CoHttpClientF
  { getH :: URI -> m (Either SomeException LByteString, a)
  } deriving(Functor)

instance Monad m => PairingM (CoHttpClientF m) HttpClientF m where
  -- pairM :: (a -> b -> m r) -> f a -> g b -> m r
  pairM p (CoHttpClientF g) (Get uri next) = do
    (result, a) <- g uri
    p a $ next result

-- * Primitives

-- | Perform an HTTP GET request
get :: (MonadFree f m, HttpClientF :<: f, LoggerF :<: f, MonadThrow m)
    => URI -> m LByteString
get uri = do
  logDebug $ "Fetching " <> prettyURI uri
  result <- liftF . inj $ Get uri id
  liftE result