packages feed

bugsnag-1.1.0.0: src/Network/Bugsnag/StackFrame.hs

{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}

module Network.Bugsnag.StackFrame
  ( attachBugsnagCode
  , currentStackFrame
  ) where

import Prelude

import Data.Bugsnag
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.Text (Text, pack, unpack)
import Instances.TH.Lift ()
import Language.Haskell.TH.Syntax
import Network.Bugsnag.CodeIndex

-- | Attempt to attach code to a 'StackFrame'
--
-- Looks up the content in the Index by File/LineNumber and, if found, sets it
-- on the record.
attachBugsnagCode :: CodeIndex -> StackFrame -> StackFrame
attachBugsnagCode index sf =
  sf
    { stackFrame_code =
        findBugsnagCode
          (unpack $ stackFrame_file sf)
          (stackFrame_lineNumber sf)
          index
    }

findBugsnagCode :: FilePath -> Int -> CodeIndex -> Maybe (HashMap Int Text)
findBugsnagCode path n =
  fmap HashMap.fromList
    . findSourceRange path (begin, n + 3)
 where
  begin
    | n < 3 = 0
    | otherwise = n - 3

-- | Construct a 'StackFrame' from the point of this splice
--
-- Unfortunately there's no way to know the function, so that must be given:
currentStackFrame :: Q Exp
currentStackFrame = [|locStackFrame $(qLocation >>= liftLoc)|]

-- brittany-disable-next-binding

locStackFrame :: Loc -> Text -> StackFrame
locStackFrame (Loc path _ _ (ls, cs) _) func =
  defaultStackFrame
    { stackFrame_file = pack path
    , stackFrame_lineNumber = ls
    , stackFrame_columnNumber = Just cs
    , stackFrame_method = func
    , stackFrame_inProject = Just True
    , -- N.B. this assumes we're unlikely to see adoption within libraries, or
      -- that such a thing would even work. If this function's used, it's
      -- assumed to be in end-user code.
      stackFrame_code = Nothing
    }

-- brittany-disable-next-binding

-- Taken from monad-logger
liftLoc :: Loc -> Q Exp
liftLoc (Loc a b c (d1, d2) (e1, e2)) =
  [|
    Loc
      $(lift a)
      $(lift b)
      $(lift c)
      ($(lift d1), $(lift d2))
      ($(lift e1), $(lift e2))
    |]