    --------------------------------------------------------------------------------
    module Hakyll.Core.Compiler.Require
        ( Snapshot
        , save
        , saveSnapshot
        , load
        , loadSnapshot
        , loadBody
        , loadSnapshotBody
        , loadAll
        , loadAllSnapshots
        ) where

    --------------------------------------------------------------------------------
    import           Control.Applicative            ((<$>))
    import           Control.Monad                  (when)
    import           Data.Binary                    (Binary)
    import qualified Data.Set                       as S
    import           Data.Typeable

    --------------------------------------------------------------------------------
    import           Hakyll.Core.Compiler.Internal
    import           Hakyll.Core.Dependencies
    import           Hakyll.Core.Identifier
    import           Hakyll.Core.Identifier.Pattern
    import           Hakyll.Core.Item
    import           Hakyll.Core.Metadata
    import           Hakyll.Core.Store              (Store)
    import qualified Hakyll.Core.Store              as Store

    --------------------------------------------------------------------------------
    save :: (Binary a, Typeable a) => Store -> Item a -> IO ()
    save store item = saveSnapshot store final item

    --------------------------------------------------------------------------------
    -- | Save a specific snapshot of an item, so you can load it later using
    -- 'loadSnapshot'.
    saveSnapshot :: (Binary a, Typeable a)
                 => Store -> Snapshot -> Item a -> IO ()
    saveSnapshot store snapshot item =
        Store.set store (key (itemIdentifier item) snapshot) (itemBody item)

    --------------------------------------------------------------------------------
    -- | Load an item compiled elsewhere. If the required item is not yet compiled,
    -- the build system will take care of that automatically.
    load :: (Binary a, Typeable a) => Identifier -> Compiler (Item a)
    load id' = loadSnapshot id' final

    --------------------------------------------------------------------------------
    -- | Require a specific snapshot of an item.
    loadSnapshot :: (Binary a, Typeable a)
                 => Identifier -> Snapshot -> Compiler (Item a)
    loadSnapshot id' snapshot = do
        store    <- compilerStore <$> compilerAsk
        universe <- compilerUniverse <$> compilerAsk

        -- Quick check for better error messages
        when (id' `S.notMember` universe) $ fail notFound

        compilerTellDependencies [IdentifierDependency id']
        compilerResult $ CompilerRequire (id', snapshot) $ do
            result <- compilerUnsafeIO $ Store.get store (key id' snapshot)
            case result of
                Store.NotFound      -> fail notFound
                Store.WrongType e r -> fail $ wrongType e r
                Store.Found x       -> return $ Item id' x
      where
        notFound =
            "Hakyll.Core.Compiler.Require.load: " ++ show id' ++
            " (snapshot " ++ snapshot ++ ") was not found in the cache, " ++
            "the cache might be corrupted or " ++
            "the item you are referring to might not exist"
        wrongType e r =
            "Hakyll.Core.Compiler.Require.load: " ++ show id' ++
            " (snapshot " ++ snapshot ++ ") was found in the cache, " ++
            "but does not have the right type: expected " ++ show e ++
            " but got " ++ show r

    --------------------------------------------------------------------------------
    -- | A shortcut for only requiring the body of an item.
    --
    -- > loadBody = fmap itemBody . load
    loadBody :: (Binary a, Typeable a) => Identifier -> Compiler a
    loadBody id' = loadSnapshotBody id' final

    --------------------------------------------------------------------------------
    loadSnapshotBody :: (Binary a, Typeable a)
                     => Identifier -> Snapshot -> Compiler a
    loadSnapshotBody id' snapshot = fmap itemBody $ loadSnapshot id' snapshot

    --------------------------------------------------------------------------------
    -- | This function allows you to 'load' a dynamic list of items
    loadAll :: (Binary a, Typeable a) => Pattern -> Compiler [Item a]
    loadAll pattern = loadAllSnapshots pattern final

    --------------------------------------------------------------------------------
    loadAllSnapshots :: (Binary a, Typeable a)
                     => Pattern -> Snapshot -> Compiler [Item a]
    loadAllSnapshots pattern snapshot = do
        matching <- getMatches pattern
        mapM (\i -> loadSnapshot i snapshot) matching

    --------------------------------------------------------------------------------
    key :: Identifier -> String -> [String]
    key identifier snapshot =
        ["Hakyll.Core.Compiler.Require", show identifier, snapshot]

    --------------------------------------------------------------------------------
    final :: Snapshot
    final = "_final"