language-puppet-1.3.13: src/Puppet/Interpreter/Utils.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
-- | The module should not depend on the Interpreter module. It is an
-- internal module and should not be used if expecting a stable API.
module Puppet.Interpreter.Utils where
import Puppet.Prelude
import Control.Monad.Operational
import Control.Monad.Writer.Class
import qualified Data.HashMap.Strict as HM
import qualified Data.List as List
import qualified Data.Maybe.Strict as S
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import qualified System.Log.Logger as Log
import Puppet.Interpreter.Types
import Puppet.Parser.Types
import Puppet.Parser.Utils
import Puppet.Paths
import Puppet.PP
initialState :: Facts
-> Container Text -- ^ Server settings
-> InterpreterState
initialState facts settings =
InterpreterState baseVars initialclass mempty [ContRoot] dummyppos mempty [] []
where
callervars = HM.fromList [("caller_module_name", PString "::" :!: dummyppos :!: ContRoot), ("module_name", PString "::" :!: dummyppos :!: ContRoot)]
factvars =
-- add the `facts` key: https://docs.puppet.com/puppet/4.10/lang_facts_and_builtin_vars.html#accessing-facts-from-puppet-code
let facts' = HM.insert "facts" (PHash facts) facts
in fmap (\x -> x :!: initialPPos "facts" :!: ContRoot) facts'
settingvars = fmap (\x -> PString x :!: initialPPos "settings" :!: ContClass "settings") settings
baseVars = HM.fromList [ ("::", ScopeInformation (factvars `mappend` callervars) mempty mempty (CurContainer ContRoot mempty) mempty S.Nothing)
, ("settings", ScopeInformation settingvars mempty mempty (CurContainer (ContClass "settings") mempty) mempty S.Nothing)
]
initialclass = mempty & at "::" ?~ (ClassIncludeLike :!: dummyppos)
getModulename :: RIdentifier -> Text
getModulename (RIdentifier t n) =
let gm x =
case Text.splitOn "::" x of
[] -> x
(y:_) -> y
in case t of
"class" -> gm n
_ -> gm t
extractPrism :: Doc -> Prism' a b -> a -> InterpreterMonad b
extractPrism msg p a =
case preview p a of
Just b -> return b
Nothing -> throwPosError ("Could not extract prism in" <+> msg)
-- Scope
popScope :: InterpreterMonad ()
popScope = curScope %= List.tail
pushScope :: CurContainerDesc -> InterpreterMonad ()
pushScope s = curScope %= (s :)
getScopeName :: InterpreterMonad Text
getScopeName = scopeName <$> getScope
scopeName :: CurContainerDesc -> Text
scopeName (ContRoot ) = "::"
scopeName (ContImported x ) = "::imported::" `Text.append` scopeName x
scopeName (ContClass x ) = x
scopeName (ContDefine dt dn _) = "#define/" `Text.append` dt `Text.append` "/" `Text.append` dn
scopeName (ContImport _ x ) = "::import::" `Text.append` scopeName x
moduleName :: CurContainerDesc -> Text
moduleName (ContRoot ) = "::"
moduleName (ContImported x ) = moduleName x
moduleName (ContClass x ) = x
moduleName (ContDefine dt _ _) = dt
moduleName (ContImport _ x ) = moduleName x
getScope :: InterpreterMonad CurContainerDesc
{-# INLINABLE getScope #-}
getScope =
use curScope >>= \s ->
if null s
then throwPosError "Internal error: empty scope!"
else pure (List.head s)
getCurContainer :: InterpreterMonad CurContainer
{-# INLINABLE getCurContainer #-}
getCurContainer = do
scp <- getScopeName
preuse (scopes . ix scp . scopeContainer) >>= \case
Just x -> return x
Nothing -> throwPosError ("Internal error: can't find the current container for" <+> green (string (Text.unpack scp)))
rcurcontainer :: Resource -> CurContainerDesc
rcurcontainer r = fromMaybe ContRoot (r ^? rscope . _head)
-- Singleton getters available in the InterpreterMonad --
getPuppetPaths :: InterpreterMonad PuppetDirPaths
getPuppetPaths = singleton PuppetPaths
getNodeName:: InterpreterMonad NodeName
getNodeName = singleton GetNodeName
isIgnoredModule :: Text -> InterpreterMonad Bool
isIgnoredModule m = singleton (IsIgnoredModule m)
-- | Throws an error if we are in strict mode
-- A warning in permissive mode
checkStrict :: Doc -- ^ The warning message.
-> Doc -- ^ The error message.
-> InterpreterMonad ()
checkStrict wrn err = do
extMod <- isExternalModule
let priority =
if extMod
then Log.NOTICE
else Log.WARNING
str <- singleton IsStrict
if str && not extMod
then throwPosError err
else do
srcname <- use (curPos . _1 . lSourceName)
logWriter priority (wrn <+> "at" <+> string srcname)
isExternalModule :: InterpreterMonad Bool
isExternalModule =
getScope >>= \case
ContClass n -> isExternal n
ContDefine n _ _ -> isExternal n
_ -> return False
where
isExternal = singleton . IsExternalModule . List.head . Text.splitOn "::"
-- Logging --
warn :: MonadWriter InterpreterWriter m => Doc -> m ()
warn d = tell [Log.WARNING :!: d]
debug :: MonadWriter InterpreterWriter m => Doc -> m ()
debug d = tell [Log.DEBUG :!: d]
logWriter :: MonadWriter InterpreterWriter m => Log.Priority -> Doc -> m ()
logWriter prio d = tell [prio :!: d]
safeDecodeUtf8 :: ByteString -> InterpreterMonad Text
{-# INLINABLE safeDecodeUtf8 #-}
safeDecodeUtf8 i = return (Text.decodeUtf8 i)
normalizeRIdentifier :: Text -> Text -> RIdentifier
normalizeRIdentifier = RIdentifier . dropInitialColons
readQueryType :: Text -> Maybe HieraQueryType
readQueryType s =
case s of
"first" -> Just QFirst
"unique" -> Just QUnique
"hash" -> Just QHash
_ -> Nothing