{-# LANGUAGE OverloadedStrings #-}
{-|
Module      : $header$
Copyright   : (c) Laurent P René de Cotret, 2020
License     : GNU GPL, version 2 or above
Maintainer  : laurent.decotret@outlook.com
Stability   : internal
Portability : portable

This module defines the @PlotM@ monad and related capabilities.
-}

module Text.Pandoc.Filter.Plot.Monad (
      Configuration(..)
    , PlotM
    , RuntimeEnv(..)
    , runPlotM
    -- * Running external commands

    , runCommand
    -- * Getting file hashes

    , fileHash
    -- * Getting executables

    , executable
    -- * Logging

    , Verbosity(..)
    , LogSink(..)
    , debug
    , err
    , warning
    , info
    -- * Lifting and other monadic operations

    , liftIO
    , ask
    , asks
    , asksConfig
    , silence
    -- * Base types

    , module Text.Pandoc.Filter.Plot.Monad.Types
) where


import           Control.Concurrent.Chan     (writeChan)
import           Control.Concurrent.MVar

import           Control.Monad.Reader
import           Control.Monad.State.Strict

import           Data.ByteString.Lazy        (toStrict)
import           Data.Hashable               (hash)
import           Data.Map.Strict             (Map)
import qualified Data.Map.Strict             as M

import           Data.Text                   (Text, pack, unpack)
import qualified Data.Text                   as T
import           Data.Text.Encoding          (decodeUtf8With)
import           Data.Text.Encoding.Error    (lenientDecode)

import           System.Directory            (doesFileExist, getModificationTime, findExecutable)
import           System.Exit                 (ExitCode (..))
import           System.Process.Typed        ( readProcessStderr, shell, nullStream
                                             , setStdout, setStderr, byteStringOutput
                                             , setWorkingDir
                                             )
import           Text.Pandoc.Definition      (Format(..))

import           Prelude                     hiding (log, fst, snd)

import Text.Pandoc.Filter.Plot.Monad.Logging as Log
import Text.Pandoc.Filter.Plot.Monad.Types


-- | pandoc-plot monad

type PlotM a = StateT PlotState (ReaderT RuntimeEnv IO) a


data RuntimeEnv = 
    RuntimeEnv { RuntimeEnv -> Configuration
envConfig :: Configuration
               , RuntimeEnv -> Logger
envLogger :: Logger
               }


-- | Modify the runtime environment to be silent.

silence :: PlotM a -> PlotM a
silence :: PlotM a -> PlotM a
silence = (RuntimeEnv -> RuntimeEnv) -> PlotM a -> PlotM a
forall r (m :: * -> *) a. MonadReader r m => (r -> r) -> m a -> m a
local (\(RuntimeEnv Configuration
c Logger
l) -> Configuration -> Logger -> RuntimeEnv
RuntimeEnv Configuration
c Logger
l{lVerbosity :: Verbosity
lVerbosity = Verbosity
Silent})


-- | Get access to configuration within the @PlotM@ monad.

asksConfig :: (Configuration -> a) -> PlotM a
asksConfig :: (Configuration -> a) -> PlotM a
asksConfig Configuration -> a
f = (RuntimeEnv -> a) -> PlotM a
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (Configuration -> a
f (Configuration -> a)
-> (RuntimeEnv -> Configuration) -> RuntimeEnv -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RuntimeEnv -> Configuration
envConfig)


-- | Evaluate a @PlotM@ action.

runPlotM :: Configuration -> PlotM a -> IO a
runPlotM :: Configuration -> PlotM a -> IO a
runPlotM Configuration
conf PlotM a
v = do
    PlotState
st <- MVar (Map FilePath FileHash)
-> MVar (Map Toolkit (Maybe Executable)) -> PlotState
PlotState (MVar (Map FilePath FileHash)
 -> MVar (Map Toolkit (Maybe Executable)) -> PlotState)
-> IO (MVar (Map FilePath FileHash))
-> IO (MVar (Map Toolkit (Maybe Executable)) -> PlotState)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Map FilePath FileHash -> IO (MVar (Map FilePath FileHash))
forall a. a -> IO (MVar a)
newMVar Map FilePath FileHash
forall a. Monoid a => a
mempty
                    IO (MVar (Map Toolkit (Maybe Executable)) -> PlotState)
-> IO (MVar (Map Toolkit (Maybe Executable))) -> IO PlotState
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Map Toolkit (Maybe Executable)
-> IO (MVar (Map Toolkit (Maybe Executable)))
forall a. a -> IO (MVar a)
newMVar Map Toolkit (Maybe Executable)
forall a. Monoid a => a
mempty
    let verbosity :: Verbosity
verbosity = Configuration -> Verbosity
logVerbosity Configuration
conf
        sink :: LogSink
sink      = Configuration -> LogSink
logSink Configuration
conf 
    Verbosity -> LogSink -> (Logger -> IO a) -> IO a
forall a. Verbosity -> LogSink -> (Logger -> IO a) -> IO a
withLogger Verbosity
verbosity LogSink
sink ((Logger -> IO a) -> IO a) -> (Logger -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ 
        \Logger
logger -> ReaderT RuntimeEnv IO a -> RuntimeEnv -> IO a
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (PlotM a -> PlotState -> ReaderT RuntimeEnv IO a
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT PlotM a
v PlotState
st) (Configuration -> Logger -> RuntimeEnv
RuntimeEnv Configuration
conf Logger
logger)


debug, err, warning, info :: Text -> PlotM ()
debug :: Text -> PlotM ()
debug     = Text -> Verbosity -> Text -> PlotM ()
log Text
"DEBUG | " Verbosity
Debug 
err :: Text -> PlotM ()
err       = Text -> Verbosity -> Text -> PlotM ()
log Text
"ERROR | " Verbosity
Error 
warning :: Text -> PlotM ()
warning   = Text -> Verbosity -> Text -> PlotM ()
log Text
"WARN  | " Verbosity
Warning 
info :: Text -> PlotM ()
info      = Text -> Verbosity -> Text -> PlotM ()
log Text
"INFO  | " Verbosity
Info 


-- | General purpose logging. 

log :: Text      -- ^ Header.

    -> Verbosity -- ^ Verbosity of the message.

    -> Text      -- ^ Message (can be multiple lines).

    -> PlotM ()
log :: Text -> Verbosity -> Text -> PlotM ()
log Text
h Verbosity
v Text
t = do
    Logger
logger <- (RuntimeEnv -> Logger)
-> StateT PlotState (ReaderT RuntimeEnv IO) Logger
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks RuntimeEnv -> Logger
envLogger
    Bool -> PlotM () -> PlotM ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Verbosity
v Verbosity -> Verbosity -> Bool
forall a. Ord a => a -> a -> Bool
>= Logger -> Verbosity
lVerbosity Logger
logger) (PlotM () -> PlotM ()) -> PlotM () -> PlotM ()
forall a b. (a -> b) -> a -> b
$ 
        IO () -> PlotM ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> PlotM ()) -> IO () -> PlotM ()
