cleveland-0.3.0: Testing framework for Morley.
Safe HaskellSafe-Inferred
LanguageHaskell2010

Test.Cleveland.Internal.Exceptions

Description

This module defines common exception helpers.

Synopsis

Documentation

class (Show ann, Typeable ann) => ExceptionAnnotation ann where Source #

Type class for exception annotations.

Minimal complete definition

displayAnnotation

Methods

displayAnnotation :: ann -> Builder -> Builder Source #

Given an annotation and the error message (as Builder), produce the text annotated with the annotation.

annotationPriority :: Int Source #

Relative priority for sorting annotations. Annotations with higher priority will be applied first. Default is 0.

annotateExceptions :: forall m a ann. (MonadCatch m, Semigroup ann, ExceptionAnnotation ann) => ann -> m a -> m a Source #

Add an annotation to exceptions thrown by a monadic action.

fromPossiblyAnnotatedException :: Exception e => SomeException -> Maybe e Source #

Try to extract a given exception type, skipping over annotations. This essentially makes AnnotatedException transparent for this function.

This is intended to be used as the default implementation of fromException for exception types that are expected to be annotated, as to make AnnotatedException transparent for catch, try, etc.

For example:

instance Exception GenericTestError where
  displayException = pretty
  fromException = fromPossiblyAnnotatedException

Now, when catching GenericTestError, it'll work even if wrapped in AnnotatedException:

>>> try @_ @GenericTestError (addCallStack $ throwM UnexpectedSuccess)
Left UnexpectedSuccess

When catching exceptions not explicitly set up like this, this trick unfortunately won't work, so using this function explicitly is required:

>>> data ExceptionToCatch = ExceptionToCatch deriving stock Show
>>> instance Exception ExceptionToCatch
>>> action = addCallStack $ throwM ExceptionToCatch
>>> try @_ @ExceptionToCatch action
*** Exception: AnnotatedException ...
...ExceptionToCatch
>>> :{
action `catch` \(err :: SomeException) -> case fromPossiblyAnnotatedException err of
  Just (err' :: ExceptionToCatch) -> pass
  Nothing -> throwM err
:}

Utilities

lookupAnnEx :: forall ann. ExceptionAnnotation ann => SomeException -> Maybe ann Source #

Extract the given annotation type from SomeException if possible.

insertAnnEx :: (Semigroup ann, ExceptionAnnotation ann) => ann -> SomeException -> SomeException Source #

Insert a new annotation into SomeException.

If it's already an AnnotatedException, simply adds a new annotation. Otherwise, wraps the exception into AnnotatedException first.

removeAnnEx :: forall ann. ExceptionAnnotation ann => SomeException -> SomeException Source #

Remove an annotation of the given type from SomeException. If it's not an AnnotatedException, this is a no-op.

Specific annotations

addCallStack :: (MonadCatch m, HasCallStack) => m a -> m a Source #

A convenience synonym for annotateExceptions (CallStackAnnotation callStack).