content: {-|
  Module      : Slick.Shake
  Description : DEPRECATED -- Advanced caching tools for using slick with shake
  Copyright   : (c) Chris Penner, 2019
  License     : BSD3
  -}
  {-# LANGUAGE DeriveGeneric              #-}
  {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  {-# LANGUAGE ScopedTypeVariables        #-}
  {-# LANGUAGE TypeFamilies               #-}
  
  module Slick.Caching {-# DEPRECATED "No longer necessary with slick >= 0.3.0.0" #-}
    ( simpleJsonCache
    , simpleJsonCache'
    , jsonCache
    , jsonCache'
    )
  where
  
  import Data.Aeson                 as A
  import Data.ByteString.Lazy
  import Development.Shake          hiding (Resource)
  import Development.Shake.Classes
  import GHC.Generics               (Generic)
  
  --------------------------------------------------------------------------------
  
  newtype CacheQuery q =
    CacheQuery q
    deriving (Show, Eq, Generic, Binary, NFData, Hashable)
  
  type instance RuleResult (CacheQuery q) = ByteString
  
  -- | Note that you probably don't need this if you're using the recommended @Development.Shake.Forward@ module. It can do a lot of caching for you, otherwise look at 'Development.Shake.Forward.cacheAction'.
  --
  -- A wrapper around 'addOracleCache' which given a @q@ which is a 'ShakeValue'
  -- allows caching and retrieving 'Value's within Shake. See documentation on
  -- 'addOracleCache' or see Slick examples for more info.
  --
  -- > -- We need to define a unique datatype as our cache key
  -- > newtype PostFilePath =
  -- >   PostFilePath String
  -- > -- We can derive the classes we need (using GeneralizedNewtypeDeriving)
  -- > -- so long as the underlying type implements them
  -- >   deriving (Show, Eq, Hashable, Binary, NFData)
  -- > -- now in our shake rules we can create a cache by providing a loader action
  -- >
  -- > do
  -- > postCache <- jsonCache $ \(PostFilePath path) ->
  -- >   readFile' path >>= markdownToHTML . Text.pack
  -- > -- Now use postCache inside an Action to load your post with caching!
  jsonCache :: ShakeValue q => (q -> Action Value) -> Rules (q -> Action Value)
  jsonCache = jsonCache'
  
  -- | Like 'jsonCache' but allows caching/retrieving any JSON serializable
  -- objects.
  jsonCache'
    :: forall a q
     . (ToJSON a, FromJSON a, ShakeValue q)
    => (q -> Action a)
    -> Rules (q -> Action a)
  jsonCache' loader = unpackJSON
    <$> addOracleCache (\(CacheQuery q) -> A.encode <$> loader q)
   where
    unpackJSON
      :: FromJSON a => (CacheQuery q -> Action ByteString) -> q -> Action a
    unpackJSON runCacheQuery = \q -> do
      bytes <- runCacheQuery $ CacheQuery q
      case A.eitherDecode bytes of
        Left  err -> fail err
        Right res -> pure res
  
  -- | Note that you probably don't need this if you're using the recommended @Development.Shake.Forward@ module. It can do a lot of caching for you, otherwise look at 'Development.Shake.Forward.cacheAction'.
  --
  -- A wrapper around 'jsonCache' which simplifies caching of values which do NOT
  -- depend on an input parameter. Unfortunately Shake still requires that the
  -- key type implement several typeclasses, however this is easily accomplished
  -- using @GeneralizedNewtypeDeriving@ and a wrapper around @()@.
  -- example usage:
  --
  -- > {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  -- > module Main where
  -- > newtype ProjectList = ProjectList ()
  -- >   deriving (Show, Eq, Hashable, Binary, NFData)
  --  Within your shake Rules:
  --
  -- > projectCache = simpleJsonCache (ProjectList ()) $ do
  -- >   -- load your project list here; returning it as a Value
  simpleJsonCache :: ShakeValue q => q -> Action Value -> Rules (Action Value)
  simpleJsonCache = simpleJsonCache'
  
  -- | Like 'simpleJsonCache' but allows caching any JSON serializable object.
  simpleJsonCache'
    :: forall q a
     . (ToJSON a, FromJSON a, ShakeValue q)
    => q
    -> Action a
    -> Rules (Action a)
  simpleJsonCache' q loader = do
    cacheGetter <- jsonCache' (const loader)
    return $ cacheGetter q
  