    {-# LANGUAGE OverloadedStrings   #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE TypeApplications    #-}
    {-|
    Module      : HsLua.Marshalling.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 use Haskell values as Lua userdata.
    -}
    module HsLua.Marshalling.Userdata
      ( pushIterator
      ) where

    import Control.Monad (void)
    import HsLua.Core as Lua

    -- | Pushes three values to the stack that can be used in a generic for
    -- loop to lazily iterate over all values in the list. Keeps the
    -- remaining list in a userdata state.
    --
    -- If the values pusher function returns @'NumResults' 0@ for a list
    -- item, then this item will be skipped and the values for the next item
    -- will be pushed.
    pushIterator :: forall a e. LuaError e
                 => (a -> LuaE e NumResults)  -- ^ pusher for the values
                 -> [a]                       -- ^ list to iterate over lazily
                 -> LuaE e NumResults
    pushIterator :: forall a e.
    LuaError e =>
    (a -> LuaE e NumResults) -> [a] -> LuaE e NumResults
    pushIterator a -> LuaE e NumResults
    pushValues [a]
    xs = do
      -- push initial state
      LuaE e NumResults -> LuaE e ()
    forall e. LuaError e => HaskellFunction e -> LuaE e ()
    pushHaskellFunction LuaE e NumResults
    nextItem
      LuaE e ()
    pushInitialState
      LuaE e ()
    forall e. LuaE e ()
    pushnil
      NumResults -> LuaE e NumResults
    forall a. a -> LuaE e a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (CInt -> NumResults
    NumResults CInt
    3)
      where
        nextItem :: LuaE e NumResults
        nextItem :: LuaE e NumResults
    nextItem = do
          props <- forall a e. StackIndex -> Name -> LuaE e (Maybe a)
    fromuserdata @[a] (CInt -> StackIndex
    nthBottom CInt
    1) Name
    statename
          case props of
            Maybe [a]
    Nothing -> String -> LuaE e NumResults
    forall e a. LuaError e => String -> LuaE e a
    failLua
              String
    "Error in iterator: could not retrieve iterator state."
            Just [] -> NumResults
    2 NumResults -> LuaE e () -> LuaE e NumResults
    forall a b. a -> LuaE e b -> LuaE e a
    forall (f :: * -> *) a b. Functor f => a -> f b -> f a
    <$ (LuaE e ()
    forall e. LuaE e ()
    pushnil LuaE e () -> LuaE e () -> LuaE e ()
    forall a b. LuaE e a -> LuaE e b -> LuaE e b
    forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
    *> LuaE e ()
    forall e. LuaE e ()
    pushnil)  -- end loop
            Just (a
    y:[a]
    ys) -> do
              success <- forall a e. StackIndex -> Name -> a -> LuaE e Bool
    putuserdata @[a] (CInt -> StackIndex
    nthBottom CInt
    1) Name
    statename [a]
    ys
              if not success
                then failLua "Error in iterator: could not update iterator state."
                else pushValues y >>= \case
                  NumResults
    0 -> LuaE e NumResults
    nextItem  -- keep going if nothing was pushed
                  NumResults
    n -> NumResults -> LuaE e NumResults
    forall a. a -> LuaE e a
    forall (m :: * -> *) a. Monad m => a -> m a
    return NumResults
    n

        statename :: Name
        statename :: Name
    statename = Name
    "HsLua iterator state"

        pushInitialState :: LuaE e ()
        pushInitialState :: LuaE e ()
    pushInitialState = do
          forall a e. a -> Int -> LuaE e ()
    newhsuserdatauv @[a] [a]
    xs Int
    0
          LuaE e Bool -> LuaE e ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void (Name -> LuaE e Bool
    forall e. Name -> LuaE e Bool
    newudmetatable Name
    statename)
          StackIndex -> LuaE e ()
    forall e. StackIndex -> LuaE e ()
    setmetatable (CInt -> StackIndex
    nth CInt
    2)