summaryrefslogtreecommitdiff
path: root/pandoc-lua-engine/src/Text/Pandoc/Lua/Marshal/Format.hs
blob: 3ebc4c03ec38fc97bea0a5f829fc5a5eabbe66bb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
{-# LANGUAGE LambdaCase           #-}
{-# LANGUAGE OverloadedStrings    #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{- |
   Module      : Text.Pandoc.Lua.Marshaling.Format
   Copyright   : © 2022-2023 Albert Krewinkel
   License     : GPL-2.0-or-later
   Maintainer  : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>

Marshaling functions and instance for format related types, including
'Extensions' and 'ExtensionConfig'.
-}
module Text.Pandoc.Lua.Marshal.Format
  ( peekExtensions
  , pushExtensions
  , peekExtensionsConfig
  , peekFlavoredFormat
  ) where

import Control.Applicative ((<|>))
import Control.Monad ((<$!>))
import Data.Maybe (fromMaybe)
import HsLua
import Text.Pandoc.Error (PandocError (..))
import Text.Pandoc.Extensions
  ( Extension, Extensions, extensionsFromList
  , getDefaultExtensions, readExtension )
import Text.Pandoc.Format
  ( ExtensionsConfig (..), ExtensionsDiff (..), FlavoredFormat (..)
  , diffExtensions, parseFlavoredFormat)
import Text.Pandoc.Lua.PandocLua (PandocLua (unPandocLua))

-- | Retrieves an 'Extensions' set from the Lua stack.
peekExtension :: LuaError e => Peeker e Extension
peekExtension idx = do
  extString <- peekString idx
  return $ readExtension extString
{-# INLINE peekExtension #-}

-- | Retrieves an 'Extensions' set from the Lua stack.
peekExtensions :: LuaError e => Peeker e Extensions
peekExtensions = fmap extensionsFromList . peekList peekExtension
{-# INLINE peekExtensions #-}

-- | Pushes a set of 'Extensions' to the top of the Lua stack.
pushExtensions :: LuaError e => Pusher e Extensions
pushExtensions = pushViaJSON
{-# INLINE pushExtensions #-}

instance Peekable Extensions where
  safepeek = peekExtensions

instance Pushable Extensions where
  push = pushExtensions

-- | Retrieves an 'ExtensionsConfig' value from the Lua stack.
peekExtensionsConfig :: LuaError e => Peeker e ExtensionsConfig
peekExtensionsConfig idx = do
  diff <- peekExtensionsDiff idx
  return $ ExtensionsConfig
    { extsDefault   = extsToEnable diff
    , extsSupported = extsToEnable diff <> extsToDisable diff
    }

instance Peekable ExtensionsConfig where
  safepeek = peekExtensionsConfig

peekExtensionsDiff :: LuaError e => Peeker e ExtensionsDiff
peekExtensionsDiff = typeChecked "table" istable $ \idx ->
      (do
          en <- peekFieldRaw (emptyOr (fmap Just . peekExtensions)) "enable" idx
          di <- peekFieldRaw (emptyOr (fmap Just . peekExtensions)) "disable" idx
          if (en, di) == (Nothing, Nothing)
            then failPeek "At least on of  'enable' and 'disable' must be set"
            else return $
                 ExtensionsDiff (fromMaybe mempty en) (fromMaybe mempty di))
  <|> -- two lists of extensions; the first is list assumed to contain those
      -- extensions to be enabled
      (uncurry ExtensionsDiff <$!> peekPair peekExtensions peekExtensions idx)
  <|> (do
          let
          exts <- peekKeyValuePairs peekExtension peekEnabled idx
          let enabled  = extensionsFromList . map fst $ filter snd exts
          let disabled = extensionsFromList . map fst $ filter (not . snd) exts
          return $ ExtensionsDiff enabled disabled)

-- | Retrieves the activation status of an extension. True or the string
-- @'enable'@ for activated, False or 'disable' for disabled.
peekEnabled :: LuaError e => Peeker e Bool
peekEnabled idx' = liftLua (ltype idx') >>= \case
  TypeBoolean -> peekBool idx'
  TypeString  -> peekText idx' >>= \case
                   "disable" -> pure False
                   "enable"  -> pure True
                   _         -> failPeek "expected 'disable' or 'enable'"
  _ -> failPeek "expected boolean or string"

-- | Retrieves a flavored format from the Lua stack.
peekFlavoredFormat :: Peeker PandocError FlavoredFormat
peekFlavoredFormat idx = retrieving "flavored format" $
  liftLua (ltype idx) >>= \case
  TypeString -> peekText idx >>= liftLua . unPandocLua . parseFlavoredFormat
  TypeTable -> do
    let diffFor format idx' = peekExtensionsDiff idx' <|>
          (getDefaultExtensions format `diffExtensions`) <$>
          (typeChecked "table" istable peekExtensions idx')
    format   <- peekFieldRaw peekText "format" idx
    extsDiff <- peekFieldRaw (emptyOr (diffFor format)) "extensions" idx
    return (FlavoredFormat format extsDiff)
  _ -> failPeek =<< typeMismatchMessage "string or table" idx

-- | Returns 'mempty' if the given stack index is @nil@, and the result
-- of the peeker otherwise.
emptyOr :: Monoid a => Peeker e a -> Peeker e a
emptyOr p idx = do
  nil <- liftLua (isnil idx)
  if nil
    then pure mempty
    else p idx