forall a b. (a -> b) -> a -> b
$ do
            [Text] -> (Text -> IO ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ ( Text -> [Text]
T.lines Text
t) ((Text -> IO ()) -> IO ()) -> (Text -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Text
l -> Chan (Maybe Text) -> Maybe Text -> IO ()
forall a. Chan a -> a -> IO ()
writeChan (Logger -> Chan (Maybe Text)
lChannel Logger
logger) (Text -> Maybe Text
forall a. a -> Maybe a
Just (Text
h Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
l Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"\n"))


-- | Run a command within the @PlotM@ monad. Stderr stream

-- is read and decoded, while Stdout is ignored. 

-- Logging happens at the debug level if the command succeeds, or at

-- the error level if it does not succeed.

runCommand :: FilePath  -- Directory from which to run the command

           -> Text      -- Command to run, including executable

           -> PlotM (ExitCode, Text)
runCommand :: FilePath -> Text -> PlotM (ExitCode, Text)
runCommand FilePath
wordir Text
command = do
    (ExitCode
ec, ByteString
processOutput') <- IO (ExitCode, ByteString)
-> StateT PlotState (ReaderT RuntimeEnv IO) (ExitCode, ByteString)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO 
                        (IO (ExitCode, ByteString)
 -> StateT PlotState (ReaderT RuntimeEnv IO) (ExitCode, ByteString))
-> IO (ExitCode, ByteString)
-> StateT PlotState (ReaderT RuntimeEnv IO) (ExitCode, ByteString)
forall a b. (a -> b) -> a -> b
$ ProcessConfig () () (STM ByteString) -> IO (ExitCode, ByteString)
forall (m :: * -> *) stdin stdout stderrIgnored.
MonadIO m =>
ProcessConfig stdin stdout stderrIgnored
-> m (ExitCode, ByteString)
readProcessStderr 
                        (ProcessConfig () () (STM ByteString) -> IO (ExitCode, ByteString))
-> ProcessConfig () () (STM ByteString)
-> IO (ExitCode, ByteString)
forall a b. (a -> b) -> a -> b
$ StreamSpec 'STOutput ()
-> ProcessConfig () () (STM ByteString)
-> ProcessConfig () () (STM ByteString)
forall stdout stdin stdout0 stderr.
StreamSpec 'STOutput stdout
-> ProcessConfig stdin stdout0 stderr
-> ProcessConfig stdin stdout stderr
setStdout StreamSpec 'STOutput ()
forall (anyStreamType :: StreamType). StreamSpec anyStreamType ()
nullStream
                        (ProcessConfig () () (STM ByteString)
 -> ProcessConfig () () (STM ByteString))
-> ProcessConfig () () (STM ByteString)
-> ProcessConfig () () (STM ByteString)
forall a b. (a -> b) -> a -> b
$ StreamSpec 'STOutput (STM ByteString)
-> ProcessConfig () () () -> ProcessConfig () () (STM ByteString)
forall stderr stdin stdout stderr0.
StreamSpec 'STOutput stderr
-> ProcessConfig stdin stdout stderr0
-> ProcessConfig stdin stdout stderr
setStderr StreamSpec 'STOutput (STM ByteString)
byteStringOutput 
                        (ProcessConfig () () () -> ProcessConfig () () (STM ByteString))
-> ProcessConfig () () () -> ProcessConfig () () (STM ByteString)
forall a b. (a -> b) -> a -> b
$ FilePath -> ProcessConfig () () () -> ProcessConfig () () ()
forall stdin stdout stderr.
FilePath
-> ProcessConfig stdin stdout stderr
-> ProcessConfig stdin stdout stderr
setWorkingDir FilePath
wordir
                        (ProcessConfig () () () -> ProcessConfig () () ())
-> ProcessConfig () () () -> ProcessConfig () () ()
forall a b. (a -> b) -> a -> b
$ FilePath -> ProcessConfig () () ()
shell (Text -> FilePath
unpack Text
command)
    let processOutput :: Text
processOutput = OnDecodeError -> ByteString -> Text
decodeUtf8With OnDecodeError
lenientDecode (ByteString -> Text) -> ByteString -> Text
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
toStrict ByteString
processOutput'
        logFunc :: Text -> PlotM ()
logFunc = if ExitCode
ec ExitCode -> ExitCode -> Bool
forall a. Eq a => a -> a -> Bool
== ExitCode
ExitSuccess
                    then Text -> PlotM ()
debug
                    else Text -> PlotM ()
err
        message :: Text
message = [Text] -> Text
T.unlines [ Text
"Running command"
                            , Text
"    " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
command
                            , Text
"ended with exit code " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> (FilePath -> Text
pack (FilePath -> Text) -> (ExitCode -> FilePath) -> ExitCode -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ExitCode -> FilePath
forall a. Show a => a -> FilePath
show (ExitCode -> Text) -> ExitCode -> Text
forall a b. (a -> b) -> a -> b
$ ExitCode
ec)
                            ] 
        errorMessage :: Text
errorMessage = if Text
processOutput Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
forall a. Monoid a => a
mempty 
            then Text
forall a. Monoid a => a
mempty 
            else [Text] -> Text
T.unlines [ Text
"*******"
                           , Text
processOutput
                           , Text
"*******"
                           ]
    
    Text -> PlotM ()
logFunc (Text -> PlotM ()) -> Text -> PlotM ()
forall a b. (a -> b) -> a -> b
$ Text
message Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
errorMessage
    (ExitCode, Text) -> PlotM (ExitCode, Text)
forall (m :: * -> *) a. Monad m => a -> m a
return (ExitCode
ec, Text
processOutput)


-- Plot state is used for caching.

-- One part consists of a map of filepaths to hashes

-- This allows multiple plots to depend on the same file/directory, and the file hashes

-- will only be calculated once. This is OK because pandoc-plot will not run for long.

-- We note that because figures are rendered possibly in parallel, access to 

-- the state must be synchronized; otherwise, each thread might compute its own

-- hashes.

-- The other part is comprised of a map of toolkits to executables (possibly missing)

-- This means that executable will be search for only once.

type FileHash  = Word
data PlotState = 
    PlotState (MVar (Map FilePath FileHash))
              (MVar (Map Toolkit (Maybe Executable)))


-- | Get a filehash. If the file hash has been computed before,

-- it is reused. Otherwise, the filehash is calculated and stored.

fileHash :: FilePath -> PlotM FileHash
fileHash :: FilePath -> PlotM FileHash
fileHash FilePath
path = do
    PlotState MVar (Map FilePath FileHash)
varHashes MVar (Map Toolkit (Maybe Executable))
varExes <- StateT PlotState (ReaderT RuntimeEnv IO) PlotState
forall s (m :: * -> *). MonadState s m => m s
get
    Map FilePath FileHash
hashes <- IO (Map FilePath FileHash)
-> StateT PlotState (ReaderT RuntimeEnv IO) (Map FilePath FileHash)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Map FilePath FileHash)
 -> StateT
      PlotState (ReaderT RuntimeEnv IO) (Map FilePath FileHash))
-> IO (Map FilePath FileHash)
-> StateT PlotState (ReaderT RuntimeEnv IO) (Map FilePath FileHash)
forall a b. (a -> b) -> a -> b
$ MVar (Map FilePath FileHash) -> IO (Map FilePath FileHash)
forall a. MVar a -> IO a
takeMVar MVar (Map FilePath FileHash)
varHashes
    (FileHash
fh, Map FilePath FileHash
hashes') <- case FilePath -> Map FilePath FileHash -> Maybe FileHash
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup FilePath
path Map FilePath FileHash
hashes of
        Maybe FileHash
Nothing -> do
            Text -> PlotM ()
debug (Text -> PlotM ()) -> Text -> PlotM ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat [Text
"Calculating hash of dependency ", FilePath -> Text
pack FilePath
path]
            FileHash
fh <- FilePath -> PlotM FileHash
fileHash' FilePath
path
            let hashes' :: Map FilePath FileHash
hashes' = FilePath
-> FileHash -> Map FilePath FileHash -> Map FilePath FileHash
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert FilePath
path FileHash
fh Map FilePath FileHash
hashes
            (FileHash, Map FilePath FileHash)
-> StateT
     PlotState (ReaderT RuntimeEnv IO) (FileHash, Map FilePath FileHash)
forall (m :: * -> *) a. Monad m => a -> m a
return (FileHash
fh, Map FilePath FileHash
hashes')
        Just FileHash
h -> do
            Text -> PlotM ()
debug (Text -> PlotM ()) -> Text -> PlotM ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat [Text
"Hash of dependency ", FilePath -> Text
pack FilePath
path, Text
" already calculated."]
            (FileHash, Map FilePath FileHash)
-> StateT
     PlotState (ReaderT RuntimeEnv IO) (FileHash, Map FilePath FileHash)
forall (m :: * -> *) a. Monad m => a -> m a
return (FileHash
h, Map FilePath FileHash
hashes)
    IO () -> PlotM ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> PlotM ()) -> IO () -> PlotM ()
forall a b. (a -> b) -> a -> b
$ MVar (Map FilePath FileHash) -> Map FilePath FileHash -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar (Map FilePath FileHash)
varHashes Map FilePath FileHash
hashes'
    PlotState -> PlotM ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put (PlotState -> PlotM ()) -> PlotState -> PlotM ()
forall a b. (a -> b) -> a -> b
$ MVar (Map FilePath FileHash)
-> MVar (Map Toolkit (Maybe Executable)) -> PlotState
PlotState MVar (Map FilePath FileHash)
varHashes MVar (Map Toolkit (Maybe Executable))
varExes
    FileHash -> PlotM FileHash
forall (m :: * -> *) a. Monad m => a -> m a
return FileHash
fh
    where
    -- As a proxy for the state of a file dependency, we use the modification time

    -- This is much faster than actual file hashing

    fileHash' :: FilePath -> PlotM FileHash
    fileHash' :: FilePath -> PlotM FileHash
fileHash' FilePath
fp = do
        Bool
fileExists <- IO Bool -> StateT PlotState (ReaderT RuntimeEnv IO) Bool
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO Bool -> StateT PlotState (ReaderT RuntimeEnv IO) Bool)
-> IO Bool -> StateT PlotState (ReaderT RuntimeEnv IO) Bool
forall a b. (a -> b) -> a -> b
$ FilePath -> IO Bool
doesFileExist FilePath
fp
        if Bool
fileExists
            then IO FileHash -> PlotM FileHash
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO FileHash -> PlotM FileHash)
-> (FilePath -> IO FileHash) -> FilePath -> PlotM FileHash
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (UTCTime -> FileHash) -> IO UTCTime -> IO FileHash
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int -> FileHash
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> FileHash) -> (UTCTime -> Int) -> UTCTime -> FileHash
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Int
forall a. Hashable a => a -> Int
hash (FilePath -> Int) -> (UTCTime -> FilePath) -> UTCTime -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UTCTime -> FilePath
forall a. Show a => a -> FilePath
show) (IO UTCTime -> IO FileHash)
-> (FilePath -> IO UTCTime) -> FilePath -> IO FileHash
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> IO UTCTime
getModificationTime (FilePath -> PlotM FileHash) -> FilePath -> PlotM FileHash
forall a b. (a -> b) -> a -> b
$ FilePath
fp
            else Text -> PlotM ()
