{-# LANGUAGE DataKinds      #-}
{-# LANGUAGE DeriveAnyClass #-}

{-|
Description: HTTP utils used elsewhere in the library.

This module contains HTTP functions needed and used from other modules
within the `Follow` library.
-}
module HTTP.Follow
  ( parseUrl
  , getResponseBody
  , HTTPError(..)
  ) where

import           Control.Monad.Catch  (Exception, MonadThrow, throwM)
import qualified Data.ByteString      as BS (ByteString)
import qualified Data.ByteString.Lazy as BL (ByteString)
import qualified Network.HTTP.Req     as R (GET (..), HttpException, MonadHttp,
                                            NoReqBody (..), Option, Scheme (..),
                                            Url, handleHttpException,
                                            lbsResponse, parseUrl, req,
                                            responseBody)

type Url s = (R.Url s, R.Option s)

type EitherUrl = (Either (Url R.Http) (Url R.Https))

-- HTTP errors
data HTTPError =
  URLWrongFormat
  deriving (Eq, Show, Exception)

-- | Parses a url type from a textual representation.
parseUrl :: (MonadThrow m) => BS.ByteString -> m EitherUrl
parseUrl url = maybe (throwM URLWrongFormat) return (R.parseUrl url)

-- | Performs a request to given url and returns just the response body
getResponseBody :: (R.MonadHttp m, MonadThrow m) => EitherUrl -> m BL.ByteString
getResponseBody = either fetch fetch
  where
    fetch (url, option) =
      R.responseBody <$> R.req R.GET url R.NoReqBody R.lbsResponse option

-- | Declares how to handle request errors for IO monad.
instance R.MonadHttp IO where
  handleHttpException = throwM