    {-|
    Module      : HsLua.Core.Package
    Copyright   : © 2019-2024 Albert Krewinkel
    License     : MIT
    Maintainer  : Albert Krewinkel <tarleb@hslua.org>
    Stability   : alpha
    Portability : Requires GHC 8 or later.

    Utility functions for HsLua modules.
    -}
    module HsLua.Core.Package
      ( requirehs
      , preloadhs
      )
    where

    import Control.Monad (void)
    import HsLua.Core.Auxiliary
    import HsLua.Core.Closures (pushHaskellFunction)
    import HsLua.Core.Error (LuaError)
    import HsLua.Core.Primary
    import HsLua.Core.Types
    -- import HsLua.Core.Utf8 (fromString)

    -- | Load a module, defined by a Haskell action, under the given
    -- name.
    --
    -- Similar to @luaL_requiref@: If @modname@ is not already present in
    -- @package.loaded@, calls function @openf@ with string @modname@ as an
    -- argument and sets the call result in @package.loaded[modname]@, as if
    -- that function has been called through
    -- <https://www.lua.org/manual/5.4/manual.html#pdf-require require>.
    --
    -- Leaves a copy of the module on the stack.
    requirehs :: LuaError e
              => Name                 -- ^ modname
              -> (Name -> LuaE e ())  -- ^ openf
              -> LuaE e ()
    requirehs :: forall e. LuaError e => Name -> (Name -> LuaE e ()) -> LuaE e ()
    requirehs Name
    modname Name -> LuaE e ()
    openf = do
      LuaE e Bool -> LuaE e ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void (LuaE e Bool -> LuaE e ()) -> LuaE e Bool -> LuaE e ()
    forall a b. (a -> b) -> a -> b
    $ StackIndex -> Name -> LuaE e Bool
    forall e. LuaError e => StackIndex -> Name -> LuaE e Bool
    getsubtable StackIndex
    registryindex Name
    loaded
      LuaE e Type -> LuaE e ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void (LuaE e Type -> LuaE e ()) -> LuaE e Type -> LuaE e ()
    forall a b. (a -> b) -> a -> b
    $ StackIndex -> Name -> LuaE e Type
    forall e. LuaError e => StackIndex -> Name -> LuaE e Type
    getfield StackIndex
    top Name
    modname
      StackIndex -> LuaE e Bool
    forall e. StackIndex -> LuaE e Bool
    toboolean StackIndex
    top LuaE e Bool -> (Bool -> LuaE e ()) -> LuaE e ()
    forall a b. LuaE e a -> (a -> LuaE e b) -> LuaE e b
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \case
        Bool
    True -> () -> LuaE e ()
    forall a. a -> LuaE e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure ()       -- package already loaded
        Bool
    False -> do
          -- package not loaded, load it now
          Int -> LuaE e ()
    forall e. Int -> LuaE e ()
    pop Int
    1  -- remove field
          oldtop <- LuaE e StackIndex
    forall e. LuaE e StackIndex
    gettop
          openf modname
          settop (oldtop + 1)
          pushvalue top  -- make copy of module (call result)
          setfield (nth 3) modname

      StackIndex -> LuaE e ()
    forall e. StackIndex -> LuaE e ()
    remove (CInt -> StackIndex
    nth CInt
    2)  -- remove LOADED table

    -- | Registers a preloading function. Takes an module name and the
    -- Lua operation which produces the package.
    preloadhs :: LuaError e => Name -> LuaE e NumResults -> LuaE e ()
    preloadhs :: forall e. LuaError e => Name -> LuaE e NumResults -> LuaE e ()
    preloadhs Name
    name LuaE e NumResults
    pushMod = do
      LuaE e Type -> LuaE e ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void (LuaE e Type -> LuaE e ()) -> LuaE e Type -> LuaE e ()
    forall a b. (a -> b) -> a -> b
    $ StackIndex -> Name -> LuaE e Type
    forall e. LuaError e => StackIndex -> Name -> LuaE e Type
    getfield StackIndex
    registryindex Name
    preload
      LuaE e NumResults -> LuaE e ()
    forall e. LuaError e => HaskellFunction e -> LuaE e ()
    pushHaskellFunction LuaE e NumResults
    pushMod
      StackIndex -> Name -> LuaE e ()
    forall e. LuaError e => StackIndex -> Name -> LuaE e ()
    setfield (CInt -> StackIndex
    nth CInt
    2) Name
    name
      Int -> LuaE e ()
    forall e. Int -> LuaE e ()
    pop Int
    1