From 015663657ceca7b168bf7d91fbc2fccc41c40904 Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Tue, 5 Apr 2011 22:14:49 +0200 Subject: New resource module hierarchy --- src/Hakyll/Core/Resource/Provider.hs | 68 +++++++++++++++++++++++++++++++ src/Hakyll/Core/Resource/Provider/File.hs | 30 ++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 src/Hakyll/Core/Resource/Provider.hs create mode 100644 src/Hakyll/Core/Resource/Provider/File.hs (limited to 'src/Hakyll/Core/Resource') diff --git a/src/Hakyll/Core/Resource/Provider.hs b/src/Hakyll/Core/Resource/Provider.hs new file mode 100644 index 0000000..377b029 --- /dev/null +++ b/src/Hakyll/Core/Resource/Provider.hs @@ -0,0 +1,68 @@ +-- | 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 (..) + , resourceExists + , resourceDigest + , resourceModified + ) where + +import Control.Monad ((<=<)) +import Data.Word (Word8) + +import qualified Data.ByteString.Lazy as LB +import OpenSSL.Digest.ByteString.Lazy (digest) +import OpenSSL.Digest (MessageDigest (MD5)) + +import Hakyll.Core.Identifier +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 + resourceLazyByteString :: Resource -> IO LB.ByteString + } + +-- | Check if a given identifier has a resource +-- +resourceExists :: ResourceProvider -> Identifier -> Bool +resourceExists provider = flip elem $ map unResource $ resourceList provider + +-- | Retrieve a digest for a given resource +-- +resourceDigest :: ResourceProvider -> Resource -> IO [Word8] +resourceDigest provider = digest MD5 <=< resourceLazyByteString provider + +-- | Check if a resource was modified +-- +resourceModified :: ResourceProvider -> Resource -> Store -> IO Bool +resourceModified provider resource store = do + -- Get the latest seen digest from the store + lastDigest <- storeGet store itemName $ unResource resource + -- Calculate the digest for the resource + newDigest <- resourceDigest provider resource + -- Check digests + if Just newDigest == lastDigest + -- All is fine, not modified + then return False + -- Resource modified; store new digest + else do storeSet store itemName (unResource resource) newDigest + return True + where + itemName = "Hakyll.Core.ResourceProvider.resourceModified" diff --git a/src/Hakyll/Core/Resource/Provider/File.hs b/src/Hakyll/Core/Resource/Provider/File.hs new file mode 100644 index 0000000..a795fac --- /dev/null +++ b/src/Hakyll/Core/Resource/Provider/File.hs @@ -0,0 +1,30 @@ +-- | A concrete 'ResourceProvider' that gets it's resources from the filesystem +-- +module Hakyll.Core.Resource.Provider.File + ( fileResourceProvider + ) where + +import Control.Applicative ((<$>)) + +import qualified Data.ByteString.Lazy as LB + +import Hakyll.Core.Resource +import Hakyll.Core.Resource.Provider +import Hakyll.Core.Identifier +import Hakyll.Core.Util.File +import Hakyll.Core.Configuration + +-- | Create a filesystem-based 'ResourceProvider' +-- +fileResourceProvider :: HakyllConfiguration -> IO ResourceProvider +fileResourceProvider configuration = do + -- Retrieve a list of identifiers + list <- map parseIdentifier . filter (not . ignoreFile configuration) <$> + getRecursiveContents False "." + + -- Construct a resource provider + return ResourceProvider + { resourceList = map Resource list + , resourceString = readFile . toFilePath . unResource + , resourceLazyByteString = LB.readFile . toFilePath . unResource + } -- cgit v1.2.3 From 3d2b2506d040546d74e83f6d9b8b4e0c45026f09 Mon Sep 17 00:00:00 2001 From: Jasper Van der Jeugt Date: Wed, 6 Apr 2011 09:30:24 +0200 Subject: Move modified cache to resource provider Currently, it's kept twice: in Run and in the Provider. On the long term, it should be migrated entirely to the Provider, this can be done when the new dependency manager is finished. --- src/Hakyll/Core/Resource/Provider.hs | 25 ++++++++++++++++++++++++- src/Hakyll/Core/Resource/Provider/File.hs | 6 ++++++ src/Hakyll/Core/Run.hs | 4 +--- 3 files changed, 31 insertions(+), 4 deletions(-) (limited to 'src/Hakyll/Core/Resource') diff --git a/src/Hakyll/Core/Resource/Provider.hs b/src/Hakyll/Core/Resource/Provider.hs index 377b029..90e93f8 100644 --- a/src/Hakyll/Core/Resource/Provider.hs +++ b/src/Hakyll/Core/Resource/Provider.hs @@ -17,8 +17,11 @@ module Hakyll.Core.Resource.Provider , resourceModified ) where +import Control.Concurrent (MVar, readMVar, modifyMVar_) import Control.Monad ((<=<)) import Data.Word (Word8) +import Data.Map (Map) +import qualified Data.Map as M import qualified Data.ByteString.Lazy as LB import OpenSSL.Digest.ByteString.Lazy (digest) @@ -37,6 +40,8 @@ data ResourceProvider = ResourceProvider resourceString :: Resource -> IO String , -- | Retrieve a certain resource as lazy bytestring resourceLazyByteString :: Resource -> IO LB.ByteString + , -- | Cache keeping track of modified items + resourceModifiedCache :: MVar (Map Resource Bool) } -- | Check if a given identifier has a resource @@ -53,6 +58,24 @@ resourceDigest provider = digest MD5 <=< resourceLazyByteString provider -- resourceModified :: ResourceProvider -> Resource -> Store -> IO Bool resourceModified provider resource store = 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 (unResource resource) + then digestModified provider resource store + 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 -> Resource -> Store -> IO Bool +digestModified provider resource store = do -- Get the latest seen digest from the store lastDigest <- storeGet store itemName $ unResource resource -- Calculate the digest for the resource @@ -65,4 +88,4 @@ resourceModified provider resource store = do else do storeSet store itemName (unResource resource) newDigest return True where - itemName = "Hakyll.Core.ResourceProvider.resourceModified" + itemName = "Hakyll.Core.ResourceProvider.digestModified" diff --git a/src/Hakyll/Core/Resource/Provider/File.hs b/src/Hakyll/Core/Resource/Provider/File.hs index a795fac..953d61c 100644 --- a/src/Hakyll/Core/Resource/Provider/File.hs +++ b/src/Hakyll/Core/Resource/Provider/File.hs @@ -5,6 +5,8 @@ module Hakyll.Core.Resource.Provider.File ) where import Control.Applicative ((<$>)) +import Control.Concurrent (newMVar) +import qualified Data.Map as M import qualified Data.ByteString.Lazy as LB @@ -22,9 +24,13 @@ fileResourceProvider configuration = do list <- map parseIdentifier . filter (not . ignoreFile configuration) <$> getRecursiveContents False "." + -- MVar for the cache + mvar <- newMVar M.empty + -- Construct a resource provider return ResourceProvider { resourceList = map Resource list , resourceString = readFile . toFilePath . unResource , resourceLazyByteString = LB.readFile . toFilePath . unResource + , resourceModifiedCache = mvar } diff --git a/src/Hakyll/Core/Run.hs b/src/Hakyll/Core/Run.hs index 8e1ba6d..54bb104 100644 --- a/src/Hakyll/Core/Run.hs +++ b/src/Hakyll/Core/Run.hs @@ -100,9 +100,7 @@ modified :: ResourceProvider -- ^ Resource provider -> [Identifier] -- ^ Identifiers to check -> IO (Set Identifier) -- ^ Modified resources modified provider store ids = fmap S.fromList $ flip filterM ids $ \id' -> - if resourceExists provider id' - then resourceModified provider (Resource id') store - else return False + resourceModified provider (Resource id') store -- | Add a number of compilers and continue using these compilers -- -- cgit v1.2.3