@@ -1,125 +1,45 @@
1--------------------------------------------------------------------------------
1--------------------------------------------------------------------------------
2−-- | This module provides an API for resource providers. Resource providers
2+-- | This module provides an wrapper API around the file system which does some
3−-- allow Hakyll to get content from resources; the type of resource depends on
3+-- caching.
4−-- the concrete instance.
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.
11−-- Therefore, it is not recommended to read files directly -- you should use the
12−-- provided 'Resource' methods.
14module Hakyll.Core.Resource.Provider
4module Hakyll.Core.Resource.Provider
5+ ( -- * Constructing resource providers
15− ( ResourceProvider (..)
6+ ResourceProvider
9+ -- * Querying resource properties
16 , resourceList
10 , resourceList
17− , makeResourceProvider
18 , resourceExists
11 , resourceExists
20 , resourceModified
12 , resourceModified
21− ) where
13+ , resourceModificationTime
24−--------------------------------------------------------------------------------
25−import Control.Applicative ((<$>))
26−import Control.Concurrent (MVar, readMVar, modifyMVar_, newMVar)
28−import qualified Data.Map as M
29−import qualified Data.Set as S
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
39−--------------------------------------------------------------------------------
40−import Hakyll.Core.Store (Store)
41−import Hakyll.Core.Resource
42−import qualified Hakyll.Core.Store as Store
15+ -- * Access to raw resource content
45−--------------------------------------------------------------------------------
19+ -- * Access to metadata and body content
46−-- | A value responsible for retrieving and listing resources
20+ , resourceMetadata
47−data ResourceProvider = ResourceProvider
21+ , resourceBody
48− { -- | A set of all resources this provider is able to provide
22+ ) where
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)
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
72−--------------------------------------------------------------------------------
73−-- | Get the list of all resources
74−resourceList :: ResourceProvider -> [Resource]
75−resourceList = S.toList . resourceSet
78−--------------------------------------------------------------------------------
79−-- | Check if a given identifier has a resource
80−resourceExists :: ResourceProvider -> Resource -> Bool
81−resourceExists provider = flip S.member $ resourceSet provider
84--------------------------------------------------------------------------------
25--------------------------------------------------------------------------------
85−-- | Retrieve a digest for a given resource
26+import Hakyll.Core.Resource
86−resourceDigest :: ResourceProvider -> Resource -> IO B.ByteString
27+import qualified Hakyll.Core.Resource.MetadataCache as Internal
87−resourceDigest provider = fmap MD5.hashlazy . resourceLBS provider
28+import Hakyll.Core.Resource.Modified
29+import Hakyll.Core.Resource.Provider.Internal
90--------------------------------------------------------------------------------
32--------------------------------------------------------------------------------
91−-- | Check if a resource was modified
33+-- | Wrapper to ensure metadata cache is invalidated if necessary
92−resourceModified :: ResourceProvider -> Store -> Resource -> IO Bool
34+resourceMetadata :: ResourceProvider -> Resource -> IO Metadata
93−resourceModified provider store r = do
35+resourceMetadata rp r = do
94− cache <- readMVar mvar
36+ _ <- resourceModified rp r
95− case M.lookup r cache of
37+ Internal.resourceMetadata rp r
96− -- Already in the cache
98− -- Not yet in the cache, check digests (if it exists)
100− m <- if resourceExists provider r
101− then digestModified provider store r
103− modifyMVar_ mvar (return . M.insert r m)
106− mvar = resourceModifiedCache provider
109--------------------------------------------------------------------------------
40--------------------------------------------------------------------------------
110−-- | Check if a resource digest was modified
41+-- | Wrapper to ensure metadata cache is invalidated if necessary
111−digestModified :: ResourceProvider -> Store -> Resource -> IO Bool
42+resourceBody :: ResourceProvider -> Resource -> IO String
112−digestModified provider store r = do
43+resourceBody rp r = do
113− -- Get the latest seen digest from the store
44+ _ <- resourceModified rp r
114− lastDigest <- Store.get store key
45+ Internal.resourceBody rp r
115− -- Calculate the digest for the resource
116− newDigest <- resourceDigest provider r
118− if Store.Found newDigest == lastDigest
119− -- All is fine, not modified
121− -- Resource modified; store new digest
122− else do Store.set store key newDigest
125− key = ["Hakyll.Core.ResourceProvider.digestModified", unResource r]