{-# LANGUAGE RankNTypes #-}
{-|
Module      : HsLua.Core.Userdata
Copyright   : © 2007–2012 Gracjan Polak;
              © 2012–2016 Ömer Sinan Ağacan;
              © 2017-2024 Albert Krewinkel
License     : MIT
Maintainer  : Albert Krewinkel <tarleb@hslua.org>
Stability   : beta
Portability : non-portable (depends on GHC)

Convenience functions to convert Haskell values into Lua userdata.
-}
module HsLua.Core.Userdata
  ( newhsuserdatauv
  , newudmetatable
  , fromuserdata
  , putuserdata
  ) where

import HsLua.Core.Types (LuaE, Name (..), StackIndex, liftLua, fromLuaBool)
import Lua.Userdata
  ( hslua_fromuserdata
  , hslua_newhsuserdatauv
  , hslua_newudmetatable
  , hslua_putuserdata
  )
import qualified Data.ByteString as B

-- | Creates a new userdata wrapping the given Haskell object. The
-- userdata is pushed to the top of the stack.
newhsuserdatauv :: forall a e. a -- ^ Haskell object
                -> Int           -- ^ number of extra userdata values
                -> LuaE e ()
newhsuserdatauv :: forall a e. a -> Int -> LuaE e ()
newhsuserdatauv a
x Int
nuvalue = (State -> IO ()) -> LuaE e ()
forall a e. (State -> IO a) -> LuaE e a
liftLua ((State -> IO ()) -> LuaE e ()) -> (State -> IO ()) -> LuaE e ()
forall a b. (a -> b) -> a -> b
$ \State
l ->
  State -> a -> CInt -> IO ()
forall a. State -> a -> CInt -> IO ()
hslua_newhsuserdatauv State
l a
x (Int -> CInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
nuvalue)
{-# INLINABLE newhsuserdatauv #-}

-- | Creates and registers a new metatable for a userdata-wrapped
-- Haskell value; checks whether a metatable of that name has been
-- registered yet and uses the registered table if possible.
--
-- Returns 'True' if a new metatable was created, and 'False' otherwise.
--
-- Using a metatable created by this functions ensures that the pointer
-- to the Haskell value will be freed when the userdata object is
-- garbage collected in Lua.
--
-- The name may not contain a nul character.
newudmetatable :: Name -> LuaE e Bool
newudmetatable :: forall e. Name -> LuaE e Bool
newudmetatable (Name ByteString
name) = (State -> IO Bool) -> LuaE e Bool
forall a e. (State -> IO a) -> LuaE e a
liftLua ((State -> IO Bool) -> LuaE e Bool)
-> (State -> IO Bool) -> LuaE e Bool
forall a b. (a -> b) -> a -> b
$ \State
l ->
  ByteString -> (CString -> IO Bool) -> IO Bool
forall a. ByteString -> (CString -> IO a) -> IO a
B.useAsCString ByteString
name ((LuaBool -> Bool) -> IO LuaBool -> IO Bool
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap LuaBool -> Bool
fromLuaBool (IO LuaBool -> IO Bool)
-> (CString -> IO LuaBool) -> CString -> IO Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. State -> CString -> IO LuaBool
hslua_newudmetatable State
l)
{-# INLINABLE newudmetatable #-}

-- | Retrieves a Haskell object from userdata at the given index. The
-- userdata /must/ have the given name.
fromuserdata :: forall a e.
                StackIndex  -- ^ stack index of userdata
             -> Name        -- ^ expected name of userdata object
             -> LuaE e (Maybe a)
fromuserdata :: forall a e. StackIndex -> Name -> LuaE e (Maybe a)
fromuserdata StackIndex
idx (Name ByteString
name) = (State -> IO (Maybe a)) -> LuaE e (Maybe a)
forall a e. (State -> IO a) -> LuaE e a
liftLua ((State -> IO (Maybe a)) -> LuaE e (Maybe a))
-> (State -> IO (Maybe a)) -> LuaE e (Maybe a)
forall a b. (a -> b) -> a -> b
$ \State
l ->
  ByteString -> (CString -> IO (Maybe a)) -> IO (Maybe a)
forall a. ByteString -> (CString -> IO a) -> IO a
B.useAsCString ByteString
name (State -> StackIndex -> CString -> IO (Maybe a)
forall a. State -> StackIndex -> CString -> IO (Maybe a)
hslua_fromuserdata State
l StackIndex
idx)
{-# INLINABLE fromuserdata #-}

-- | Replaces the Haskell value contained in the userdata value at
-- @index@. Checks that the userdata is of type @name@ and returns
-- 'True' on success, or 'False' otherwise.
putuserdata :: forall a e.
               StackIndex   -- ^ index
            -> Name         -- ^ name
            -> a            -- ^ new value
            -> LuaE e Bool
putuserdata :: forall a e. StackIndex -> Name -> a -> LuaE e Bool
putuserdata StackIndex
idx (Name ByteString
name) a
x = (State -> IO Bool) -> LuaE e Bool
forall a e. (State -> IO a) -> LuaE e a
liftLua ((State -> IO Bool) -> LuaE e Bool)
-> (State -> IO Bool) -> LuaE e Bool
forall a b. (a -> b) -> a -> b
$ \State
l ->
  ByteString -> (CString -> IO Bool) -> IO Bool
forall a. ByteString -> (CString -> IO a) -> IO a
B.useAsCString ByteString
name ((CString -> IO Bool) -> IO Bool)
-> (CString -> IO Bool) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \CString
namePtr ->
  State -> StackIndex -> CString -> a -> IO Bool
forall a. State -> StackIndex -> CString -> a -> IO Bool
hslua_putuserdata State
l StackIndex
idx CString
namePtr a
x
{-# INLINABLE putuserdata #-}