{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE OverloadedStrings #-}
module Util.CallStack (
Stack
, Caller(..), caller, showCaller
, errorStack
, errorIO
, getStack, getStack1
, throw
) where
import qualified Control.Exception as Exception
import qualified Control.Monad.Trans as Trans
import qualified Data.Aeson as Aeson
import qualified Data.Text as Text
import Data.Text (Text)
import qualified GHC.Stack as Stack
type Stack = Stack.HasCallStack
data Caller = Caller !FilePath !Int | NoCaller deriving (Caller -> Caller -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Caller -> Caller -> Bool
$c/= :: Caller -> Caller -> Bool
== :: Caller -> Caller -> Bool
$c== :: Caller -> Caller -> Bool
Eq, Int -> Caller -> ShowS
[Caller] -> ShowS
Caller -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Caller] -> ShowS
$cshowList :: [Caller] -> ShowS
show :: Caller -> String
$cshow :: Caller -> String
showsPrec :: Int -> Caller -> ShowS
$cshowsPrec :: Int -> Caller -> ShowS
Show, ReadPrec [Caller]
ReadPrec Caller
Int -> ReadS Caller
ReadS [Caller]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Caller]
$creadListPrec :: ReadPrec [Caller]
readPrec :: ReadPrec Caller
$creadPrec :: ReadPrec Caller
readList :: ReadS [Caller]
$creadList :: ReadS [Caller]
readsPrec :: Int -> ReadS Caller
$creadsPrec :: Int -> ReadS Caller
Read)
instance Aeson.ToJSON Caller where
toJSON :: Caller -> Value
toJSON (Caller String
fname Int
line) = forall a. ToJSON a => a -> Value
Aeson.toJSON (String
fname, Int
line)
toJSON Caller
NoCaller = Value
Aeson.Null
instance Aeson.FromJSON Caller where
parseJSON :: Value -> Parser Caller
parseJSON Value
val = case Value
val of
Value
Aeson.Null -> forall (m :: * -> *) a. Monad m => a -> m a
return Caller
NoCaller
Value
_ -> forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry String -> Int -> Caller
Caller forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. FromJSON a => Value -> Parser a
Aeson.parseJSON Value
val
caller :: Stack.CallStack -> Caller
caller :: CallStack -> Caller
caller CallStack
stack = case forall a. [a] -> [a]
reverse (CallStack -> [(String, SrcLoc)]
Stack.getCallStack CallStack
stack) of
(String
_, SrcLoc
srcloc) : [(String, SrcLoc)]
_ ->
String -> Int -> Caller
Caller (ShowS
strip (SrcLoc -> String
Stack.srcLocFile SrcLoc
srcloc))
(SrcLoc -> Int
Stack.srcLocStartLine SrcLoc
srcloc)
[] -> Caller
NoCaller
where
strip :: ShowS
strip (Char
'.':Char
'/':String
s) = String
s
strip String
s = String
s
showFrame :: (String, Stack.SrcLoc) -> String
showFrame :: (String, SrcLoc) -> String
showFrame (String
name, SrcLoc
srcloc) =
String
name forall a. Semigroup a => a -> a -> a
<> String
": " forall a. Semigroup a => a -> a -> a
<> ShowS
strip (SrcLoc -> String
Stack.srcLocFile SrcLoc
srcloc) forall a. Semigroup a => a -> a -> a
<> String
":"
forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show (SrcLoc -> Int
Stack.srcLocStartLine SrcLoc
srcloc)
where
strip :: ShowS
strip (Char
'.':Char
'/':String
s) = String
s
strip String
s = String
s
showCaller :: Caller -> Text
showCaller :: Caller -> Text
showCaller (Caller String
fname Int
line) = String -> Text
Text.pack String
fname forall a. Semigroup a => a -> a -> a
<> Text
":" forall a. Semigroup a => a -> a -> a
<> String -> Text
Text.pack (forall a. Show a => a -> String
show Int
line)
showCaller Caller
NoCaller = Text
"<no-caller>"
getStack :: Stack => Text
getStack :: Stack => Text
getStack = Text -> [Text] -> Text
Text.intercalate Text
"; " forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map (String -> Text
Text.pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String, SrcLoc) -> String
showFrame) forall a b. (a -> b) -> a -> b
$ forall a. [a] -> [a]
reverse forall a b. (a -> b) -> a -> b
$
CallStack -> [(String, SrcLoc)]
Stack.getCallStack Stack => CallStack
Stack.callStack
getStack1 :: Stack => Text
getStack1 :: Stack => Text
getStack1 = Caller -> Text
showCaller forall a b. (a -> b) -> a -> b
$ CallStack -> Caller
caller forall a b. (a -> b) -> a -> b
$ Stack => CallStack
Stack.callStack
newtype Error = Error Text
deriving (Int -> Error -> ShowS
[Error] -> ShowS
Error -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Error] -> ShowS
$cshowList :: [Error] -> ShowS
show :: Error -> String
$cshow :: Error -> String
showsPrec :: Int -> Error -> ShowS
$cshowsPrec :: Int -> Error -> ShowS
Show)
instance Exception.Exception Error where
displayException :: Error -> String
displayException (Error Text
msg) = Text -> String
Text.unpack Text
msg
errorStack :: Stack => Text -> a
errorStack :: forall a. Stack => Text -> a
errorStack = forall a e. Exception e => e -> a
Exception.throw forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Error
Error forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Stack => Text
getStack forall a. Semigroup a => a -> a -> a
<> Text
": ") <>)
errorIO :: Stack => Trans.MonadIO m => Text -> m a
errorIO :: forall (m :: * -> *) a. (Stack, MonadIO m) => Text -> m a
errorIO = forall (m :: * -> *) a. MonadIO m => IO a -> m a
Trans.liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall e a. Exception e => e -> IO a
Exception.throwIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Error
Error forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Stack => Text
getStack forall a. Semigroup a => a -> a -> a
<> Text
": ") <>)
throw :: (Stack, Exception.Exception e) => (Text -> e) -> Text -> a
throw :: forall e a. (Stack, Exception e) => (Text -> e) -> Text -> a
throw Text -> e
toExc Text
msg = forall a e. Exception e => e -> a
Exception.throw (Text -> e
toExc (Stack => Text
getStack forall a. Semigroup a => a -> a -> a
<> Text
": " forall a. Semigroup a => a -> a -> a
<> Text
msg))