err ([Text] -> Text
forall a. Monoid a => [a] -> a
mconcat [Text
"Dependency ", FilePath -> Text
pack FilePath
fp, Text
" does not exist."]) PlotM () -> PlotM FileHash -> PlotM FileHash
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> FileHash -> PlotM FileHash
forall (m :: * -> *) a. Monad m => a -> m a
return FileHash
0 


-- | Get an executable. If the executable has not been used before, 

-- find it and store where it is. It will be re-used.

executable :: Toolkit -> PlotM (Maybe Executable)
executable :: Toolkit -> PlotM (Maybe Executable)
executable Toolkit
tk = do
    FilePath
name <- Toolkit -> PlotM FilePath
exeSelector Toolkit
tk
    PlotState MVar (Map FilePath FileHash)
varHashes MVar (Map Toolkit (Maybe Executable))
varExes <- StateT PlotState (ReaderT RuntimeEnv IO) PlotState
forall s (m :: * -> *). MonadState s m => m s
get
    Map Toolkit (Maybe Executable)
exes <- IO (Map Toolkit (Maybe Executable))
-> StateT
     PlotState (ReaderT RuntimeEnv IO) (Map Toolkit (Maybe Executable))
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Map Toolkit (Maybe Executable))
 -> StateT
      PlotState (ReaderT RuntimeEnv IO) (Map Toolkit (Maybe Executable)))
