module Graphics.Rendering.Ombra.Internal.Resource (
ResMap,
ResStatus(..),
Resource(..),
newResMap,
addResource,
getResource,
getResource',
checkResource,
removeResource,
unloader
) where
import Control.Monad.Base
import Control.Monad.Trans.Control
import qualified Data.HashTable.IO as H
import Data.Hashable
import System.Mem.Weak
data ResMap r = ResMap (H.LinearHashTable Int (Either String r))
data ResStatus r = Loaded r
| Unloaded
| Error String
class (Eq i, Applicative m, MonadBaseControl IO m, Hashable i) =>
Resource i r m where
loadResource :: i -> m (Either String r)
unloadResource :: Maybe i -> r -> m ()
newResMap :: MonadBase IO m => m (ResMap r)
newResMap = ResMap <$> liftBase H.new
addResource :: Resource i r m => i -> ResMap r -> m ()
addResource i m = () <$ getResource i m
checkResource :: Resource i r m
=> i
-> ResMap r
-> m (ResStatus r)
checkResource i = checkResource' (Just i) $ hash i
checkResource' :: Resource i r m
=> Maybe i
-> Int
-> ResMap r
-> m (ResStatus r)
checkResource' _ i (ResMap map) = do m <- liftBase $ H.lookup map i
return $ case m of
Just (Right r) -> Loaded r
Just (Left e) -> Error e
Nothing -> Unloaded
getResource :: Resource i r m => i -> ResMap r -> m (Either String r)
getResource i = getResource' (Just i) i
getResource' :: Resource i r m => Maybe k -> i -> ResMap r -> m (Either String r)
getResource' mk (i :: i) rmap@(ResMap map) =
do status <- checkResource i rmap
case status of
Unloaded ->
do r <- loadResource i
liftBase $
case r of
Left s -> H.insert map ihash $ Left s
Right r -> H.insert map ihash $ Right r
case mk of
Just k -> liftBaseDiscard (addFinalizer k) $
removeResource' (Nothing :: Maybe i)
ihash rmap
Nothing -> return ()
meRes <- liftBase . H.lookup map $ ihash
return $ case meRes of
Just eRes -> eRes
Nothing -> Left "Resource finalized"
Error s -> return $ Left s
Loaded r -> return $ Right r
where ihash = hash i
removeResource :: Resource i r m => i -> ResMap r -> m ()
removeResource i = removeResource' (Just i) $ hash i
removeResource' :: Resource i r m => Maybe i -> Int -> ResMap r -> m ()
removeResource' mi i rmap@(ResMap map) =
do status <- checkResource' mi i rmap
case status of
Loaded r -> unloadResource mi r
_ -> return ()
liftBase $ H.delete map i
unloader :: (Resource i r m, MonadBaseControl IO m) => k -> Maybe i -> r -> m ()
unloader k i r = liftBaseDiscard (addFinalizer k) $ unloadResource i r
instance Functor ResStatus where
fmap f (Loaded r) = Loaded (f r)
fmap _ Unloaded = Unloaded
fmap _ (Error s) = Error s