{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} module AppCache where import Control.Monad (when) import Control.Monad.Trans.Writer import Data.Hashable (hashWithSalt) import Data.List (intercalate) import qualified Data.Set as Set import Data.Text (Text) import Data.Text (pack) import Language.Haskell.TH.Syntax import Yesod.Core import Yesod.Routes.TH newtype AppCache = AppCache { unAppCache :: Text } appCache :: [ResourceTree String] -> Q Exp appCache trees = do piecesSet <- execWriterT $ mapM_ (goTree id) trees let body = unlines $ map toPath $ Set.toList piecesSet hash = hashWithSalt 0 body total = concat [ "CACHE MANIFEST\n# Version: " , show hash , "\n\nCACHE:\n" , body ] [|return (AppCache (pack total))|] where toPath [] = "/" toPath x = concatMap ('/':) x goTree :: Monad m => ([String] -> [String]) -> ResourceTree String -> WriterT (Set.Set [String]) m () goTree front (ResourceLeaf res) = do pieces' <- goPieces (resourceName res) $ resourcePieces res when ("CACHE" `elem` resourceAttrs res) $ tell $ Set.singleton $ front pieces' goTree front (ResourceParent name pieces trees) = do pieces' <- goPieces name pieces mapM_ (goTree $ front . (pieces' ++)) trees goPieces :: Monad m => String -> [(CheckOverlap, Piece String)] -> m [String] goPieces name = mapM (goPiece . snd) where goPiece (Static s) = return s goPiece (Dynamic _) = fail $ concat [ "AppCache only applies to fully-static paths, but " , name , " has dynamic pieces." ] instance ToContent AppCache where toContent = toContent . unAppCache instance ToTypedContent AppCache where toTypedContent = TypedContent "text/cache-manifest" . toContent