on 5dba607 help sign in
103 lines 4.0 kB history raw
1-- | This module provides an API for resource providers. Resource providers
2-- allow Hakyll to get content from resources; the type of resource depends on
3-- the concrete instance.
4--
5-- A resource is represented by the 'Resource' type. This is basically just a
6-- newtype wrapper around 'Identifier' -- but it has an important effect: it
7-- guarantees that a resource with this identifier can be provided by one or
8-- more resource providers.
9--
10-- Therefore, it is not recommended to read files directly -- you should use the
11-- provided 'Resource' methods.
12--
13module Hakyll.Core.Resource.Provider
14 ( ResourceProvider (..)
15 , makeResourceProvider
16 , resourceExists
17 , resourceDigest
18 , resourceModified
19 ) where
21import Control.Applicative ((<$>))
22import Control.Concurrent (MVar, readMVar, modifyMVar_, newMVar)
23import Data.Map (Map)
24import qualified Data.Map as M
26import Data.Time (UTCTime)
27import qualified Crypto.Hash.MD5 as MD5
28import qualified Data.ByteString as B
29import qualified Data.ByteString.Lazy as LB
31import Hakyll.Core.Store
32import Hakyll.Core.Resource
34-- | A value responsible for retrieving and listing resources
35--
36data ResourceProvider = ResourceProvider
37 { -- | A list of all resources this provider is able to provide
38 resourceList :: [Resource]
39 , -- | Retrieve a certain resource as string
40 resourceString :: Resource -> IO String
41 , -- | Retrieve a certain resource as lazy bytestring
42 resourceLBS :: Resource -> IO LB.ByteString
43 , -- | Check when a resource was last modified
44 resourceModificationTime :: Resource -> IO UTCTime
45 , -- | Cache keeping track of modified items
46 resourceModifiedCache :: MVar (Map Resource Bool)
47 }
49-- | Create a resource provider
50--
51makeResourceProvider :: [Resource] -- ^ Resource list
52 -> (Resource -> IO String) -- ^ String reader
53 -> (Resource -> IO LB.ByteString) -- ^ ByteString reader
54 -> (Resource -> IO UTCTime) -- ^ Time checker
55 -> IO ResourceProvider -- ^ Resulting provider
56makeResourceProvider l s b t = ResourceProvider l s b t <$> newMVar M.empty
58-- | Check if a given identifier has a resource
59--
60resourceExists :: ResourceProvider -> Resource -> Bool
61resourceExists provider = flip elem $ resourceList provider
63-- | Retrieve a digest for a given resource
64--
65resourceDigest :: ResourceProvider -> Resource -> IO B.ByteString
66resourceDigest provider = fmap MD5.hashlazy . resourceLBS provider
68-- | Check if a resource was modified
69--
70resourceModified :: ResourceProvider -> Store -> Resource -> IO Bool
71resourceModified provider store resource = do
72 cache <- readMVar mvar
73 case M.lookup resource cache of
74 -- Already in the cache
75 Just m -> return m
76 -- Not yet in the cache, check digests (if it exists)
77 Nothing -> do
78 m <- if resourceExists provider resource
79 then digestModified provider store resource
80 else return False
81 modifyMVar_ mvar (return . M.insert resource m)
82 return m
83 where
84 mvar = resourceModifiedCache provider
86-- | Check if a resource digest was modified
87--
88digestModified :: ResourceProvider -> Store -> Resource -> IO Bool
89digestModified provider store resource = do
90 -- Get the latest seen digest from the store
91 lastDigest <- storeGet store itemName identifier
92 -- Calculate the digest for the resource
93 newDigest <- resourceDigest provider resource
94 -- Check digests
95 if Found newDigest == lastDigest
96 -- All is fine, not modified
97 then return False
98 -- Resource modified; store new digest
99 else do storeSet store itemName identifier newDigest
100 return True
101 where
102 identifier = toIdentifier resource
103 itemName = "Hakyll.Core.ResourceProvider.digestModified"