@@ -1,125 +1,45 @@
11--------------------------------------------------------------------------------
2−-- | This module provides an API for resource providers. Resource providers
3−-- allow Hakyll to get content from resources; the type of resource depends on
4−-- the concrete instance.
5−--
6−-- A resource is represented by the 'Resource' type. This is basically just a
7−-- newtype wrapper around 'Identifier' -- but it has an important effect: it
8−-- guarantees that a resource with this identifier can be provided by one or
9−-- more resource providers.
10−--
11−-- Therefore, it is not recommended to read files directly -- you should use the
12−-- provided 'Resource' methods.
13−--
2+-- | This module provides an wrapper API around the file system which does some
3+-- caching.
144module Hakyll.Core.Resource.Provider
15− ( ResourceProvider (..)
5+ ( -- * Constructing resource providers
6+ ResourceProvider
7+ , newResourceProvider
8+
9+ -- * Querying resource properties
1610 , resourceList
17− , makeResourceProvider
1811 , resourceExists
19− , resourceDigest
2012 , resourceModified
21− ) where
22−
23−
24−--------------------------------------------------------------------------------
25−import Control.Applicative ((<$>))
26−import Control.Concurrent (MVar, readMVar, modifyMVar_, newMVar)
27−import Data.Map (Map)
28−import qualified Data.Map as M
29−import qualified Data.Set as S
30−
31−
32−--------------------------------------------------------------------------------
33−import Data.Time (UTCTime)
34−import qualified Crypto.Hash.MD5 as MD5
35−import qualified Data.ByteString as B
36−import qualified Data.ByteString.Lazy as LB
37−
38−
39−--------------------------------------------------------------------------------
40−import Hakyll.Core.Store (Store)
41−import Hakyll.Core.Resource
42−import qualified Hakyll.Core.Store as Store
13+ , resourceModificationTime
4314
15+ -- * Access to raw resource content
16+ , resourceString
17+ , resourceLBS
4418
45−--------------------------------------------------------------------------------
46−-- | A value responsible for retrieving and listing resources
47−data ResourceProvider = ResourceProvider
48− { -- | A set of all resources this provider is able to provide
49− resourceSet :: S.Set Resource
50− , -- | Retrieve a certain resource as string
51− resourceString :: Resource -> IO String
52− , -- | Retrieve a certain resource as lazy bytestring
53− resourceLBS :: Resource -> IO LB.ByteString
54− , -- | Check when a resource was last modified
55− resourceModificationTime :: Resource -> IO UTCTime
56− , -- | Cache keeping track of modified items
57− resourceModifiedCache :: MVar (Map Resource Bool)
58− }
59−
60−
61−--------------------------------------------------------------------------------
62−-- | Create a resource provider
63−makeResourceProvider :: [Resource] -- ^ Resource list
64− -> (Resource -> IO String) -- ^ String reader
65− -> (Resource -> IO LB.ByteString) -- ^ ByteString reader
66− -> (Resource -> IO UTCTime) -- ^ Time checker
67− -> IO ResourceProvider -- ^ Resulting provider
68−makeResourceProvider l s b t =
69− ResourceProvider (S.fromList l) s b t <$> newMVar M.empty
70−
71−
72−--------------------------------------------------------------------------------
73−-- | Get the list of all resources
74−resourceList :: ResourceProvider -> [Resource]
75−resourceList = S.toList . resourceSet
76−
77−
78−--------------------------------------------------------------------------------
79−-- | Check if a given identifier has a resource
80−resourceExists :: ResourceProvider -> Resource -> Bool
81−resourceExists provider = flip S.member $ resourceSet provider
19+ -- * Access to metadata and body content
20+ , resourceMetadata
21+ , resourceBody
22+ ) where
8223
8324
8425--------------------------------------------------------------------------------
85−-- | Retrieve a digest for a given resource
86−resourceDigest :: ResourceProvider -> Resource -> IO B.ByteString
87−resourceDigest provider = fmap MD5.hashlazy . resourceLBS provider
26+import Hakyll.Core.Resource
27+import qualified Hakyll.Core.Resource.MetadataCache as Internal
28+import Hakyll.Core.Resource.Modified
29+import Hakyll.Core.Resource.Provider.Internal
8830
8931
9032--------------------------------------------------------------------------------
91−-- | Check if a resource was modified
92−resourceModified :: ResourceProvider -> Store -> Resource -> IO Bool
93−resourceModified provider store r = do
94− cache <- readMVar mvar
95− case M.lookup r cache of
96− -- Already in the cache
97− Just m -> return m
98− -- Not yet in the cache, check digests (if it exists)
99− Nothing -> do
100− m <- if resourceExists provider r
101− then digestModified provider store r
102− else return False
103− modifyMVar_ mvar (return . M.insert r m)
104− return m
105− where
106− mvar = resourceModifiedCache provider
33+-- | Wrapper to ensure metadata cache is invalidated if necessary
34+resourceMetadata :: ResourceProvider -> Resource -> IO Metadata
35+resourceMetadata rp r = do
36+ _ <- resourceModified rp r
37+ Internal.resourceMetadata rp r
10738
10839
10940--------------------------------------------------------------------------------
110−-- | Check if a resource digest was modified
111−digestModified :: ResourceProvider -> Store -> Resource -> IO Bool
112−digestModified provider store r = do
113− -- Get the latest seen digest from the store
114− lastDigest <- Store.get store key
115− -- Calculate the digest for the resource
116− newDigest <- resourceDigest provider r
117− -- Check digests
118− if Store.Found newDigest == lastDigest
119− -- All is fine, not modified
120− then return False
121− -- Resource modified; store new digest
122− else do Store.set store key newDigest
123− return True
124− where
125− key = ["Hakyll.Core.ResourceProvider.digestModified", unResource r]
41+-- | Wrapper to ensure metadata cache is invalidated if necessary
42+resourceBody :: ResourceProvider -> Resource -> IO String
43+resourceBody rp r = do
44+ _ <- resourceModified rp r
45+ Internal.resourceBody rp r