{-# OPTIONS_GHC -fno-warn-orphans #-} module Crypto.Hash.Instances () where import ClassyPrelude import Crypto.Hash hiding (hash) import Database.Persist import Database.Persist.Sql import Data.ByteArray (convert) import Web.PathPieces import Web.HttpApiData import Data.Aeson as Aeson import Control.Monad.Fail import Language.Haskell.TH.Syntax (Lift(liftTyped)) import Instances.TH.Lift () import Data.Binary import qualified Data.Binary.Put as Binary import qualified Data.Binary.Get as Binary import qualified Data.ByteString.Base64.URL as Base64 import Type.Reflection (typeRep) instance HashAlgorithm hash => PersistField (Digest hash) where toPersistValue = PersistByteString . convert fromPersistValue (PersistByteString bs) = maybe (Left "Could not convert Digest from ByteString") Right $ digestFromByteString bs fromPersistValue (PersistText t) = maybe (Left "Cours not convert Digest from String") Right $ readMay t fromPersistValue _ = Left "Digest values must be converted from PersistByteString or PersistText" instance HashAlgorithm hash => PersistFieldSql (Digest hash) where sqlType _ = SqlBlob instance HashAlgorithm hash => PathPiece (Digest hash) where toPathPiece = decodeUtf8 . Base64.encodeUnpadded . convert fromPathPiece = digestFromByteString <=< either (const Nothing) Just . Base64.decodeUnpadded . encodeUtf8 instance HashAlgorithm hash => ToHttpApiData (Digest hash) where toUrlPiece = toPathPiece instance HashAlgorithm hash => FromHttpApiData (Digest hash) where parseUrlPiece = maybe (Left "Could not read Digest") Right . fromPathPiece instance HashAlgorithm hash => ToJSON (Digest hash) where toJSON = Aeson.String . toUrlPiece instance HashAlgorithm hash => FromJSON (Digest hash) where parseJSON = withText "Digest" $ either (fail . unpack) return . parseUrlPiece instance Typeable hash => Hashable (Digest hash) where hashWithSalt s h = s `hashWithSalt` hash (typeRep @hash) `hashWithSalt` hash @ByteString (convert h) instance HashAlgorithm hash => Lift (Digest hash) where liftTyped dgst = [||fromMaybe (error "Lifted digest has wrong length") $ digestFromByteString $$(liftTyped (convert dgst :: ByteString))||] instance HashAlgorithm hash => Binary (Digest hash) where put = Binary.putByteString . convert get = Binary.getByteString (hashDigestSize (error "hashDigestSize inspected value of type hash" :: hash)) >>= maybe (fail "Could not parse Digest") return . digestFromByteString