content: {-|
  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 :| []
  