-> IO (Map Toolkit (Maybe Executable))
-> StateT
     PlotState (ReaderT RuntimeEnv IO) (Map Toolkit (Maybe Executable))
forall a b. (a -> b) -> a -> b
$ MVar (Map Toolkit (Maybe Executable))
-> IO (Map Toolkit (Maybe Executable))
forall a. MVar a -> IO a
takeMVar MVar (Map Toolkit (Maybe Executable))
varExes
    (Maybe Executable
exe', Map Toolkit (Maybe Executable)
exes') <- case Toolkit
-> Map Toolkit (Maybe Executable) -> Maybe (Maybe Executable)
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup Toolkit
tk Map Toolkit (Maybe Executable)
exes of
        Maybe (Maybe Executable)
Nothing -> do
            Text -> PlotM ()
debug (Text -> PlotM ()) -> Text -> PlotM ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat [Text
"Looking for executable \"", FilePath -> Text
pack FilePath
name, Text
"\" for ", FilePath -> Text
pack (FilePath -> Text) -> FilePath -> Text
forall a b. (a -> b) -> a -> b
$ Toolkit -> FilePath
forall a. Show a => a -> FilePath
show Toolkit
tk]
            Maybe Executable
exe' <- IO (Maybe Executable) -> PlotM (Maybe Executable)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe Executable) -> PlotM (Maybe Executable))
-> IO (Maybe Executable) -> PlotM (Maybe Executable)
forall a b. (a -> b) -> a -> b
$ FilePath -> IO (Maybe FilePath)
findExecutable FilePath
name IO (Maybe FilePath)
-> (Maybe FilePath -> IO (Maybe Executable))
-> IO (Maybe Executable)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Maybe Executable -> IO (Maybe Executable)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Executable -> IO (Maybe Executable))
-> (Maybe FilePath -> Maybe Executable)
-> Maybe FilePath
-> IO (Maybe Executable)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (FilePath -> Executable) -> Maybe FilePath -> Maybe Executable
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap FilePath -> Executable
exeFromPath
            let exes' :: Map Toolkit (Maybe Executable)
