{-|
Module      : Client.EventLoop.Errors
Description : Human-readable versions of connection failure
Copyright   : (c) Eric Mertens, 2016
License     : ISC
Maintainer  : emertens@gmail.com

This module provides a prettier rendering for exceptions that are
common in network connects as well as hints about what causes these
errors.
-}

module Client.EventLoop.Errors
  ( exceptionToLines
  ) where

import Control.Exception (SomeException, Exception(displayException, fromException))
import Data.List.NonEmpty (NonEmpty(..))
import OpenSSL.Session (ConnectionAbruptlyTerminated, ProtocolError(ProtocolError))
import Hookup (ConnectionFailure(..))

-- | Compute the message message text to be used for a connection error
exceptionToLines ::
  SomeException   {- ^ network error -} ->
  NonEmpty String {- ^ client lines  -}
exceptionToLines
  = indentMessages
  . exceptionToLines'

indentMessages :: NonEmpty String -> NonEmpty String
indentMessages (x :| xs) = x :| map ("⋯ "++) xs

exceptionToLines' ::
  SomeException   {- ^ network error -} ->
  NonEmpty String {- ^ client lines  -}
exceptionToLines' ex

  -- connection package errors
  | Just err  <- fromException ex = explainHookupError err

  -- HsOpenSSL errors
  | Just _ <- fromException ex :: Maybe ConnectionAbruptlyTerminated =
     "Connection abruptly terminated" :| []
  | Just (ProtocolError e) <- fromException ex =
     ("TLS protocol error: " ++ e) :| []

  -- IOErrors, typically network package.
  | Just ioe <- fromException ex =
     explainIOError ioe :| []

  -- Anything else including glirc's errors (which use displayException)
  | otherwise = displayException ex :| []

explainIOError :: IOError -> String
explainIOError ioe = "IO error: " ++ displayException ioe

explainHookupError :: ConnectionFailure -> NonEmpty String
explainHookupError e =
  case e of
    ConnectionFailure exs ->
      "Connect failed" :| map displayException exs

    LineTooLong ->
      "IRC message too long" :| []

    LineTruncated ->
      "IRC message incomplete" :| []

    -- the remaining messages can be used as is and on one line
    _ -> displayException e :| []
