module Text.Strapped.Render
( combineBuckets
, varBucket
, bucketLookup
, bucketFromList
, emptyBucket
, render
, defaultConfig
) where
import Blaze.ByteString.Builder
import Blaze.ByteString.Builder.Char.Utf8
import Control.Monad
import qualified Data.Map as M
import Data.List (intersperse)
import Data.Monoid ((<>), mempty, mconcat)
import Control.Monad.Except
import Control.Monad.IO.Class
import Data.Maybe (catMaybes)
import qualified Data.Text.Lazy as T
import Text.Strapped.Types
import Text.Parsec.Pos
instance Renderable Builder where
renderOutput _ = id
instance Renderable Literal where
renderOutput (RenderConfig _ ef) (LitText s) = fromLazyText $ ef s
renderOutput _ (LitSafe s) = fromLazyText s
renderOutput rc (LitInteger i) = fromShow i
renderOutput rc (LitDouble i) = fromShow i
renderOutput _ (LitBuilder b) = b
renderOutput rc (LitList l) = (fromChar '[') <>
(mconcat $ intersperse (fromChar ',') (map (renderOutput rc) l)) <>
(fromChar ']')
renderOutput rc (LitDyn r) = renderOutput rc r
defaultConfig :: RenderConfig
defaultConfig = RenderConfig (\_ -> return Nothing) id
combineBuckets :: InputBucket m -> InputBucket m -> InputBucket m
combineBuckets = (++)
varBucket :: String -> Input m -> InputBucket m
varBucket varName o = [M.fromList [(varName, o)]]
emptyBucket :: InputBucket m
emptyBucket = []
bucketLookup :: String -> InputBucket m -> Maybe (Input m)
bucketLookup v [] = Nothing
bucketLookup v (m:ms) = maybe (bucketLookup v ms) Just (M.lookup v m)
bucketFromList :: [(String, Input m)] -> InputBucket m
bucketFromList l = [M.fromList l]
getOrThrow v getter pos = maybe (throwError $ InputNotFound v pos) return (bucketLookup v getter)
reduceExpression :: Monad m => RenderConfig -> ParsedExpression -> InputBucket m -> ExceptT StrapError m Literal
reduceExpression c (ParsedExpression exp pos) getter = convert exp
where convertMore exp = reduceExpression c exp getter
convert (IntegerExpression i) = return $ LitInteger i
convert (FloatExpression i) = return $ LitDouble i
convert (StringExpression s) = return $ LitText (T.pack s)
convert (Multipart []) = return $ LitEmpty
convert (Multipart (f:[])) = convertMore f
convert (Multipart ((ParsedExpression (LookupExpression func) ipos):args)) = do
val <- getOrThrow func getter pos
case val of
(Func f) -> convert (Multipart args) >>= f
_ -> throwError $ StrapError ("`" ++ func ++ "` is not a function but has args: " ++ (show args)) ipos
convert (Multipart v) = throwError $ StrapError ("`" ++ (show v) ++ "` cannot be reduced.") pos
convert (ListExpression args) = mapM convertMore args >>= (return . LitList)
convert (LookupExpression f) = do
val <- getOrThrow f getter pos
inputToLiteral val
inputToLiteral inp = case inp of
(List args) -> mapM inputToLiteral args >>= (return . LitList)
(RenderVal r) -> return $ LitBuilder (renderOutput c r)
(Func f) -> f LitEmpty
(LitVal v) -> return v
render :: MonadIO m => RenderConfig -> InputBucket m -> String -> m (Either StrapError Output)
render renderConfig getter' tmplName = do
tmpl <- liftIO $ tmplStore tmplName
maybe (return $ Left $ TemplateNotFound tmplName (initialPos tmplName))
(\(Template c) -> runExceptT $ loop mempty mempty getter' c)
tmpl
where tmplStore = templateStore renderConfig
loop accum _ _ [] = return accum
loop accum blks getter ((ParsedPiece (StaticPiece s) pos):ps) =
loop (accum <> s) blks getter ps
loop accum blks getter ((ParsedPiece (BlockPiece n def) pos):ps) =
(maybe (loop accum blks getter def)
(\content -> loop accum blks getter content)
(lookup n blks)
) >>= (\a -> loop a blks getter ps)
loop accum blks getter ((ParsedPiece (ForPiece n exp c) pos):ps) = do
var <- reduceExpression renderConfig exp getter
case var of
LitList l -> (processFor getter n c accum blks l) >>= (\a -> loop a blks getter ps)
_ -> throwError $ StrapError ("`" ++ show exp ++ "` is not a LitList") pos
loop accum blks getter ((ParsedPiece (Inherits n b) pos):ps) =
liftIO (tmplStore n) >>=
maybe (throwError (TemplateNotFound n pos))
(\(Template c) -> (loop accum (b ++ blks) getter c) >>=
(\a -> loop a blks getter ps))
loop accum blks getter ((ParsedPiece (Include n) pos):ps) =
liftIO (tmplStore n) >>=
maybe (throwError (TemplateNotFound n pos))
(\(Template c) -> (loop accum blks getter c) >>=
(\a -> loop a blks getter ps))
loop accum blks getter ((ParsedPiece (Decl n exp) pos):ps) =
(reduceExpression renderConfig exp getter) >>=
(\v -> loop accum blks (combineBuckets (varBucket n (LitVal v)) getter) ps)
loop accum blks getter ((ParsedPiece (FuncPiece exp) pos):ps) =
(reduceExpression renderConfig exp getter) >>=
(\r -> loop (accum <> (renderOutput renderConfig r)) blks getter ps)
processFor getter varName content accum blks objs = loopFor accum objs
where loopGetter o = combineBuckets (varBucket varName (LitVal o)) getter
loopFor accum [] = return accum
loopFor accum (o:os) = do
s <- loop accum blks (loopGetter o) content
loopFor s os