{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE FunctionalDependencies #-}

module Hercules.CNix.Encapsulation
  ( HasEncapsulation (..),
    nullableMoveToForeignPtrWrapper,
  )
where

import Data.Coerce (Coercible, coerce)
import Foreign (ForeignPtr, Ptr, newForeignPtr, nullPtr)
import Hercules.CNix.Memory (Finalizer, finalizer)
import Prelude

class HasEncapsulation a b | b -> a where
  -- | Takes ownership of the pointer, freeing/finalizing the pointer when
  -- collectable.
  moveToForeignPtrWrapper :: Ptr a -> IO b
  default moveToForeignPtrWrapper :: (Finalizer a, Coercible b (ForeignPtr a)) => Ptr a -> IO b
  moveToForeignPtrWrapper = IO (ForeignPtr a) -> IO b
forall a b. Coercible a b => a -> b
coerce (IO (ForeignPtr a) -> IO b)
-> (Ptr a -> IO (ForeignPtr a)) -> Ptr a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FinalizerPtr a -> Ptr a -> IO (ForeignPtr a)
forall a. FinalizerPtr a -> Ptr a -> IO (ForeignPtr a)
newForeignPtr FinalizerPtr a
forall a. Finalizer a => FinalizerPtr a
finalizer

nullableMoveToForeignPtrWrapper :: (HasEncapsulation a b) => Ptr a -> IO (Maybe b)
nullableMoveToForeignPtrWrapper :: forall a b. HasEncapsulation a b => Ptr a -> IO (Maybe b)
nullableMoveToForeignPtrWrapper Ptr a
rawPtr | Ptr a
rawPtr Ptr a -> Ptr a -> Bool
forall a. Eq a => a -> a -> Bool
== Ptr a
forall a. Ptr a
nullPtr = Maybe b -> IO (Maybe b)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe b
forall a. Maybe a
Nothing
nullableMoveToForeignPtrWrapper Ptr a
rawPtr = b -> Maybe b
forall a. a -> Maybe a
Just (b -> Maybe b) -> IO b -> IO (Maybe b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Ptr a -> IO b
forall a b. HasEncapsulation a b => Ptr a -> IO b
moveToForeignPtrWrapper Ptr a
rawPtr