AppCache.hs 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. {-# LANGUAGE OverloadedStrings #-}
  2. {-# LANGUAGE TemplateHaskell #-}
  3. module AppCache where
  4. import Control.Monad (when)
  5. import Control.Monad.Trans.Writer
  6. import Data.Hashable (hashWithSalt)
  7. import Data.List (intercalate)
  8. import qualified Data.Set as Set
  9. import Data.Text (Text)
  10. import Data.Text (pack)
  11. import Language.Haskell.TH.Syntax
  12. import Yesod.Core
  13. import Yesod.Routes.TH
  14. newtype AppCache = AppCache { unAppCache :: Text }
  15. appCache :: [ResourceTree String] -> Q Exp
  16. appCache trees = do
  17. piecesSet <- execWriterT $ mapM_ (goTree id) trees
  18. let body = unlines $ map toPath $ Set.toList piecesSet
  19. hash = hashWithSalt 0 body
  20. total = concat
  21. [ "CACHE MANIFEST\n# Version: "
  22. , show hash
  23. , "\n\nCACHE:\n"
  24. , body
  25. ]
  26. [|return (AppCache (pack total))|]
  27. where
  28. toPath [] = "/"
  29. toPath x = concatMap ('/':) x
  30. goTree :: Monad m
  31. => ([String] -> [String])
  32. -> ResourceTree String
  33. -> WriterT (Set.Set [String]) m ()
  34. goTree front (ResourceLeaf res) = do
  35. pieces' <- goPieces (resourceName res) $ resourcePieces res
  36. when ("CACHE" `elem` resourceAttrs res) $
  37. tell $ Set.singleton $ front pieces'
  38. goTree front (ResourceParent name pieces trees) = do
  39. pieces' <- goPieces name pieces
  40. mapM_ (goTree $ front . (pieces' ++)) trees
  41. goPieces :: Monad m => String -> [(CheckOverlap, Piece String)] -> m [String]
  42. goPieces name =
  43. mapM (goPiece . snd)
  44. where
  45. goPiece (Static s) = return s
  46. goPiece (Dynamic _) = fail $ concat
  47. [ "AppCache only applies to fully-static paths, but "
  48. , name
  49. , " has dynamic pieces."
  50. ]
  51. instance ToContent AppCache where
  52. toContent = toContent . unAppCache
  53. instance ToTypedContent AppCache where
  54. toTypedContent = TypedContent "text/cache-manifest" . toContent