-- | A safe interface to SHA that still lets you digest incrementally
module Codec.Digest.SHA.Monad(
  SHA, SHAT, Length(..), runSHAT, runSHA, Hashable(..),
) where

import Codec.Digest.SHA.IO
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import Control.Monad.ST

import Control.Monad.UnsafeIO
import Control.Monad.Reader

type SHAT m a = ReaderT SHACtx m a

type SHA s a = SHAT (ST s) a

runSHAT :: MonadUnsafeIO m => Length -> SHAT m a -> m (a,B.ByteString)
runSHAT len shat = do
  ctx <- liftUnsafeIO $ newCtx len
  wrapped <- runReaderT shat ctx
  hash <- liftUnsafeIO $ unsafeFinalizeCtx ctx
  return (wrapped,hash)

runSHA :: Length
         -> (forall s. SHA s a)
         -> (a,B.ByteString)
runSHA len sha = runST $ runSHAT len sha

-- | Hash some data, combining it with everything already hashed in
-- this context
class Hashable a where
  update :: MonadUnsafeIO m => a -> SHAT m ()

instance Hashable B.ByteString where
  update bs = do
    ctx <- ask
    liftUnsafeIO $ unsafeUpdateCtx ctx bs
instance Hashable BL.ByteString where
  update bs = mapM_ update (BL.toChunks bs)