exes' = Toolkit
-> Maybe Executable
-> Map Toolkit (Maybe Executable)
-> Map Toolkit (Maybe Executable)
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert Toolkit
tk Maybe Executable
exe' Map Toolkit (Maybe Executable)
exes
            (Maybe Executable, Map Toolkit (Maybe Executable))
-> StateT
     PlotState
     (ReaderT RuntimeEnv IO)
     (Maybe Executable, Map Toolkit (Maybe Executable))
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Executable
exe', Map Toolkit (Maybe Executable)
exes')
        Just Maybe Executable
e -> do
            Text -> PlotM ()
debug (Text -> PlotM ()) -> Text -> PlotM ()
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat [Text
"Executable \"", FilePath -> Text
pack FilePath
name, Text
"\" already found."]
            (Maybe Executable, Map Toolkit (Maybe Executable))
-> StateT
     PlotState
     (ReaderT RuntimeEnv IO)
     (Maybe Executable, Map Toolkit (Maybe Executable))
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe Executable
e, Map Toolkit (Maybe Executable)
exes)
    IO () -> PlotM ()
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> PlotM ()) -> IO () -> PlotM ()
forall a b. (a -> b) -> a -> b
$ MVar (Map Toolkit (Maybe Executable))
-> Map Toolkit (Maybe Executable) -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar (Map Toolkit (Maybe Executable))
varExes Map Toolkit (Maybe Executable)
exes'
    PlotState -> PlotM ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put (PlotState -> PlotM ()) -> PlotState -> PlotM ()
