{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

-- |
-- Module      : $header$
-- Copyright   : (c) Laurent P René de Cotret, 2019 - present
-- License     : GNU GPL, version 2 or above
-- Maintainer  : laurent.decotret@outlook.com
-- Stability   : internal
-- Portability : portable
--
-- This module defines types and functions that help
-- with keeping track of figure specifications
module Text.Pandoc.Filter.Plot.Parse
  ( plotToolkit,
    parseFigureSpec,
    ParseFigureResult (..),
    captionReader,
  )
where

import Control.Monad (join, unless, when)
import Data.Char (isSpace)
import Data.Default (def)
import Data.List (find, intersperse)
import qualified Data.Map.Strict as Map
import Data.Maybe (fromJust, fromMaybe, isJust)
import Data.String (fromString)
import Data.Text (Text, pack, unpack)
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Data.Version (showVersion)
import Paths_pandoc_plot (version)
import System.FilePath (makeValid, normalise)
import Text.Pandoc.Class (runPure)
import Text.Pandoc.Definition
  ( Block (..),
    Format (..),
    Inline,
    Pandoc (..),
  )
import Text.Pandoc.Filter.Plot.Monad
import Text.Pandoc.Filter.Plot.Renderers
import Text.Pandoc.Options (ReaderOptions (..))
import Text.Pandoc.Readers (Reader (..), getReader)

tshow :: Show a => a -> Text
tshow :: a -> Text
tshow = String -> Text
pack (String -> Text) -> (a -> String) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> String
forall a. Show a => a -> String
show

data ParseFigureResult
  -- | The block is not meant to become a figure
  = NotAFigure
  -- | The block is meant to become a figure
  | Figure FigureSpec
  -- | The block is meant to become a figure, but the plotting toolkit is missing
  | MissingToolkit Toolkit
  -- | The block is meant to become a figure, but the figure format is incompatible 
  --   with the plotting toolkit
  | UnsupportedSaveFormat Toolkit SaveFormat

-- | Determine inclusion specifications from @Block@ attributes.
-- If an environment is detected, but the save format is incompatible,
-- an error will be thrown.
parseFigureSpec :: Block -> PlotM ParseFigureResult
parseFigureSpec :: Block -> PlotM ParseFigureResult
parseFigureSpec block :: Block
block@(CodeBlock (Text
id', [Text]
classes, [(Text, Text)]
attrs) Text
_) = do
  -- The following would be a good case for the use of MaybeT
  -- but I don't want to bring in another dependency
  let mtk :: Maybe Toolkit
mtk = Block -> Maybe Toolkit
plotToolkit Block
block
  case Maybe Toolkit
mtk of
    Maybe Toolkit
Nothing -> ParseFigureResult -> PlotM ParseFigureResult
forall (m :: * -> *) a. Monad m => a -> m a
return ParseFigureResult
NotAFigure
    Just Toolkit
tk -> do
      Maybe Renderer
r <- Toolkit -> PlotM (Maybe Renderer)
renderer Toolkit
tk
      case Maybe Renderer
r of
        Maybe Renderer
Nothing -> do
          Text -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall (m :: * -> *). (MonadLogger m, MonadIO m) => Text -> m ()
err (Text -> StateT PlotState (ReaderT RuntimeEnv IO) ())
-> Text -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat [Text
"Renderer for ", Toolkit -> Text
forall a. Show a => a -> Text
tshow Toolkit
tk, Text
" needed but is not installed"]
          ParseFigureResult -> PlotM ParseFigureResult
forall (m :: * -> *) a. Monad m => a -> m a
return (ParseFigureResult -> PlotM ParseFigureResult)
-> ParseFigureResult -> PlotM ParseFigureResult
forall a b. (a -> b) -> a -> b
$ Toolkit -> ParseFigureResult
MissingToolkit Toolkit
tk
        Just Renderer
r' -> Renderer -> PlotM ParseFigureResult
figureSpec Renderer
r'
  where
    attrs' :: Map Text Text
attrs' = [(Text, Text)] -> Map Text Text
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Text, Text)]
attrs
    preamblePath :: Maybe String
