packages feed

bugsnag-1.0.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))
    |]