forall a b. (a -> b) -> a -> b
$ MVar (Map FilePath FileHash)
-> MVar (Map Toolkit (Maybe Executable)) -> PlotState
PlotState MVar (Map FilePath FileHash)
varHashes MVar (Map Toolkit (Maybe Executable))
varExes
    Maybe Executable -> PlotM (Maybe Executable)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe Executable
exe'
    where
        exeSelector :: Toolkit -> PlotM FilePath
exeSelector Toolkit
Matplotlib   = (Configuration -> FilePath) -> PlotM FilePath
forall a. (Configuration -> a) -> PlotM a
asksConfig Configuration -> FilePath
matplotlibExe  
        exeSelector Toolkit
PlotlyPython = (Configuration -> FilePath) -> PlotM FilePath
forall a. (Configuration -> a) -> PlotM a
asksConfig Configuration -> FilePath
plotlyPythonExe 
        exeSelector Toolkit
PlotlyR      = (Configuration -> FilePath) -> PlotM FilePath
forall a. (Configuration -> a) -> PlotM a
asksConfig Configuration -> FilePath
plotlyRExe      
        exeSelector Toolkit
Matlab       = (Configuration -> FilePath) -> PlotM FilePath
forall a. (Configuration -> a) -> PlotM a
asksConfig Configuration -> FilePath
matlabExe       
        exeSelector Toolkit