preamblePath = Text -> String
unpack (Text -> String) -> Maybe Text -> Maybe String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Map Text Text -> Maybe Text
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (InclusionKey -> Text
forall a. Show a => a -> Text
tshow InclusionKey
PreambleK) Map Text Text
attrs'

    figureSpec :: Renderer -> PlotM ParseFigureResult
    figureSpec :: Renderer -> PlotM ParseFigureResult
figureSpec renderer_ :: Renderer
renderer_@Renderer {String
[SaveFormat]
[Text -> CheckResult]
Text
Executable
Toolkit
Text -> Text
OutputSpec -> Text
FigureSpec -> String -> Text
rendererScriptExtension :: Renderer -> String
rendererComment :: Renderer -> Text -> Text
rendererLanguage :: Renderer -> Text
rendererChecks :: Renderer -> [Text -> CheckResult]
rendererSupportedSaveFormats :: Renderer -> [SaveFormat]
rendererCommand :: Renderer -> OutputSpec -> Text
rendererCapture :: Renderer -> FigureSpec -> String -> Text
rendererExe :: Renderer -> Executable
rendererToolkit :: Renderer -> Toolkit
rendererScriptExtension :: String
rendererComment :: Text -> Text
rendererLanguage :: Text
rendererChecks :: [Text -> CheckResult]
rendererSupportedSaveFormats :: [SaveFormat]
rendererCommand :: OutputSpec -> Text
rendererCapture :: FigureSpec -> String -> Text
rendererExe :: Executable
rendererToolkit :: Toolkit
..} = do
      Configuration
conf <- (RuntimeEnv -> Configuration)
-> StateT PlotState (ReaderT RuntimeEnv IO) Configuration
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks RuntimeEnv -> Configuration
envConfig
      let toolkit :: Toolkit
toolkit = Toolkit
rendererToolkit
          saveFormat :: SaveFormat
saveFormat = SaveFormat -> (Text -> SaveFormat) -> Maybe Text -> SaveFormat
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Configuration -> SaveFormat
defaultSaveFormat Configuration
conf) (String -> SaveFormat
forall a. IsString a => String -> a
fromString (String -> SaveFormat) -> (Text -> String) -> Text -> SaveFormat
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
unpack) (Text -> Map Text Text -> Maybe Text
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (InclusionKey -> Text
forall a. Show a => a -> Text
tshow InclusionKey
SaveFormatK) Map Text Text
attrs')

      -- Check if the saveformat is compatible with the toolkit
      if Bool -> Bool
not (SaveFormat
saveFormat SaveFormat -> [SaveFormat] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [SaveFormat]
rendererSupportedSaveFormats) 
        then do
          Text -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall (m :: * -> *). (MonadLogger m, MonadIO m) => Text -> m ()
err (Text -> StateT PlotState (ReaderT RuntimeEnv IO) ())
-> Text -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall a b. (a -> b) -> a -> b
$ String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall a. Monoid a => [a] -> a
mconcat [String
"Save format ", SaveFormat -> String
forall a. Show a => a -> String
show SaveFormat
saveFormat, String
" not supported by ", Toolkit -> String
forall a. Show a => a -> String
show Toolkit
toolkit]
          ParseFigureResult -> PlotM ParseFigureResult
forall (m :: * -> *) a. Monad m => a -> m a
return (ParseFigureResult -> PlotM ParseFigureResult)
-> ParseFigureResult -> PlotM ParseFigureResult
forall a b. (a -> b) -> a -> b
$ Toolkit -> SaveFormat -> ParseFigureResult
UnsupportedSaveFormat Toolkit
toolkit SaveFormat
saveFormat
        else do
          let extraAttrs' :: Map Text Text
extraAttrs' = Toolkit -> Map Text Text -> Map Text Text
parseExtraAttrs Toolkit
toolkit Map Text Text
attrs'
              header :: Text
header = Text -> Text
rendererComment (Text -> Text) -> Text -> Text
forall a b. (a -> b) -> a -> b
$ Text
"Generated by pandoc-plot " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> (String -> Text
pack (String -> Text) -> (Version -> String) -> Version -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Version -> String
showVersion) Version
version
              defaultPreamble :: Text
defaultPreamble = Toolkit -> Configuration -> Text
preambleSelector Toolkit
toolkit Configuration
conf

          Text
includeScript <-
            StateT PlotState (ReaderT RuntimeEnv IO) Text
-> (String -> StateT PlotState (ReaderT RuntimeEnv IO) Text)
-> Maybe String
-> StateT PlotState (ReaderT RuntimeEnv IO) Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
              (Text -> StateT PlotState (ReaderT RuntimeEnv IO) Text
forall (m :: * -> *) a. Monad m => a -> m a
return Text
defaultPreamble)
              (IO Text -> StateT PlotState (ReaderT RuntimeEnv IO) Text
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Text -> StateT PlotState (ReaderT RuntimeEnv IO) Text)
-> (String -> IO Text)
-> String
-> StateT PlotState (ReaderT RuntimeEnv IO) Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> IO Text
TIO.readFile)
              Maybe String
