{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE IncoherentInstances  #-}
{-# LANGUAGE OverloadedStrings    #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{- |
Module      : Text.Pandoc.Lua.Filter
Copyright   : © 2012-2023 John MacFarlane,
              © 2017-2023 Albert Krewinkel
License     : GNU GPL, version 2 or above
Maintainer  : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
Stability   : alpha

Types and functions for running Lua filters.
-}
module Text.Pandoc.Lua.Filter
  ( applyFilter
  ) where
import Control.Monad ((>=>), (<$!>))
import HsLua as Lua
import Text.Pandoc.Definition
import Text.Pandoc.Filter (Environment (..))
import Text.Pandoc.Lua.Marshal.AST
import Text.Pandoc.Lua.Marshal.Filter
import Text.Pandoc.Lua.Global (Global (..), setGlobals)
import Text.Pandoc.Lua.Init (runLua)
import Text.Pandoc.Lua.PandocLua ()
import Control.Exception (throw)
import qualified Data.Text as T
import Text.Pandoc.Class (PandocMonad)
import Control.Monad.Trans (MonadIO)
import Text.Pandoc.Error (PandocError (PandocFilterError, PandocLuaError))

-- | Transform document using the filter defined in the given file.
runFilterFile :: FilePath -> Pandoc -> LuaE PandocError Pandoc
runFilterFile :: FilePath -> Pandoc -> LuaE PandocError Pandoc
runFilterFile FilePath
filterPath Pandoc
doc = do
  StackIndex
oldtop <- LuaE PandocError StackIndex
forall e. LuaE e StackIndex
gettop
  Status
stat <- Maybe FilePath -> LuaE PandocError Status
forall e. Maybe FilePath -> LuaE e Status
dofileTrace (FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just FilePath
filterPath)
  if Status
stat Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
/= Status
Lua.OK
    then LuaE PandocError Pandoc
forall e a. LuaError e => LuaE e a
throwErrorAsException
    else do
      StackIndex
newtop <- LuaE PandocError StackIndex
forall e. LuaE e StackIndex
gettop
      -- Use the returned filters, or the implicitly defined global
      -- filter if nothing was returned.
      [Filter]
luaFilters <- Peek PandocError [Filter] -> LuaE PandocError [Filter]
forall e a. LuaError e => Peek e a -> LuaE e a
forcePeek (Peek PandocError [Filter] -> LuaE PandocError [Filter])
-> Peek PandocError [Filter] -> LuaE PandocError [Filter]
forall a b. (a -> b) -> a -> b
$
        if StackIndex
newtop StackIndex -> StackIndex -> StackIndex
forall a. Num a => a -> a -> a
- StackIndex
oldtop StackIndex -> StackIndex -> Bool
forall a. Ord a => a -> a -> Bool
>= StackIndex
1
        then Peeker PandocError Filter -> Peeker PandocError [Filter]
forall a e. LuaError e => Peeker e a -> Peeker e [a]
peekList Peeker PandocError Filter
forall e. LuaError e => Peeker e Filter
peekFilter StackIndex
top
        else (Filter -> [Filter] -> [Filter]
forall a. a -> [a] -> [a]
:[]) (Filter -> [Filter])
-> Peek PandocError Filter -> Peek PandocError [Filter]
forall (m :: * -> *) a b. Monad m => (a -> b) -> m a -> m b
<$!> (LuaE PandocError () -> Peek PandocError ()
forall e a. LuaE e a -> Peek e a
liftLua LuaE PandocError ()
forall e. LuaE e ()
pushglobaltable Peek PandocError ()
-> Peek PandocError Filter -> Peek PandocError Filter
forall a b.
Peek PandocError a -> Peek PandocError b -> Peek PandocError b
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Peeker PandocError Filter
forall e. LuaError e => Peeker e Filter
peekFilter StackIndex
top)
      StackIndex -> LuaE PandocError ()
forall e. StackIndex -> LuaE e ()
settop StackIndex
oldtop
      [Filter] -> Pandoc -> LuaE PandocError Pandoc
runAll [Filter]
luaFilters Pandoc
doc

runAll :: [Filter] -> Pandoc -> LuaE PandocError Pandoc
runAll :: [Filter] -> Pandoc -> LuaE PandocError Pandoc
runAll = (Filter
 -> (Pandoc -> LuaE PandocError Pandoc)
 -> Pandoc
 -> LuaE PandocError Pandoc)
-> (Pandoc -> LuaE PandocError Pandoc)
-> [Filter]
-> Pandoc
-> LuaE PandocError Pandoc
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ((Pandoc -> LuaE PandocError Pandoc)
-> (Pandoc -> LuaE PandocError Pandoc)
-> Pandoc
-> LuaE PandocError Pandoc
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
(>=>) ((Pandoc -> LuaE PandocError Pandoc)
 -> (Pandoc -> LuaE PandocError Pandoc)
 -> Pandoc
 -> LuaE PandocError Pandoc)
-> (Filter -> Pandoc -> LuaE PandocError Pandoc)
-> Filter
-> (Pandoc -> LuaE PandocError Pandoc)
-> Pandoc
-> LuaE PandocError Pandoc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Filter -> Pandoc -> LuaE PandocError Pandoc
forall e. LuaError e => Filter -> Pandoc -> LuaE e Pandoc
applyFully) Pandoc -> LuaE PandocError Pandoc
forall a. a -> LuaE PandocError a
forall (m :: * -> *) a. Monad m => a -> m a
return

-- | 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 -> [FilePath] -> FilePath -> Pandoc -> m Pandoc
applyFilter Environment
fenv [FilePath]
args FilePath
fp Pandoc
doc = do
  let globals :: [Global]
globals = [ Text -> Global
FORMAT (Text -> Global) -> Text -> Global
forall a b. (a -> b) -> a -> b
$ case [FilePath]
args of
                    FilePath
x:[FilePath]
_ -> FilePath -> Text
T.pack FilePath
x
                    [FilePath]
_   -> Text
""
                , ReaderOptions -> Global
PANDOC_READER_OPTIONS (Environment -> ReaderOptions
envReaderOptions Environment
fenv)
                , WriterOptions -> Global
PANDOC_WRITER_OPTIONS (Environment -> WriterOptions
envWriterOptions Environment
fenv)
                , FilePath -> Global
PANDOC_SCRIPT_FILE FilePath
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
>=> FilePath -> Either PandocError Pandoc -> m Pandoc
forall (m :: * -> *).
(PandocMonad m, MonadIO m) =>
FilePath -> Either PandocError Pandoc -> m Pandoc
forceResult FilePath
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
    FilePath -> Pandoc -> LuaE PandocError Pandoc
runFilterFile FilePath
fp Pandoc
doc

forceResult :: (PandocMonad m, MonadIO m)
            => FilePath -> Either PandocError Pandoc -> m Pandoc
forceResult :: forall (m :: * -> *).
(PandocMonad m, MonadIO m) =>
FilePath -> Either PandocError Pandoc -> m Pandoc
forceResult FilePath
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. 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 (FilePath -> Text
T.pack FilePath
fp) (Text -> m Pandoc) -> Text -> m Pandoc
forall a b. (a -> b) -> a -> b
$ case PandocError
err of
    PandocLuaError Text
msg -> Text
msg
    PandocError
_                  -> FilePath -> Text
T.pack (FilePath -> Text) -> FilePath -> Text
forall a b. (a -> b) -> a -> b
$ PandocError -> FilePath
forall a. Show a => a -> FilePath
show PandocError
err