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