croaring-ffi-1.0: src/Data/Bitmap/Roaring/FFI/NullHandle.hs
{-# LANGUAGE NoFieldSelectors #-}
-- | The exception the @with@ helpers throw when their constructor returns a NULL handle.
module Data.Bitmap.Roaring.FFI.NullHandle
( NullHandle (..)
, bracket
) where
import Control.Exception (Exception (..), throwIO)
import Control.Exception qualified as Exception
import Data.Coerce (Coercible, coerce)
import Foreign.Ptr (Ptr, nullPtr)
{- | A @with@ helper's constructor returned NULL.
The helper cannot tell why.
Depending on the constructor, NULL means failed allocation, rejected arguments or malformed bytes.
-}
newtype NullHandle = NullHandle
{ function :: String
-- ^ The helper that received the NULL handle, such as @Data.Bitmap.Roaring.FFI.Bitmap.with@.
}
deriving (Eq, Show)
instance Exception NullHandle where
displayException (NullHandle function) = function <> ": the constructor returned NULL"
{- | Runs the action on the constructed handle and releases the handle when the action ends.
If the constructor returns NULL, throws t'NullHandle' with the given name instead of running the action.
-}
bracket :: forall handle a. (Coercible handle (Ptr handle)) => String -> (handle -> IO ()) -> IO handle -> (handle -> IO a) -> IO a
bracket function release construct = Exception.bracket (construct >>= checked) release
where
checked handle
| coerce handle == (nullPtr :: Ptr handle) = throwIO $ NullHandle function
| otherwise = pure handle
{-# INLINE bracket #-}