{-# Language Trustworthy #-} {-# Language ImplicitParams #-} {-# Language TemplateHaskell #-} module Panic ( Panic(..) , PanicComponent(..) , useGitRevision , HasCallStack , panic ) where import Development.GitRev import Language.Haskell.TH import Data.Typeable import Control.Exception(Exception, throw) import Data.Maybe(fromMaybe,listToMaybe) import GHC.Stack -- | Throw an exception for the given component. panic :: (PanicComponent a, HasCallStack) => a {- ^ Component identification -} -> String {- ^ Location of problem -} -> [String] {- ^ Problem description (lines) -} -> b panic comp loc msg = throw Panic { panicComponent = comp , panicLoc = loc , panicMsg = msg , panicStack = freezeCallStack ?callStack } -- | The exception thrown when panicing. data Panic a = Panic { panicComponent :: a , panicLoc :: String , panicMsg :: [String] , panicStack :: CallStack } -- | Description of a component. class Typeable a => PanicComponent a where panicComponentName :: a -> String -- ^ Name of the panicing component. panicComponentIssues :: a -> String -- ^ Issue tracker for the panicking component. panicComponentRevision :: a -> (String,String) -- ^ Information about the component's revision. -- (commit hash, branch info) -- | An expression of type @a -> (String,String)@. -- Uses template Haskell to query Git for the current state of the repo. -- Note that the state reported depends on when the module containing -- the splice was compiled. useGitRevision :: Q Exp useGitRevision = [| \_ -> ($gitHash, $gitBranch ++ $dirty) |] where dirty = [| if $gitDirty then " (uncommited files present)" else "" |] instance (PanicComponent a) => Show (Panic a) where show p = unlines $ [ "You have encountered a bug in " ++ panicComponentName comp ++ "'s implementation." , "*** Please create an issue at " ++ panicComponentIssues comp , "" , "%< --------------------------------------------------- " ] ++ rev ++ [ locLab ++ panicLoc p , msgLab ++ fromMaybe "" (listToMaybe msgLines) ] ++ map (tabs ++) (drop 1 msgLines) ++ [ prettyCallStack (panicStack p) ] ++ [ "%< --------------------------------------------------- " ] where comp = panicComponent p msgLab = " Message: " locLab = " Location: " revLab = " Revision: " branchLab = " Branch: " msgLines = panicMsg p tabs = map (const ' ') msgLab (commitHash,commitBranch) = panicComponentRevision comp rev | null commitHash = [] | otherwise = [ revLab ++ commitHash , branchLab ++ commitBranch ] instance PanicComponent a => Exception (Panic a)