module Patrol.Type.Stacktrace where

import qualified Data.Aeson as Aeson
import qualified Data.Map as Map
import qualified Data.Text as Text
import qualified GHC.Stack as Stack
import qualified Patrol.Extra.Aeson as Aeson
import qualified Patrol.Type.Frame as Frame

-- | <https://develop.sentry.dev/sdk/event-payloads/types/#stacktrace>
data Stacktrace = Stacktrace
  { Stacktrace -> [Frame]
frames :: [Frame.Frame],
    Stacktrace -> Map Text Text
registers :: Map.Map Text.Text Text.Text
  }
  deriving (Stacktrace -> Stacktrace -> Bool
(Stacktrace -> Stacktrace -> Bool)
-> (Stacktrace -> Stacktrace -> Bool) -> Eq Stacktrace
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Stacktrace -> Stacktrace -> Bool
== :: Stacktrace -> Stacktrace -> Bool
$c/= :: Stacktrace -> Stacktrace -> Bool
/= :: Stacktrace -> Stacktrace -> Bool
Eq, Int -> Stacktrace -> ShowS
[Stacktrace] -> ShowS
Stacktrace -> String
(Int -> Stacktrace -> ShowS)
-> (Stacktrace -> String)
-> ([Stacktrace] -> ShowS)
-> Show Stacktrace
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Stacktrace -> ShowS
showsPrec :: Int -> Stacktrace -> ShowS
$cshow :: Stacktrace -> String
show :: Stacktrace -> String
$cshowList :: [Stacktrace] -> ShowS
showList :: [Stacktrace] -> ShowS
Show)

instance Aeson.ToJSON Stacktrace where
  toJSON :: Stacktrace -> Value
toJSON Stacktrace
stacktrace =
    [Pair] -> Value
Aeson.intoObject
      [ String -> [Frame] -> Pair
forall a. ToJSON a => String -> a -> Pair
Aeson.pair String
"frames" ([Frame] -> Pair) -> [Frame] -> Pair
forall a b. (a -> b) -> a -> b
$ Stacktrace -> [Frame]
frames Stacktrace
stacktrace,
        String -> Map Text Text -> Pair
forall a. ToJSON a => String -> a -> Pair
Aeson.pair String
"registers" (Map Text Text -> Pair) -> Map Text Text -> Pair
forall a b. (a -> b) -> a -> b
$ Stacktrace -> Map Text Text
registers Stacktrace
stacktrace
      ]

empty :: Stacktrace
empty :: Stacktrace
empty =
  Stacktrace
    { frames :: [Frame]
frames = [],
      registers :: Map Text Text
registers = Map Text Text
forall k a. Map k a
Map.empty
    }

fromCallStack :: Stack.CallStack -> Stacktrace
fromCallStack :: CallStack -> Stacktrace
fromCallStack =
  let intoFrame :: String -> SrcLoc -> Frame
intoFrame String
string SrcLoc
srcLoc =
        (SrcLoc -> Frame
Frame.fromSrcLoc SrcLoc
srcLoc)
          { Frame.function = Text.pack string
          }
   in ([Frame] -> Map Text Text -> Stacktrace)
-> Map Text Text -> [Frame] -> Stacktrace
forall a b c. (a -> b -> c) -> b -> a -> c
flip [Frame] -> Map Text Text -> Stacktrace
Stacktrace Map Text Text
forall k a. Map k a
Map.empty ([Frame] -> Stacktrace)
-> (CallStack -> [Frame]) -> CallStack -> Stacktrace
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((String, SrcLoc) -> Frame) -> [(String, SrcLoc)] -> [Frame]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((String -> SrcLoc -> Frame) -> (String, SrcLoc) -> Frame
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry String -> SrcLoc -> Frame
intoFrame) ([(String, SrcLoc)] -> [Frame])
-> (CallStack -> [(String, SrcLoc)]) -> CallStack -> [Frame]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(String, SrcLoc)] -> [(String, SrcLoc)]
forall a. [a] -> [a]
reverse ([(String, SrcLoc)] -> [(String, SrcLoc)])
-> (CallStack -> [(String, SrcLoc)])
-> CallStack
-> [(String, SrcLoc)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CallStack -> [(String, SrcLoc)]
Stack.getCallStack