Mathematica  = (Configuration -> FilePath) -> PlotM FilePath
forall a. (Configuration -> a) -> PlotM a
asksConfig Configuration -> FilePath
mathematicaExe  
        exeSelector Toolkit
Octave       = (Configuration -> FilePath) -> PlotM FilePath
forall a. (Configuration -> a) -> PlotM a
asksConfig Configuration -> FilePath
octaveExe       
        exeSelector Toolkit
GGPlot2      = (Configuration -> FilePath) -> PlotM FilePath
forall a. (Configuration -> a) -> PlotM a
asksConfig Configuration -> FilePath
ggplot2Exe      
        exeSelector Toolkit
GNUPlot      = (Configuration -> FilePath) -> PlotM FilePath
forall a. (Configuration -> a) -> PlotM a
asksConfig Configuration -> FilePath
gnuplotExe      
        exeSelector Toolkit
Graphviz     = (Configuration -> FilePath) -> PlotM FilePath
forall a. (Configuration -> a) -> PlotM a
asksConfig Configuration -> FilePath
graphvizExe     
        exeSelector Toolkit
Bokeh        = (Configuration -> FilePath) -> PlotM FilePath
forall a. (Configuration -> a) -> PlotM a
asksConfig Configuration -> FilePath
bokehExe         
        exeSelector Toolkit
Plotsjl      = (Configuration -> FilePath) -> PlotM FilePath
forall a. (Configuration -> a) -> PlotM a
asksConfig Configuration -> FilePath
plotsjlExe 


-- | The @Configuration@ type holds the default values to use

-- when running pandoc-plot. These values can be overridden in code blocks.

--

-- You can create an instance of the @Configuration@ type from file using the @configuration@ function.

--

-- You can store the path to a configuration file in metadata under the key @plot-configuration@. For example, in Markdown:

--

-- @

--     ---

--     title: My document

--     author: John Doe

--     plot-configuration: path\to\file.yml

--     ---     

-- @

--

-- The same can be specified via the command line using Pandoc's @-M@ flag:

--

-- > pandoc --filter pandoc-plot -M plot-configuration="path/to/file.yml" ...

--

-- In this case, use @configurationPathMeta@ to extact the path from @Pandoc@ documents.

