    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {- |
       Module      : Text.Pandoc.Lua.Packages
       Copyright   : Copyright © 2017-2020 Albert Krewinkel
       License     : GNU GPL, version 2 or above

       Maintainer  : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
       Stability   : alpha

    Pandoc module for lua.
    -}
    module Text.Pandoc.Lua.Packages
      ( LuaPackageParams (..)
      , installPandocPackageSearcher
      ) where

    import Control.Monad (forM_)
    import Data.ByteString (ByteString)
    import Foreign.Lua (Lua, NumResults, liftIO)
    import Text.Pandoc.Class.PandocIO (runIO)
    import Text.Pandoc.Class.PandocMonad (readDataFile, setUserDataDir)

    import qualified Foreign.Lua as Lua
    import Text.Pandoc.Lua.Module.Pandoc as Pandoc
    import Text.Pandoc.Lua.Module.MediaBag as MediaBag
    import Text.Pandoc.Lua.Module.System as System
    import Text.Pandoc.Lua.Module.Types as Types
    import Text.Pandoc.Lua.Module.Utils as Utils

    -- | Parameters used to create lua packages/modules.
    data LuaPackageParams = LuaPackageParams
      { LuaPackageParams -> Maybe FilePath
    luaPkgDataDir :: Maybe FilePath
      }

    -- | Insert pandoc's package loader as the first loader, making it the default.
    installPandocPackageSearcher :: LuaPackageParams -> Lua ()
    installPandocPackageSearcher :: LuaPackageParams -> Lua ()
    installPandocPackageSearcher luaPkgParams :: LuaPackageParams
    luaPkgParams = do
      FilePath -> Lua ()
    Lua.getglobal' "package.searchers"
      Lua ()
    shiftArray
      (FilePath -> Lua NumResults) -> Lua ()
    forall a. ToHaskellFunction a => a -> Lua ()
    Lua.pushHaskellFunction (LuaPackageParams -> FilePath -> Lua NumResults
    pandocPackageSearcher LuaPackageParams
    luaPkgParams)
      StackIndex -> Integer -> Lua ()
    Lua.rawseti (CInt -> StackIndex
    Lua.nthFromTop 2) 1
      StackIndex -> Lua ()
    Lua.pop 1           -- remove 'package.searchers' from stack
     where
      shiftArray :: Lua ()
    shiftArray = [Integer] -> (Integer -> Lua ()) -> Lua ()
    forall (t :: * -> *) (m :: * -> *) a b.
    (Foldable t, Monad m) =>
    t a -> (a -> m b) -> m ()
    forM_ [4, 3, 2, 1] ((Integer -> Lua ()) -> Lua ()) -> (Integer -> Lua ()) -> Lua ()
    forall a b. (a -> b) -> a -> b
    $ \i :: Integer
    i -> do
        StackIndex -> Integer -> Lua ()
    Lua.rawgeti (-1) Integer
    i
        StackIndex -> Integer -> Lua ()
    Lua.rawseti (-2) (Integer
    i Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    + 1)

    -- | Load a pandoc module.
    pandocPackageSearcher :: LuaPackageParams -> String -> Lua NumResults
    pandocPackageSearcher :: LuaPackageParams -> FilePath -> Lua NumResults
    pandocPackageSearcher pkgParams :: LuaPackageParams
    pkgParams pkgName :: FilePath
    pkgName =
      case FilePath
    pkgName of
        "pandoc"          -> let datadir :: Maybe FilePath
    datadir = LuaPackageParams -> Maybe FilePath
    luaPkgDataDir LuaPackageParams
    pkgParams
                             in Lua NumResults -> Lua NumResults
    forall a b. (ToHaskellFunction a, Num b) => a -> Lua b
    pushWrappedHsFun (Maybe FilePath -> Lua NumResults
    Pandoc.pushModule Maybe FilePath
    datadir)
        "pandoc.mediabag" -> Lua NumResults -> Lua NumResults
    forall a b. (ToHaskellFunction a, Num b) => a -> Lua b
    pushWrappedHsFun Lua NumResults
    MediaBag.pushModule
        "pandoc.system"   -> Lua NumResults -> Lua NumResults
    forall a b. (ToHaskellFunction a, Num b) => a -> Lua b
    pushWrappedHsFun Lua NumResults
    System.pushModule
        "pandoc.types"    -> Lua NumResults -> Lua NumResults
    forall a b. (ToHaskellFunction a, Num b) => a -> Lua b
    pushWrappedHsFun Lua NumResults
    Types.pushModule
        "pandoc.utils"    -> let datadir :: Maybe FilePath
    datadir = LuaPackageParams -> Maybe FilePath
    luaPkgDataDir LuaPackageParams
    pkgParams
                             in Lua NumResults -> Lua NumResults
    forall a b. (ToHaskellFunction a, Num b) => a -> Lua b
    pushWrappedHsFun (Maybe FilePath -> Lua NumResults
    Utils.pushModule Maybe FilePath
    datadir)
        _ -> Lua NumResults
    searchPureLuaLoader
     where
      pushWrappedHsFun :: a -> Lua b
    pushWrappedHsFun f :: a
    f = do
        a -> Lua ()
    forall a. ToHaskellFunction a => a -> Lua ()
    Lua.pushHaskellFunction a
    f
        b -> Lua b
    forall (m :: * -> *) a. Monad m => a -> m a
    return 1
      searchPureLuaLoader :: Lua NumResults
    searchPureLuaLoader = do
        let filename :: FilePath
    filename = FilePath
    pkgName FilePath -> FilePath -> FilePath
    forall a. [a] -> [a] -> [a]
    ++ ".lua"
        Maybe ByteString
    modScript <- IO (Maybe ByteString) -> Lua (Maybe ByteString)
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (Maybe FilePath -> FilePath -> IO (Maybe ByteString)
    dataDirScript (LuaPackageParams -> Maybe FilePath
    luaPkgDataDir LuaPackageParams
    pkgParams) FilePath
    filename)
        case Maybe ByteString
    modScript of
          Just script :: ByteString
    script -> Lua NumResults -> Lua NumResults
    forall a b. (ToHaskellFunction a, Num b) => a -> Lua b
    pushWrappedHsFun (FilePath -> ByteString -> Lua NumResults
    loadStringAsPackage FilePath
    pkgName ByteString
    script)
          Nothing -> do
            FilePath -> Lua ()
    forall a. Pushable a => a -> Lua ()
    Lua.push ("\n\tno file '" FilePath -> FilePath -> FilePath
    forall a. [a] -> [a] -> [a]
    ++ FilePath
    filename FilePath -> FilePath -> FilePath
    forall a. [a] -> [a] -> [a]
    ++ "' in pandoc's datadir")
            NumResults -> Lua NumResults
    forall (m :: * -> *) a. Monad m => a -> m a
    return 1

    loadStringAsPackage :: String -> ByteString -> Lua NumResults
    loadStringAsPackage :: FilePath -> ByteString -> Lua NumResults
    loadStringAsPackage pkgName :: FilePath
    pkgName script :: ByteString
    script = do
      Status
    status <- ByteString -> Lua Status
    Lua.dostring ByteString
    script
      if Status
    status Status -> Status -> Bool
    forall a. Eq a => a -> a -> Bool
    == Status
    Lua.OK
        then NumResults -> Lua NumResults
    forall (m :: * -> *) a. Monad m => a -> m a
    return (1 :: NumResults)
        else do
          FilePath
    msg <- Lua FilePath
    forall a. Peekable a => Lua a
    Lua.popValue
          FilePath -> Lua NumResults
    forall a. Pushable a => a -> Lua NumResults
    Lua.raiseError ("Error while loading `" FilePath -> FilePath -> FilePath
    forall a. Semigroup a => a -> a -> a
    <> FilePath
    pkgName FilePath -> FilePath -> FilePath
    forall a. Semigroup a => a -> a -> a
    <> "`.\n" FilePath -> FilePath -> FilePath
    forall a. Semigroup a => a -> a -> a
    <> FilePath
    msg)

    -- | Get the ByteString representation of the pandoc module.
    dataDirScript :: Maybe FilePath -> FilePath -> IO (Maybe ByteString)
    dataDirScript :: Maybe FilePath -> FilePath -> IO (Maybe ByteString)
    dataDirScript datadir :: Maybe FilePath
    datadir moduleFile :: FilePath
    moduleFile = do
      Either PandocError ByteString
    res <- PandocIO ByteString -> IO (Either PandocError ByteString)
    forall a. PandocIO a -> IO (Either PandocError a)
    runIO (PandocIO ByteString -> IO (Either PandocError ByteString))
    -> PandocIO ByteString -> IO (Either PandocError ByteString)
    forall a b. (a -> b) -> a -> b
    $ Maybe FilePath -> PandocIO ()
    forall (m :: * -> *). PandocMonad m => Maybe FilePath -> m ()
    setUserDataDir Maybe FilePath
    datadir PandocIO () -> PandocIO ByteString -> PandocIO ByteString
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> FilePath -> PandocIO ByteString
    forall (m :: * -> *). PandocMonad m => FilePath -> m ByteString
    readDataFile FilePath
    moduleFile
      Maybe ByteString -> IO (Maybe ByteString)
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Maybe ByteString -> IO (Maybe ByteString))
    -> Maybe ByteString -> IO (Maybe ByteString)
    forall a b. (a -> b) -> a -> b
    $ case Either PandocError ByteString
    res of
        Left _ -> Maybe ByteString
    forall a. Maybe a
    Nothing
        Right s :: ByteString
    s -> ByteString -> Maybe ByteString
    forall a. a -> Maybe a
    Just ByteString
    s