--------------------------------------------------------------------------------
-- | This module provides an wrapper API around the file system which does some
-- caching.
module Hakyll.Core.Provider
    ( -- * Constructing resource providers
      Internal.Provider
    , newProvider

      -- * Querying resource properties
    , Internal.resourceList
    , Internal.resourceExists
    , Internal.resourceFilePath
    , Internal.resourceModified
    , Internal.resourceModificationTime

      -- * Access to raw resource content
    , Internal.resourceString
    , Internal.resourceLBS

      -- * Access to metadata and body content
    , Internal.resourceMetadata
    , Internal.resourceBody
    ) where

--------------------------------------------------------------------------------
import           Control.Monad                      (forM)
import qualified Hakyll.Core.Provider.Internal      as Internal
import qualified Hakyll.Core.Provider.MetadataCache as Internal
import           Hakyll.Core.Store                  (Store)
import           Hakyll.Core.Identifier             (Identifier)


--------------------------------------------------------------------------------
-- | Create a resource provider
newProvider :: Store                                -- ^ Store to use
            -> (FilePath -> IO Bool)                -- ^ Should we ignore this file?
            -> FilePath                             -- ^ Search directory
            -> IO (Internal.Provider, [Identifier]) -- ^ Resulting provider and modified metadata
newProvider :: Store
-> (FilePath -> IO Bool) -> FilePath -> IO (Provider, [Identifier])
newProvider Store
store FilePath -> IO Bool
ignore FilePath
directory = do
  p <- Store -> (FilePath -> IO Bool) -> FilePath -> IO Provider
Internal.newProvider Store
store FilePath -> IO Bool
ignore FilePath
directory

  let modified =
        (Identifier -> Bool) -> [Identifier] -> [Identifier]
forall a. (a -> Bool) -> [a] -> [a]
filter
          (Provider -> Identifier -> Bool
Internal.resourceModified Provider
p)
          (Provider -> [Identifier]
Internal.resourceList Provider
p)

  -- Delete metadata cache where necessary and extract identifiers for which the
  -- metadata was actually modified. That is, exclude identifiers where the body
  -- was modified but the metadata wasn't.
  modifiedMetadata <- fmap concat $ forM modified $ \Identifier
identifier -> do
    mbCachedMetadata <- Provider -> Identifier -> IO (Maybe Metadata)
Internal.resourceLookupMetadataCache Provider
p Identifier
identifier
    case mbCachedMetadata of
      Maybe Metadata
Nothing -> [Identifier] -> IO [Identifier]
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure []
      Just Metadata
oldMeta -> do
        Provider -> Identifier -> IO ()
Internal.resourceInvalidateMetadataCache Provider
p Identifier
identifier
        newMeta <- Provider -> Identifier -> IO Metadata
Internal.resourceMetadata Provider
p Identifier
identifier
        pure $ if oldMeta == newMeta then [] else [identifier]

  return (p, modifiedMetadata)