{-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} -- | This is an internal module. module Puppet.Interpreter.IO where import Puppet.PP import Puppet.Interpreter.Types import Puppet.Interpreter.PrettyPrinter() import Puppet.Plugins() import Control.Monad.Operational import Control.Monad.RSS.Strict import Control.Monad.State.Strict import Control.Lens import qualified Data.ByteString as BS import qualified Data.Either.Strict as S import GHC.Stack import Debug.Trace (traceEventIO) import qualified Data.Text as T import qualified Data.Text.IO as T import Control.Exception import qualified Scripting.Lua as Lua import Control.Concurrent.MVar import Data.Tuple.Strict (Pair(..)) import System.Log.Logger (Priority(..)) bs :: BS.ByteString -> PrettyError bs = PrettyError . string . show defaultImpureMethods :: (Functor m, MonadIO m) => ImpureMethods m defaultImpureMethods = ImpureMethods (liftIO currentCallStack) (liftIO . file) (liftIO . traceEventIO) (\c fname args -> liftIO (runlua c fname args)) where file [] = return $ Left "" file (x:xs) = fmap Right (T.readFile (T.unpack x)) `catch` (\SomeException{} -> file xs) runlua c fname args = liftIO $ withMVar c $ \lstt -> catch (fmap Right (Lua.callfunc lstt (T.unpack fname) args)) (\e -> return $ Left $ show (e :: SomeException)) evalInstrGen :: (Functor m, Monad m) => InterpreterReader m -> InterpreterState -> ProgramViewT InterpreterInstr (State InterpreterState) a -> m (Either PrettyError a, InterpreterState, InterpreterWriter) evalInstrGen _ stt (Return x) = return (Right x, stt, mempty) evalInstrGen rdr stt (a :>>= f) = let runC a' = interpretMonad rdr stt (f a') thpe = interpretMonad rdr stt . throwPosError . getError pdb = _pdbAPI rdr strFail iof errf = iof >>= \case Left rr -> thpe (errf (string rr)) Right x -> runC x canFail iof = iof >>= \case S.Left rr -> thpe rr S.Right x -> runC x logStuff x c = (_3 %~ (x <>)) `fmap` c in case a of ExternalFunction fname args -> case rdr ^. externalFunctions . at fname of Just fn -> interpretMonad rdr stt ( fn args >>= f) Nothing -> thpe (PrettyError ("Unknown function: " <> ttext fname)) GetStatement topleveltype toplevelname -> canFail ((rdr ^. getStatement) topleveltype toplevelname) ComputeTemplate fn scp cscps -> canFail ((rdr ^. computeTemplateFunction) fn scp cscps) WriterTell t -> logStuff t (runC ()) WriterPass _ -> thpe "WriterPass" WriterListen _ -> thpe "WriterListen" GetNativeTypes -> runC (rdr ^. nativeTypes) ErrorThrow d -> return (Left d, stt, mempty) ErrorCatch _ _ -> thpe "ErrorCatch" GetNodeName -> runC (rdr ^. thisNodename) hq@(HieraQuery scps q t) -> logStuff [DEBUG :!: pretty hq] (canFail ((rdr ^. hieraQuery) scps q t)) PDBInformation -> pdbInformation pdb >>= runC PDBReplaceCatalog w -> canFail (replaceCatalog pdb w) PDBReplaceFacts fcts -> canFail (replaceFacts pdb fcts) PDBDeactivateNode nn -> canFail (deactivateNode pdb nn) PDBGetFacts q -> canFail (getFacts pdb q) PDBGetResources q -> canFail (getResources pdb q) PDBGetNodes q -> canFail (getNodes pdb q) PDBCommitDB -> canFail (commitDB pdb) PDBGetResourcesOfNode nn q -> canFail (getResourcesOfNode pdb nn q) GetCurrentCallStack -> (rdr ^. ioMethods . imGetCurrentCallStack) >>= runC ReadFile fls -> strFail ((rdr ^. ioMethods . imReadFile) fls) (const $ PrettyError ("No file found in " <> list (map ttext fls))) TraceEvent e -> (rdr ^. ioMethods . imTraceEvent) e >>= runC IsIgnoredModule m -> runC (rdr ^. ignoredModules . contains m) CallLua c fname args -> (rdr ^. ioMethods . imCallLua) c fname args >>= \case Right x -> runC x Left rr -> thpe (PrettyError (string rr)) interpretMonad :: (Functor m, Monad m) => InterpreterReader m -> InterpreterState -> InterpreterMonad a -> m (Either PrettyError a, InterpreterState, InterpreterWriter) interpretMonad rd_ prmstate instr = case runState (viewT instr) prmstate of (!a,!nextstate) -> evalInstrGen rd_ nextstate a