    {-# LANGUAGE OverloadedStrings #-}
    {-# LANGUAGE TypeApplications  #-}
    {- |
       Module      : Text.Pandoc.Lua.Engine
       Copyright   : Copyright © 2017-2024 Albert Krewinkel
       License     : GPL-2.0-or-later
       Maintainer  : Albert Krewinkel <albert+pandoc@tarleb.com>

    Running pandoc Lua filters.
    -}
    module Text.Pandoc.Lua.Engine
      ( getEngine
      , applyFilter
      ) where

    import Control.Exception (throw)
    import Control.Monad ((>=>))
    import Control.Monad.IO.Class (MonadIO (liftIO))
    import HsLua.Core (getglobal, openlibs, run, top, tostring)
    import Text.Pandoc.Class (PandocMonad)
    import Text.Pandoc.Definition (Pandoc)
    import Text.Pandoc.Filter (Environment (..))
    import Text.Pandoc.Error (PandocError (PandocFilterError, PandocLuaError))
    import Text.Pandoc.Lua.Custom (loadCustom)
    import Text.Pandoc.Lua.Filter (runFilterFile)
    import Text.Pandoc.Lua.Global (Global (..), setGlobals)
    import Text.Pandoc.Lua.Run (runLua)
    import Text.Pandoc.Lua.Orphans ()
    import Text.Pandoc.Scripting (ScriptingEngine (..))
    import qualified Text.Pandoc.UTF8 as UTF8
    import qualified Data.Text as T

    -- | Constructs the Lua scripting engine.
    getEngine :: MonadIO m => m ScriptingEngine
    getEngine :: forall (m :: * -> *). MonadIO m => m ScriptingEngine
    getEngine = do
      versionName <- IO (Maybe ByteString) -> m (Maybe ByteString)
    forall a. IO a -> m a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO (Maybe ByteString) -> m (Maybe ByteString))
    -> (LuaE PandocError (Maybe ByteString) -> IO (Maybe ByteString))
    -> LuaE PandocError (Maybe ByteString)
    -> m (Maybe ByteString)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . forall e a. LuaE e a -> IO a
    run @PandocError (LuaE PandocError (Maybe ByteString) -> m (Maybe ByteString))
    -> LuaE PandocError (Maybe ByteString) -> m (Maybe ByteString)
    forall a b. (a -> b) -> a -> b
    $ do
        LuaE PandocError ()
    forall e. LuaE e ()
    openlibs
        Name -> LuaE PandocError Type
    forall e. LuaError e => Name -> LuaE e Type
    getglobal Name
    "_VERSION"
        StackIndex -> LuaE PandocError (Maybe ByteString)
    forall e. StackIndex -> LuaE e (Maybe ByteString)
    tostring StackIndex
    top
      pure $ ScriptingEngine
        { engineName = maybe "Lua (unknown version)" UTF8.toText versionName
        , engineApplyFilter = applyFilter
        , engineLoadCustom = loadCustom
        }

    -- | Run the Lua filter in @filterPath@ for a transformation to the
    -- target format (first element in args). Pandoc uses Lua init files to
    -- setup the Lua interpreter.
    applyFilter :: (PandocMonad m, MonadIO m)
                => Environment
                -> [String]
                -> FilePath
                -> Pandoc
                -> m Pandoc
    applyFilter :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    Environment -> [String] -> String -> Pandoc -> m Pandoc
    applyFilter Environment
    fenv [String]
    args String
    fp Pandoc
    doc = do
      let globals :: [Global]
    globals = [ Text -> Global
    FORMAT (Text -> Global) -> Text -> Global
    forall a b. (a -> b) -> a -> b
    $ case [String]
    args of
                        String
    x:[String]
    _ -> String -> Text
    T.pack String
    x
                        [String]
    _   -> Text
    ""
                    , ReaderOptions -> Global
    PANDOC_READER_OPTIONS (Environment -> ReaderOptions
    envReaderOptions Environment
    fenv)
                    , WriterOptions -> Global
    PANDOC_WRITER_OPTIONS (Environment -> WriterOptions
    envWriterOptions Environment
    fenv)
                    , String -> Global
    PANDOC_SCRIPT_FILE String
    fp
                    ]
      LuaE PandocError Pandoc -> m (Either PandocError Pandoc)
    forall (m :: * -> *) a.
    (PandocMonad m, MonadIO m) =>
    LuaE PandocError a -> m (Either PandocError a)
    runLua (LuaE PandocError Pandoc -> m (Either PandocError Pandoc))
    -> (Either PandocError Pandoc -> m Pandoc)
    -> LuaE PandocError Pandoc
    -> m Pandoc
    forall (m :: * -> *) a b c.
    Monad m =>
    (a -> m b) -> (b -> m c) -> a -> m c
    >=> String -> Either PandocError Pandoc -> m Pandoc
    forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String -> Either PandocError Pandoc -> m Pandoc
    forceResult String
    fp (LuaE PandocError Pandoc -> m Pandoc)
    -> LuaE PandocError Pandoc -> m Pandoc
    forall a b. (a -> b) -> a -> b
    $ do
        [Global] -> LuaE PandocError ()
    setGlobals [Global]
    globals
        String -> Pandoc -> LuaE PandocError Pandoc
    runFilterFile String
    fp Pandoc
    doc

    forceResult :: (PandocMonad m, MonadIO m)
                => FilePath -> Either PandocError Pandoc -> m Pandoc
    forceResult :: forall (m :: * -> *).
    (PandocMonad m, MonadIO m) =>
    String -> Either PandocError Pandoc -> m Pandoc
    forceResult String
    fp Either PandocError Pandoc
    eitherResult = case Either PandocError Pandoc
    eitherResult of
      Right Pandoc
    x  -> Pandoc -> m Pandoc
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Pandoc
    x
      Left PandocError
    err -> PandocError -> m Pandoc
    forall a e. (HasCallStack, Exception e) => e -> a
    throw (PandocError -> m Pandoc)
    -> (Text -> PandocError) -> Text -> m Pandoc
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Text -> PandocError
    PandocFilterError (String -> Text
    T.pack String
    fp) (Text -> m Pandoc) -> Text -> m Pandoc
    forall a b. (a -> b) -> a -> b
    $ case PandocError
    err of
        PandocLuaError Text
    msg -> Text
    msg
        PandocError
    _                  -> String -> Text
    T.pack (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ PandocError -> String
    forall a. Show a => a -> String
    show PandocError
    err