preamblePath
          let -- Filter attributes that are not relevant to pandoc-plot
              -- This presumes that inclusionKeys includes ALL possible keys, for all toolkits
              filteredAttrs :: [(Text, Text)]
filteredAttrs = ((Text, Text) -> Bool) -> [(Text, Text)] -> [(Text, Text)]
forall a. (a -> Bool) -> [a] -> [a]
filter (\(Text
k, Text
_) -> Text
k Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` (InclusionKey -> Text
forall a. Show a => a -> Text
tshow (InclusionKey -> Text) -> [InclusionKey] -> [Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [InclusionKey]
inclusionKeys)) [(Text, Text)]
attrs
              defWithSource :: Bool
defWithSource = Configuration -> Bool
defaultWithSource Configuration
conf
              defDPI :: Int
defDPI = Configuration -> Int
defaultDPI Configuration
conf

          -- Decide between reading from file or using document content
          Text
content <- Block -> StateT PlotState (ReaderT RuntimeEnv IO) Text
parseContent Block
block

          let caption :: Text
caption = Text -> Text -> Map Text Text -> Text
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault Text
forall a. Monoid a => a
mempty (InclusionKey -> Text
forall a. Show a => a -> Text
tshow InclusionKey
CaptionK) Map Text Text
attrs'
              withSource :: Bool
withSource = Bool -> (Text -> Bool) -> Maybe Text -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
defWithSource Text -> Bool
readBool (Text -> Map Text Text -> Maybe Text
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (InclusionKey -> Text
forall a. Show a => a -> Text
tshow InclusionKey
WithSourceK) Map Text Text
attrs')
              script :: Text
script = [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat ([Text] -> Text) -> [Text] -> Text
forall a b. (a -> b) -> a -> b
$ Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
intersperse Text
"\n" [Text
header, Text
includeScript, Text
content]
              directory :: String
directory = String -> String
makeValid (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ Text -> String
unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ Text -> Text -> Map Text Text -> Text
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault (String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Configuration -> String
defaultDirectory Configuration
conf) (InclusionKey -> Text
forall a. Show a => a -> Text
tshow InclusionKey
DirectoryK) Map Text Text
attrs'
              dpi :: Int
dpi = Int -> (Text -> Int) -> Maybe Text -> Int
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Int
defDPI (String -> Int
forall a. Read a => String -> a
read (String -> Int) -> (Text -> String) -> Text -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
unpack) (Text -> Map Text Text -> Maybe Text
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (InclusionKey -> Text
forall a. Show a => a -> Text
tshow InclusionKey
DpiK) Map Text Text
attrs')
              extraAttrs :: [(Text, Text)]
extraAttrs = Map Text Text -> [(Text, Text)]
forall k a. Map k a -> [(k, a)]
Map.toList Map Text Text
extraAttrs'
              blockAttrs :: (Text, [Text], [(Text, Text)])
blockAttrs = (Text
id', (Text -> Bool) -> [Text] -> [Text]
forall a. (a -> Bool) -> [a] -> [a]
filter (Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Toolkit -> Text
cls Toolkit
toolkit) [Text]
classes, [(Text, Text)]
filteredAttrs)

          let blockDependencies :: [String]
blockDependencies = Text -> [String]
parseFileDependencies (Text -> [String]) -> Text -> [String]
forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
forall a. Monoid a => a
mempty (Maybe Text -> Text) -> Maybe Text -> Text
forall a b. (a -> b) -> a -> b
$ Text -> Map Text Text -> Maybe Text
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (InclusionKey -> Text
forall a. Show a => a -> Text
tshow InclusionKey
DependenciesK) Map Text Text
attrs'
              dependencies :: [String]
dependencies = Configuration -> [String]
defaultDependencies Configuration
conf [String] -> [String] -> [String]
forall a. Semigroup a => a -> a -> a
<> [String]
blockDependencies

          -- This is the first opportunity to check save format compatibility
          ()
_' <-
            Bool
-> StateT PlotState (ReaderT RuntimeEnv IO) ()
-> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (SaveFormat
saveFormat SaveFormat -> [SaveFormat] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [SaveFormat]
rendererSupportedSaveFormats) (StateT PlotState (ReaderT RuntimeEnv IO) ()
 -> StateT PlotState (ReaderT RuntimeEnv IO) ())
-> StateT PlotState (ReaderT RuntimeEnv IO) ()
-> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall a b. (a -> b) -> a -> b
$
              let msg :: Text
msg = String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall a. Monoid a => [a] -> a
mconcat [String
"Save format ", SaveFormat -> String
forall a. Show a => a -> String
show SaveFormat
saveFormat, String
" not supported by ", Toolkit -> String
forall a. Show a => a -> String
show Toolkit
toolkit]
              in Text -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall (m :: * -> *). (MonadLogger m, MonadIO m) => Text -> m ()
err Text
msg
          
          -- Ensure that the save format makes sense given the final conversion format, if known
          ParseFigureResult -> PlotM ParseFigureResult
forall (m :: * -> *) a. Monad m => a -> m a
return (ParseFigureResult -> PlotM ParseFigureResult)
-> ParseFigureResult -> PlotM ParseFigureResult
forall a b. (a -> b) -> a -> b
$ FigureSpec -> ParseFigureResult
Figure (FigureSpec :: Renderer
-> Text
-> Bool
-> Text
-> SaveFormat
-> String
-> Int
-> [String]
-> [(Text, Text)]
-> (Text, [Text], [(Text, Text)])
-> FigureSpec
FigureSpec {Bool
Int
String
[String]
[(Text, Text)]
(Text, [Text], [(Text, Text)])
Text
Renderer
SaveFormat
blockAttrs :: (Text, [Text], [(Text, Text)])
extraAttrs :: [(Text, Text)]
dependencies :: [String]
dpi :: Int
directory :: String
saveFormat :: SaveFormat
script :: Text
withSource :: Bool
caption :: Text
renderer_ :: Renderer
dependencies :: [String]
blockAttrs :: (Text, [Text], [(Text, Text)])
extraAttrs :: [(Text, Text)]
dpi :: Int
directory :: String
script :: Text
withSource :: Bool
caption :: Text
saveFormat :: SaveFormat
renderer_ :: Renderer
..})
-- Base case: block is not a CodeBlock
parseFigureSpec Block
_ = ParseFigureResult -> PlotM ParseFigureResult
forall (m :: * -> *) a. Monad m => a -> m a
return ParseFigureResult
NotAFigure

-- | Parse script content from a block, if possible.
-- The script content can either come from a file
-- or from the code block itself. If both are present,
-- the file is preferred.
parseContent :: Block -> PlotM Script
parseContent :: Block -> StateT PlotState (ReaderT RuntimeEnv IO) Text
parseContent (CodeBlock (Text
_, [Text]
_, [(Text, Text)]
attrs) Text
content) = do
  let attrs' :: Map Text Text
attrs' = [(Text, Text)] -> Map Text Text
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList [(Text, Text)]
attrs
      mfile :: Maybe String
mfile = String -> String
normalise (String -> String) -> (Text -> String) -> Text -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
unpack (Text -> String) -> Maybe Text -> Maybe String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Map Text Text -> Maybe Text
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup (InclusionKey -> Text
forall a. Show a => a -> Text
tshow InclusionKey
FileK) Map Text Text
attrs'
  Bool
-> StateT PlotState (ReaderT RuntimeEnv IO) ()
-> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Text
content Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
forall a. Monoid a => a
mempty Bool -> Bool -> Bool
&& Maybe String -> Bool
forall a. Maybe a -> Bool
isJust Maybe String
mfile) (StateT PlotState (ReaderT RuntimeEnv IO) ()
 -> StateT PlotState (ReaderT RuntimeEnv IO) ())
-> StateT PlotState (ReaderT RuntimeEnv IO) ()
-> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall a b. (a -> b) -> a -> b
$ do
    Text -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall (m :: * -> *). (MonadLogger m, MonadIO m) => Text -> m ()
warning (Text -> StateT PlotState (ReaderT RuntimeEnv IO) ())
-> Text -> StateT PlotState (ReaderT RuntimeEnv IO) ()
forall a b. (a -> b) -> a -> b
$
      [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
        [ Text
"Figure refers to a file (",
          String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Maybe String -> String
forall a. HasCallStack => Maybe a -> a
fromJust Maybe String
mfile,
          Text
") but also has content in the document.\nThe file content will be preferred."
        ]
  let loadFromFile :: String -> m Text
loadFromFile String
fp = do
        Text -> m ()
forall (m :: * -> *). (MonadLogger m, MonadIO m) => Text -> m ()
info (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ Text
"Loading figure content from " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> String -> Text
pack String
fp
        IO Text -> m Text
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Text -> m Text) -> IO Text -> m Text
forall a b. (a -> b) -> a -> b
$ String -> IO Text
TIO.readFile String
fp
  StateT PlotState (ReaderT RuntimeEnv IO) Text
-> (String -> StateT PlotState (ReaderT RuntimeEnv IO) Text)
-> Maybe String
-> StateT PlotState (ReaderT RuntimeEnv IO) Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Text -> StateT PlotState (ReaderT RuntimeEnv IO) Text
forall (m :: * -> *) a. Monad m => a -> m a
return Text
content) String -> StateT PlotState (ReaderT RuntimeEnv IO) Text
forall (m :: * -> *).
(MonadLogger m, MonadIO m) =>
String -> m Text
loadFromFile Maybe String
mfile
parseContent Block
_ = Text -> StateT PlotState (ReaderT RuntimeEnv IO) Text
forall (m :: * -> *) a. Monad m => a -> m a
return Text
forall a. Monoid a => a
mempty

-- | Determine which toolkit should be used to render the plot
-- from a code block, if any.
plotToolkit :: Block -> Maybe Toolkit
plotToolkit :: Block -> Maybe Toolkit
plotToolkit (CodeBlock (Text
_, [Text]
classes, [(Text, Text)]
_) Text
_) =
  (Toolkit -> Bool) -> [Toolkit] -> Maybe Toolkit
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\Toolkit
tk -> Toolkit -> Text
cls Toolkit
tk Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
classes) [Toolkit]
toolkits
plotToolkit Block
_ = Maybe Toolkit
forall a. Maybe a
Nothing

-- | Reader a caption, based on input document format
captionReader :: Format -> Text -> Maybe [Inline]
captionReader :: Format -> Text -> Maybe [Inline]
captionReader (Format Text
f) Text
t = (PandocError -> Maybe [Inline])
-> (Pandoc -> Maybe [Inline])
-> Either PandocError Pandoc
-> Maybe [Inline]
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Maybe [Inline] -> PandocError -> Maybe [Inline]
forall a b. a -> b -> a
const Maybe [Inline]
forall a. Maybe a
Nothing) ([Inline] -> Maybe [Inline]
forall a. a -> Maybe a
Just ([Inline] -> Maybe [Inline])
-> (Pandoc -> [Inline]) -> Pandoc -> Maybe [Inline]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pandoc -> [Inline]
extractFromBlocks) (Either PandocError Pandoc -> Maybe [Inline])
-> Either PandocError Pandoc -> Maybe [Inline]
forall a b. (a -> b) -> a -> b
$
  PandocPure Pandoc -> Either PandocError Pandoc
forall a. PandocPure a -> Either PandocError a
runPure (PandocPure Pandoc -> Either PandocError Pandoc)
-> PandocPure Pandoc -> Either PandocError Pandoc
forall a b. (a -> b) -> a -> b
$ do
    (Reader PandocPure
reader, Extensions
exts) <- Text -> PandocPure (Reader PandocPure, Extensions)
forall (m :: * -> *).
PandocMonad m =>
Text -> m (Reader m, Extensions)
getReader Text
f
    let readerOpts :: ReaderOptions
readerOpts = ReaderOptions
forall a. Default a => a
def {readerExtensions :: Extensions
readerExtensions = Extensions
exts}
    -- Assuming no ByteString readers...
    case Reader PandocPure
reader of
      TextReader forall a. ToSources a => ReaderOptions -> a -> PandocPure Pandoc
fct -> ReaderOptions -> Text -> PandocPure Pandoc
forall a. ToSources a => ReaderOptions -> a -> PandocPure Pandoc
fct ReaderOptions
readerOpts Text
t
      Reader PandocPure
_ -> Pandoc -> PandocPure Pandoc
forall (m :: * -> *) a. Monad m => a -> m a
return Pandoc
forall a. Monoid a => a
mempty
  where
    extractFromBlocks :: Pandoc -> [Inline]
extractFromBlocks (Pandoc Meta
_ [Block]
blocks) = [[Inline]] -> [Inline]
forall a. Monoid a => [a] -> a
mconcat ([[Inline]] -> [Inline]) -> [[Inline]] -> [Inline]
forall a b. (a -> b) -> a -> b
$ Block -> [Inline]
extractInlines (Block -> [Inline]) -> [Block] -> [[Inline]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [Block]
blocks

    extractInlines :: Block -> [Inline]
extractInlines (Plain [Inline]
inlines) = [Inline]
inlines
    extractInlines (Para [Inline]
inlines) = [Inline]
inlines
    extractInlines (LineBlock [[Inline]]
multiinlines) = [[Inline]] -> [Inline]
forall (m :: * -> *) a. Monad m => m (m a) -> m a
join [[Inline]]
multiinlines
    extractInlines Block
_ = []

-- | Flexible boolean parsing
readBool :: Text -> Bool
readBool :: Text -> Bool
readBool Text
s
  | Text
s Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text
"True", Text
"true", Text
"'True'", Text
"'true'", Text
"1"] = Bool
True
  | Text
s Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text
"False", Text
"false", Text
"'False'", Text
"'false'", Text
"0"] = Bool
False
  | Bool
otherwise = String -> Bool
forall a. String -> a
errorWithoutStackTrace (String -> Bool) -> String -> Bool
forall a b. (a -> b) -> a -> b
$ Text -> String
unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat [Text
"Could not parse '", Text
s, Text
"' into a boolean. Please use 'True' or 'False'"]

-- | Parse a list of file dependencies such as /[foo.bar, hello.txt]/.
parseFileDependencies :: Text -> [FilePath]
parseFileDependencies :: Text -> [String]
parseFileDependencies Text
t
  | Text
t Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
forall a. Monoid a => a
mempty = [String]
forall a. Monoid a => a
mempty
  | Bool
otherwise =
    (Text -> String) -> [Text] -> [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (String -> String
normalise (String -> String) -> (Text -> String) -> Text -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> String
unpack (Text -> String) -> (Text -> Text) -> Text -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> Text -> Text
T.dropAround Char -> Bool
isSpace)
      ([Text] -> [String]) -> (Text -> [Text]) -> Text -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text -> [Text]
T.splitOn Text
","
      (Text -> [Text]) -> (Text -> Text) -> Text -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> Text -> Text
T.dropAround (\Char
c -> Char
c Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
'[', Char
']'])
      (Text -> [String]) -> Text -> [String]
forall a b. (a -> b) -> a -> b
$ Text
t