data Configuration = Configuration
    { Configuration -> FilePath
defaultDirectory      :: !FilePath   -- ^ The default directory where figures will be saved.

    , Configuration -> Bool
defaultWithSource     :: !Bool       -- ^ The default behavior of whether or not to include links to source code and high-res

    , Configuration -> Int
defaultDPI            :: !Int        -- ^ The default dots-per-inch value for generated figures. Renderers might ignore this.

    , Configuration -> SaveFormat
defaultSaveFormat     :: !SaveFormat -- ^ The default save format of generated figures.

    , Configuration -> [FilePath]
defaultDependencies   :: ![FilePath] -- ^ List of files/directories on which all figures depend.

    , Configuration -> Format
captionFormat         :: !Format     -- ^ Caption format, in the same notation as Pandoc format, e.g. "markdown+tex_math_dollars"


    , Configuration -> Verbosity
logVerbosity          :: !Verbosity  -- ^ Level of logging verbosity.

    , Configuration -> LogSink
logSink               :: !LogSink    -- ^ Method of logging, i.e. printing to stderr or file.


    , Configuration -> Text
matplotlibPreamble    :: !Script     -- ^ The default preamble script for the matplotlib toolkit.

    , Configuration -> Text
plotlyPythonPreamble  :: !Script     -- ^ The default preamble script for the Plotly/Python toolkit.

    , Configuration -> Text
plotlyRPreamble       :: !Script     -- ^ The default preamble script for the Plotly/R toolkit.

    , Configuration -> Text
matlabPreamble        :: !Script     -- ^ The default preamble script for the MATLAB toolkit.

    , Configuration -> Text
mathematicaPreamble   :: !Script     -- ^ The default preamble script for the Mathematica toolkit.

    , Configuration -> Text
octavePreamble        :: !Script     -- ^ The default preamble script for the GNU Octave toolkit.

    , Configuration -> Text
ggplot2Preamble       :: !Script     -- ^ The default preamble script for the GGPlot2 toolkit.

    , Configuration -> Text
gnuplotPreamble       :: !Script     -- ^ The default preamble script for the gnuplot toolkit.

    , Configuration -> Text
graphvizPreamble      :: !Script     -- ^ The default preamble script for the Graphviz toolkit.

    , Configuration -> Text
bokehPreamble         :: !Script     -- ^ The default preamble script for the Python/Bokeh toolkit.

    , Configuration -> Text
plotsjlPreamble       :: !Script     -- ^ The default preamble script for the Julia/Plots.jl toolkit.

    
    , Configuration -> FilePath
matplotlibExe         :: !FilePath   -- ^ The executable to use to generate figures using the matplotlib toolkit.

    , Configuration -> FilePath
matlabExe             :: !FilePath   -- ^ The executable to use to generate figures using the MATLAB toolkit.

    , Configuration -> FilePath
plotlyPythonExe       :: !FilePath   -- ^ The executable to use to generate figures using the Plotly/Python toolkit.

    , Configuration -> FilePath
plotlyRExe            :: !FilePath   -- ^ The executable to use to generate figures using the Plotly/R toolkit.

    , Configuration -> FilePath
mathematicaExe        :: !FilePath   -- ^ The executable to use to generate figures using the Mathematica toolkit.

    , Configuration -> FilePath
octaveExe             :: !FilePath   -- ^ The executable to use to generate figures using the GNU Octave toolkit.

    , Configuration -> FilePath
ggplot2Exe            :: !FilePath   -- ^ The executable to use to generate figures using the GGPlot2 toolkit.

    , Configuration -> FilePath
gnuplotExe            :: !FilePath   -- ^ The executable to use to generate figures using the gnuplot toolkit.

    , Configuration -> FilePath
graphvizExe           :: !FilePath   -- ^ The executable to use to generate figures using the Graphviz toolkit.

    , Configuration -> FilePath
bokehExe              :: !FilePath   -- ^ The executable to use to generate figures using the Python/Bokeh toolkit.

    , Configuration -> FilePath
plotsjlExe            :: !FilePath   -- ^ The executable to use to generate figures using the Julia/Plots.jl toolkit.

    
    , Configuration -> Bool
matplotlibTightBBox   :: !Bool       -- ^ Whether or not to make Matplotlib figures tight by default.

    , Configuration -> Bool
matplotlibTransparent :: !Bool       -- ^ Whether or not to make Matplotlib figures transparent by default.

    } deriving (Configuration -> Configuration -> Bool
(Configuration -> Configuration -> Bool)
-> (Configuration -> Configuration -> Bool) -> Eq Configuration
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Configuration -> Configuration -> Bool
$c/= :: Configuration -> Configuration -> Bool
== :: Configuration -> Configuration -> Bool
$c== :: Configuration -> Configuration -> Bool
Eq, Int -> Configuration -> ShowS
[Configuration] -> ShowS
Configuration -> FilePath
(Int -> Configuration -> ShowS)
-> (Configuration -> FilePath)
-> ([Configuration] -> ShowS)
-> Show Configuration
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [Configuration] -> ShowS
$cshowList :: [Configuration] -> ShowS
show :: Configuration -> FilePath
$cshow :: Configuration -> FilePath
showsPrec :: Int -> Configuration -> ShowS
$cshowsPrec :: Int -> Configuration -> ShowS
Show)