-- | Private module. Evaluates a ruby template from what's generated by "Erb.Parser". module Puppet.Runner.Erb.Evaluate ( rubyEvaluate ) where import XPrelude import qualified Data.Char as Char import qualified Data.HashMap.Strict as HM import qualified Data.Text as Text import qualified Data.Vector as V import Erb.Ruby import Puppet.Interpreter type ScopeName = Text -- | Evaluate a list of ruby statements. rubyEvaluate :: Container ScopeInformation -> ScopeName -> [RubyStatement] -> Either Doc Text rubyEvaluate vars ctx = foldl (evalruby vars ctx) (Right "") . optimize where optimize [] = [] optimize (Puts x : DropPrevSpace' : xs) = optimize $ DropPrevSpace (Puts x) : xs optimize (x:xs) = x : optimize xs spaceNotCR :: Char -> Bool spaceNotCR c = Char.isSpace c && c /= '\n' && c /= '\r' evalruby :: Container ScopeInformation -> ScopeName -> Either Doc Text -> RubyStatement -> Either Doc Text evalruby _ _ (Left err) _ = Left err evalruby _ _ (Right _) (DropPrevSpace') = Left "Could not evaluate a non optimize DropPrevSpace'" evalruby mp ctx (Right curstr) (DropNextSpace x) = case evalruby mp ctx (Right curstr) x of Left err -> Left err Right y -> Right (Text.dropWhile spaceNotCR y) evalruby mp ctx (Right curstr) (DropPrevSpace x) = case evalruby mp ctx (Right curstr) x of Left err -> Left err Right y -> Right (Text.dropWhileEnd spaceNotCR y) evalruby mp ctx (Right curstr) (Puts e) = case (evalExpression mp ctx e >>= evalValue) of Left err -> Left err Right ex -> Right (curstr <> ex) evalExpression :: Container ScopeInformation -> ScopeName -> Expression -> Either Doc PValue evalExpression mp ctx (LookupOperation expvar expidx) = do val <- evalExpression mp ctx expvar idx <- evalExpression mp ctx expidx case val of PArray arr -> case idx ^? _PValueInteger of Nothing -> Left $ "Can't convert index to integer when resolving" <+> pretty val <> brackets (pretty idx) Just i -> if fromIntegral (V.length arr) <= i then Left $ "Array out of bound" <+> pretty val <> brackets (pretty idx) else Right (arr V.! fromIntegral i) PHash hs -> case idx of PString idx' -> case hs ^. at idx' of Just x' -> Right x' _ -> Left $ "Can't index variable" <+> pretty val <+> ", it is " <+> pretty (PHash hs) _ -> Left $ "Can't index variable" <+> pretty val <+> ", it is " <+> pretty (PHash hs) unexpectedval -> Left $ "Can't index variable" <+> pretty val <+> ", it is " <+> pretty unexpectedval evalExpression _ _ (Value (Literal x)) = Right (PString x) evalExpression mp ctx (ScopeObject (Value (Literal x))) = getVariable mp ctx x evalExpression mp ctx (Object (Value (Literal x))) = do case Text.stripPrefix "@" x of Nothing -> Left $ "Erb variables '" <> ppline x <> "' should be prefixed by '@' in puppet version 4 and above." Just x' -> getVariable mp ctx x' evalExpression _ _ x = Left $ "Can't evaluate" <+> pretty x evalValue :: PValue -> Either Doc Text evalValue = go False where go escaped p = case p of PString x -> Right $ if escaped then show x else x PNumber x -> Right (scientific2text x) PUndef -> Right "nil" PBoolean True -> Right "true" PBoolean False -> Right "false" PArray lst -> fmap (\c -> "[" <> Text.intercalate ", " c <> "]") (mapM (go True) (V.toList lst)) PHash hash -> fmap (\l -> "{" <> Text.intercalate ", " (map (\(k,v) -> show k <> "=>" <> v) l) <> "}") (mapM (traverse (go True)) (HM.toList hash)) _ -> Left ("Can't display the ruby equivalent of" <+> pretty p)