hakyll/src/Hakyll/Core/Store.hs

126 lines
3.8 KiB
Haskell
Raw Normal View History

2010-12-26 15:12:57 +00:00
-- | A store for stroing and retreiving items
--
2011-05-18 22:41:39 +00:00
{-# LANGUAGE ExistentialQuantification, ScopedTypeVariables #-}
2010-12-26 15:12:57 +00:00
module Hakyll.Core.Store
( Store
2011-05-17 08:57:37 +00:00
, StoreGet (..)
2010-12-26 15:12:57 +00:00
, makeStore
, storeSet
, storeGet
) where
2012-05-12 11:17:20 +00:00
import Control.Applicative ((<$>))
import Control.Concurrent.MVar (MVar, newMVar, readMVar, modifyMVar_)
2010-12-26 15:12:57 +00:00
import System.FilePath ((</>))
import System.Directory (doesFileExist)
import Data.Maybe (fromMaybe)
import Data.Map (Map)
import qualified Data.Map as M
2010-12-26 15:12:57 +00:00
import Data.Binary (Binary, encodeFile, decodeFile)
2011-05-18 22:41:39 +00:00
import Data.Typeable (Typeable, TypeRep, cast, typeOf)
2010-12-26 15:12:57 +00:00
import Hakyll.Core.Identifier
import Hakyll.Core.Util.File
import qualified Data.Cache.LRU.IO as LRU
2010-12-26 15:12:57 +00:00
-- | Items we can store
--
data Storable = forall a. (Binary a, Typeable a) => Storable a
2011-05-17 08:57:37 +00:00
-- | Result when an item from the store
--
data StoreGet a = Found a
| NotFound
2011-05-18 22:41:39 +00:00
| WrongType TypeRep TypeRep
deriving (Show, Eq)
2011-05-17 08:57:37 +00:00
2010-12-26 15:12:57 +00:00
-- | Data structure used for the store
--
data Store = Store
{ -- | All items are stored on the filesystem
storeDirectory :: FilePath
, -- | And some items are also kept in-memory
storeLRU :: Maybe (LRU.AtomicLRU FilePath Storable)
2010-12-26 15:12:57 +00:00
}
-- | The size of the in-memory cache to use in items.
storeLRUSize :: Maybe Integer
storeLRUSize = Just 500
2010-12-26 15:12:57 +00:00
-- | Initialize the store
--
2012-05-12 11:17:20 +00:00
makeStore :: Bool -- ^ Use in-memory caching
-> FilePath -- ^ Directory to use for hard disk storage
-> IO Store -- ^ Store
makeStore inMemory directory = do
2012-08-08 00:37:23 +00:00
lru <- if inMemory
then Just <$> LRU.newAtomicLRU storeLRUSize
else return Nothing
return Store
{ storeDirectory = directory
, storeLRU = lru
}
-- | Auxiliary: add an item to the map
--
2012-05-12 11:17:20 +00:00
cacheInsert :: (Binary a, Typeable a) => Store -> FilePath -> a -> IO ()
2012-08-08 00:37:23 +00:00
cacheInsert (Store _ Nothing) _ _ = return ()
cacheInsert (Store _ (Just lru)) path value =
LRU.insert path (Storable value) lru
2012-05-12 11:17:20 +00:00
-- | Auxiliary: get an item from the cache
--
cacheLookup :: forall a. (Binary a, Typeable a)
=> Store -> FilePath -> IO (StoreGet a)
cacheLookup (Store _ Nothing) _ = return NotFound
cacheLookup (Store _ (Just lru)) path = do
res <- LRU.lookup path lru
case res of
2012-08-08 00:37:23 +00:00
Nothing -> return NotFound
Just (Storable s) -> return $ case cast s of
Nothing -> WrongType (typeOf s) $ typeOf (undefined :: a)
Just s' -> Found s'
2010-12-26 15:12:57 +00:00
-- | Create a path
--
2011-05-24 09:58:13 +00:00
makePath :: Store -> String -> Identifier a -> FilePath
makePath store name identifier = storeDirectory store </> name
</> group </> toFilePath identifier </> "hakyllstore"
where
group = fromMaybe "" $ identifierGroup identifier
2010-12-26 15:12:57 +00:00
-- | Store an item
--
storeSet :: (Binary a, Typeable a)
2011-05-24 09:58:13 +00:00
=> Store -> String -> Identifier a -> a -> IO ()
2010-12-26 15:12:57 +00:00
storeSet store name identifier value = do
makeDirectories path
encodeFile path value
2012-05-12 11:17:20 +00:00
cacheInsert store path value
2010-12-26 15:12:57 +00:00
where
path = makePath store name identifier
-- | Load an item
--
2012-05-12 11:17:20 +00:00
storeGet :: (Binary a, Typeable a)
2011-05-24 09:58:13 +00:00
=> Store -> String -> Identifier a -> IO (StoreGet a)
2010-12-26 15:12:57 +00:00
storeGet store name identifier = do
-- First check the in-memory map
2012-05-12 11:17:20 +00:00
mv <- cacheLookup store path
case mv of
-- Not found in the map, try the filesystem
2012-05-12 11:17:20 +00:00
NotFound -> do
exists <- doesFileExist path
if not exists
-- Not found in the filesystem either
2011-05-17 08:57:37 +00:00
then return NotFound
-- Found in the filesystem
else do v <- decodeFile path
2012-05-12 11:17:20 +00:00
cacheInsert store path v
2011-05-17 08:57:37 +00:00
return $ Found v
2012-05-12 11:17:20 +00:00
-- Found in the in-memory map, just return
s -> return s
2010-12-26 15:12:57 +00:00
where
path = makePath store name identifier