    {- |
       Module      : Text.Pandoc.Lua.Module.Utils
       Copyright   : Copyright © 2017-2020 Albert Krewinkel
       License     : GNU GPL, version 2 or above

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

    Utility module for lua, exposing internal helper functions.
    -}
    module Text.Pandoc.Lua.Module.Utils
      ( pushModule
      ) where

    import Control.Applicative ((<|>))
    import Data.Default (def)
    import Data.Version (Version)
    import Foreign.Lua (Peekable, Lua, NumResults)
    import Text.Pandoc.Class.PandocIO (runIO)
    import Text.Pandoc.Class.PandocMonad (setUserDataDir)
    import Text.Pandoc.Definition ( Pandoc, Meta, MetaValue (..), Block, Inline
                                  , Citation, Attr, ListAttributes)
    import Text.Pandoc.Lua.Marshaling ()
    import Text.Pandoc.Lua.Util (addFunction)

    import qualified Data.Digest.Pure.SHA as SHA
    import qualified Data.ByteString.Lazy as BSL
    import qualified Data.Text as T
    import qualified Foreign.Lua as Lua
    import qualified Text.Pandoc.Builder as B
    import qualified Text.Pandoc.Filter.JSON as JSONFilter
    import qualified Text.Pandoc.Shared as Shared

    -- | Push the "pandoc.utils" module to the lua stack.
    pushModule :: Maybe FilePath -> Lua NumResults
    pushModule :: Maybe FilePath -> Lua NumResults
    pushModule mbDatadir :: Maybe FilePath
    mbDatadir = do
      Lua ()
    Lua.newtable
      FilePath
    -> ([Block] -> Optional [Inline] -> Lua [Inline]) -> Lua ()
    forall a. ToHaskellFunction a => FilePath -> a -> Lua ()
    addFunction "blocks_to_inlines" [Block] -> Optional [Inline] -> Lua [Inline]
    blocksToInlines
      FilePath -> (AstElement -> AstElement -> Lua Bool) -> Lua ()
    forall a. ToHaskellFunction a => FilePath -> a -> Lua ()
    addFunction "equals" AstElement -> AstElement -> Lua Bool
    equals
      FilePath
    -> (Bool -> Optional Int -> [Block] -> Lua [Block]) -> Lua ()
    forall a. ToHaskellFunction a => FilePath -> a -> Lua ()
    addFunction "make_sections" Bool -> Optional Int -> [Block] -> Lua [Block]
    makeSections
      FilePath -> (Text -> Lua (Optional Text)) -> Lua ()
    forall a. ToHaskellFunction a => FilePath -> a -> Lua ()
    addFunction "normalize_date" Text -> Lua (Optional Text)
    normalizeDate
      FilePath
    -> (Pandoc -> FilePath -> Optional [FilePath] -> Lua NumResults)
    -> Lua ()
    forall a. ToHaskellFunction a => FilePath -> a -> Lua ()
    addFunction "run_json_filter" (Maybe FilePath
    -> Pandoc -> FilePath -> Optional [FilePath] -> Lua NumResults
    runJSONFilter Maybe FilePath
    mbDatadir)
      FilePath -> (ByteString -> Lua Text) -> Lua ()
    forall a. ToHaskellFunction a => FilePath -> a -> Lua ()
    addFunction "sha1" ByteString -> Lua Text
    sha1
      FilePath -> (AstElement -> Lua Text) -> Lua ()
    forall a. ToHaskellFunction a => FilePath -> a -> Lua ()
    addFunction "stringify" AstElement -> Lua Text
    stringify
      FilePath -> (Integer -> Lua Text) -> Lua ()
    forall a. ToHaskellFunction a => FilePath -> a -> Lua ()
    addFunction "to_roman_numeral" Integer -> Lua Text
    toRomanNumeral
      FilePath -> (Version -> Lua Version) -> Lua ()
    forall a. ToHaskellFunction a => FilePath -> a -> Lua ()
    addFunction "Version" (Version -> Lua Version
    forall (m :: * -> *) a. Monad m => a -> m a
    return :: Version -> Lua Version)
      NumResults -> Lua NumResults
    forall (m :: * -> *) a. Monad m => a -> m a
    return 1

    -- | Squashes a list of blocks into inlines.
    blocksToInlines :: [Block] -> Lua.Optional [Inline] -> Lua [Inline]
    blocksToInlines :: [Block] -> Optional [Inline] -> Lua [Inline]
    blocksToInlines blks :: [Block]
    blks optSep :: Optional [Inline]
    optSep = do
      let sep :: Many Inline
    sep = case Optional [Inline] -> Maybe [Inline]
    forall a. Optional a -> Maybe a
    Lua.fromOptional Optional [Inline]
    optSep of
                  Just x :: [Inline]
    x -> [Inline] -> Many Inline
    forall a. [a] -> Many a
    B.fromList [Inline]
    x
                  Nothing -> Many Inline
    Shared.defaultBlocksSeparator
      [Inline] -> Lua [Inline]
    forall (m :: * -> *) a. Monad m => a -> m a
    return ([Inline] -> Lua [Inline]) -> [Inline] -> Lua [Inline]
    forall a b. (a -> b) -> a -> b
    $ Many Inline -> [Inline]
    forall a. Many a -> [a]
    B.toList (Many Inline -> [Block] -> Many Inline
    Shared.blocksToInlinesWithSep Many Inline
    sep [Block]
    blks)

    -- | Convert list of Pandoc blocks into sections using Divs.
    makeSections :: Bool -> Lua.Optional Int -> [Block] -> Lua [Block]
    makeSections :: Bool -> Optional Int -> [Block] -> Lua [Block]
    makeSections number :: Bool
    number baselevel :: Optional Int
    baselevel =
      [Block] -> Lua [Block]
    forall (m :: * -> *) a. Monad m => a -> m a
    return ([Block] -> Lua [Block])
    -> ([Block] -> [Block]) -> [Block] -> Lua [Block]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Bool -> Maybe Int -> [Block] -> [Block]
    Shared.makeSections Bool
    number (Optional Int -> Maybe Int
    forall a. Optional a -> Maybe a
    Lua.fromOptional Optional Int
    baselevel)

    -- | Parse a date and convert (if possible) to "YYYY-MM-DD" format. We
    -- limit years to the range 1601-9999 (ISO 8601 accepts greater than
    -- or equal to 1583, but MS Word only accepts dates starting 1601).
    -- Returns nil instead of a string if the conversion failed.
    normalizeDate :: T.Text -> Lua (Lua.Optional T.Text)
    normalizeDate :: Text -> Lua (Optional Text)
    normalizeDate = Optional Text -> Lua (Optional Text)
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Optional Text -> Lua (Optional Text))
    -> (Text -> Optional Text) -> Text -> Lua (Optional Text)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Maybe Text -> Optional Text
    forall a. Maybe a -> Optional a
    Lua.Optional (Maybe Text -> Optional Text)
    -> (Text -> Maybe Text) -> Text -> Optional Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Maybe Text
    Shared.normalizeDate

    -- | Run a JSON filter on the given document.
    runJSONFilter :: Maybe FilePath
                  -> Pandoc
                  -> FilePath
                  -> Lua.Optional [String]
                  -> Lua NumResults
    runJSONFilter :: Maybe FilePath
    -> Pandoc -> FilePath -> Optional [FilePath] -> Lua NumResults
    runJSONFilter mbDatadir :: Maybe FilePath
    mbDatadir doc :: Pandoc
    doc filterFile :: FilePath
    filterFile optArgs :: Optional [FilePath]
    optArgs = do
      [FilePath]
    args <- case Optional [FilePath] -> Maybe [FilePath]
    forall a. Optional a -> Maybe a
    Lua.fromOptional Optional [FilePath]
    optArgs of
                Just x :: [FilePath]
    x -> [FilePath] -> Lua [FilePath]
    forall (m :: * -> *) a. Monad m => a -> m a
    return [FilePath]
    x
                Nothing -> do
                  FilePath -> Lua ()
    Lua.getglobal "FORMAT"
                  (FilePath -> [FilePath] -> [FilePath]
    forall a. a -> [a] -> [a]
    :[]) (FilePath -> [FilePath]) -> Lua FilePath -> Lua [FilePath]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Lua FilePath
    forall a. Peekable a => Lua a
    Lua.popValue
      Either PandocError Pandoc
    filterRes <- IO (Either PandocError Pandoc) -> Lua (Either PandocError Pandoc)
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    Lua.liftIO (IO (Either PandocError Pandoc) -> Lua (Either PandocError Pandoc))
    -> (PandocIO Pandoc -> IO (Either PandocError Pandoc))
    -> PandocIO Pandoc
    -> Lua (Either PandocError Pandoc)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . PandocIO Pandoc -> IO (Either PandocError Pandoc)
    forall a. PandocIO a -> IO (Either PandocError a)
    runIO (PandocIO Pandoc -> Lua (Either PandocError Pandoc))
    -> PandocIO Pandoc -> Lua (Either PandocError Pandoc)
    forall a b. (a -> b) -> a -> b
    $ do
        Maybe FilePath -> PandocIO ()
    forall (m :: * -> *). PandocMonad m => Maybe FilePath -> m ()
    setUserDataDir Maybe FilePath
    mbDatadir
        ReaderOptions
    -> [FilePath] -> FilePath -> Pandoc -> PandocIO Pandoc
    JSONFilter.apply ReaderOptions
    forall a. Default a => a
    def [FilePath]
    args FilePath
    filterFile Pandoc
    doc
      case Either PandocError Pandoc
    filterRes of
        Left err :: PandocError
    err -> FilePath -> Lua NumResults
    forall a. Pushable a => a -> Lua NumResults
    Lua.raiseError (PandocError -> FilePath
    forall a. Show a => a -> FilePath
    show PandocError
    err)
        Right d :: Pandoc
    d -> (1 :: NumResults) NumResults -> Lua () -> Lua NumResults
    forall (f :: * -> *) a b. Functor f => a -> f b -> f a
    <$ Pandoc -> Lua ()
    forall a. Pushable a => a -> Lua ()
    Lua.push Pandoc
    d

    -- | Calculate the hash of the given contents.
    sha1 :: BSL.ByteString
         -> Lua T.Text
    sha1 :: ByteString -> Lua Text
    sha1 = Text -> Lua Text
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Text -> Lua Text)
    -> (ByteString -> Text) -> ByteString -> Lua Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FilePath -> Text
    T.pack (FilePath -> Text)
    -> (ByteString -> FilePath) -> ByteString -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Digest SHA1State -> FilePath
    forall t. Digest t -> FilePath
    SHA.showDigest (Digest SHA1State -> FilePath)
    -> (ByteString -> Digest SHA1State) -> ByteString -> FilePath
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ByteString -> Digest SHA1State
    SHA.sha1

    -- | Convert pandoc structure to a string with formatting removed.
    -- Footnotes are skipped (since we don't want their contents in link
    -- labels).
    stringify :: AstElement -> Lua T.Text
    stringify :: AstElement -> Lua Text
    stringify el :: AstElement
    el = Text -> Lua Text
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Text -> Lua Text) -> Text -> Lua Text
    forall a b. (a -> b) -> a -> b
    $ case AstElement
    el of
      PandocElement pd :: Pandoc
    pd -> Pandoc -> Text
    forall a. Walkable Inline a => a -> Text
    Shared.stringify Pandoc
    pd
      InlineElement i :: Inline
    i  -> Inline -> Text
    forall a. Walkable Inline a => a -> Text
    Shared.stringify Inline
    i
      BlockElement b :: Block
    b   -> Block -> Text
    forall a. Walkable Inline a => a -> Text
    Shared.stringify Block
    b
      MetaElement m :: Meta
    m    -> Meta -> Text
    forall a. Walkable Inline a => a -> Text
    Shared.stringify Meta
    m
      CitationElement c :: Citation
    c  -> Citation -> Text
    forall a. Walkable Inline a => a -> Text
    Shared.stringify Citation
    c
      MetaValueElement m :: MetaValue
    m -> MetaValue -> Text
    stringifyMetaValue MetaValue
    m
      _                  -> Text
    forall a. Monoid a => a
    mempty

    stringifyMetaValue :: MetaValue -> T.Text
    stringifyMetaValue :: MetaValue -> Text
    stringifyMetaValue mv :: MetaValue
    mv = case MetaValue
    mv of
      MetaBool b :: Bool
    b   -> Text -> Text
    T.toLower (Text -> Text) -> Text -> Text
    forall a b. (a -> b) -> a -> b
    $ FilePath -> Text
    T.pack (Bool -> FilePath
    forall a. Show a => a -> FilePath
    show Bool
    b)
      MetaString s :: Text
    s -> Text
    s
      _            -> MetaValue -> Text
    forall a. Walkable Inline a => a -> Text
    Shared.stringify MetaValue
    mv

    equals :: AstElement -> AstElement -> Lua Bool
    equals :: AstElement -> AstElement -> Lua Bool
    equals e1 :: AstElement
    e1 e2 :: AstElement
    e2 = Bool -> Lua Bool
    forall (m :: * -> *) a. Monad m => a -> m a
    return (AstElement
    e1 AstElement -> AstElement -> Bool
    forall a. Eq a => a -> a -> Bool
    == AstElement
    e2)

    data AstElement
      = PandocElement Pandoc
      | MetaElement Meta
      | BlockElement Block
      | InlineElement Inline
      | MetaValueElement MetaValue
      | AttrElement Attr
      | ListAttributesElement ListAttributes
      | CitationElement Citation
      deriving (AstElement -> AstElement -> Bool
    (AstElement -> AstElement -> Bool)
    -> (AstElement -> AstElement -> Bool) -> Eq AstElement
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    /= :: AstElement -> AstElement -> Bool
    $c/= :: AstElement -> AstElement -> Bool
    == :: AstElement -> AstElement -> Bool
    $c== :: AstElement -> AstElement -> Bool
    Eq, Int -> AstElement -> ShowS
    [AstElement] -> ShowS
    AstElement -> FilePath
    (Int -> AstElement -> ShowS)
    -> (AstElement -> FilePath)
    -> ([AstElement] -> ShowS)
    -> Show AstElement
    forall a.
    (Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
    showList :: [AstElement] -> ShowS
    $cshowList :: [AstElement] -> ShowS
    show :: AstElement -> FilePath
    $cshow :: AstElement -> FilePath
    showsPrec :: Int -> AstElement -> ShowS
    $cshowsPrec :: Int -> AstElement -> ShowS
    Show)

    instance Peekable AstElement where
      peek :: StackIndex -> Lua AstElement
    peek idx :: StackIndex
    idx  = do
        Either Exception AstElement
    res <- Lua AstElement -> Lua (Either Exception AstElement)
    forall a. Lua a -> Lua (Either Exception a)
    Lua.try (Lua AstElement -> Lua (Either Exception AstElement))
    -> Lua AstElement -> Lua (Either Exception AstElement)
    forall a b. (a -> b) -> a -> b
    $  (Pandoc -> AstElement
    PandocElement (Pandoc -> AstElement) -> Lua Pandoc -> Lua AstElement
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> StackIndex -> Lua Pandoc
    forall a. Peekable a => StackIndex -> Lua a
    Lua.peek StackIndex
    idx)
                      Lua AstElement -> Lua AstElement -> Lua AstElement
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> (Inline -> AstElement
    InlineElement (Inline -> AstElement) -> Lua Inline -> Lua AstElement
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> StackIndex -> Lua Inline
    forall a. Peekable a => StackIndex -> Lua a
    Lua.peek StackIndex
    idx)
                      Lua AstElement -> Lua AstElement -> Lua AstElement
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> (Block -> AstElement
    BlockElement (Block -> AstElement) -> Lua Block -> Lua AstElement
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> StackIndex -> Lua Block
    forall a. Peekable a => StackIndex -> Lua a
    Lua.peek StackIndex
    idx)
                      Lua AstElement -> Lua AstElement -> Lua AstElement
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> (Attr -> AstElement
    AttrElement (Attr -> AstElement) -> Lua Attr -> Lua AstElement
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> StackIndex -> Lua Attr
    forall a. Peekable a => StackIndex -> Lua a
    Lua.peek StackIndex
    idx)
                      Lua AstElement -> Lua AstElement -> Lua AstElement
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> (ListAttributes -> AstElement
    ListAttributesElement (ListAttributes -> AstElement)
    -> Lua ListAttributes -> Lua AstElement
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> StackIndex -> Lua ListAttributes
    forall a. Peekable a => StackIndex -> Lua a
    Lua.peek StackIndex
    idx)
                      Lua AstElement -> Lua AstElement -> Lua AstElement
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> (Meta -> AstElement
    MetaElement (Meta -> AstElement) -> Lua Meta -> Lua AstElement
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> StackIndex -> Lua Meta
    forall a. Peekable a => StackIndex -> Lua a
    Lua.peek StackIndex
    idx)
                      Lua AstElement -> Lua AstElement -> Lua AstElement
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
    <|> (MetaValue -> AstElement
    MetaValueElement (MetaValue -> AstElement) -> Lua MetaValue -> Lua AstElement
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> StackIndex -> Lua MetaValue
    forall a. Peekable a => StackIndex -> Lua a
    Lua.peek StackIndex
    idx)
        case Either Exception AstElement
    res of
          Right x :: AstElement
    x -> AstElement -> Lua AstElement
    forall (m :: * -> *) a. Monad m => a -> m a
    return AstElement
    x
          Left _ -> FilePath -> Lua AstElement
    forall a. FilePath -> Lua a
    Lua.throwException
            "Expected an AST element, but could not parse value as such."

    -- | Convert a number < 4000 to uppercase roman numeral.
    toRomanNumeral :: Lua.Integer -> Lua T.Text
    toRomanNumeral :: Integer -> Lua Text
    toRomanNumeral = Text -> Lua Text
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Text -> Lua Text) -> (Integer -> Text) -> Integer -> Lua Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Int -> Text
    Shared.toRomanNumeral (Int -> Text) -> (Integer -> Int) -> Integer -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Integer -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral