module Crypto.Error.Types
    ( CryptoError(..)
    , CryptoFailable(..)
    , throwCryptoErrorIO
    , throwCryptoError
    , onCryptoFailure
    , eitherCryptoError
    , maybeCryptoError
    ) where
import qualified Control.Exception as E
import           Data.Data
import           Basement.Monad (MonadFailure(..))
import           Crypto.Internal.Imports
data CryptoError =
    
      CryptoError_KeySizeInvalid
    | CryptoError_IvSizeInvalid
    | CryptoError_SeedSizeInvalid
    | CryptoError_AEADModeNotSupported
    
    | CryptoError_SecretKeySizeInvalid
    | CryptoError_SecretKeyStructureInvalid
    | CryptoError_PublicKeySizeInvalid
    | CryptoError_SharedSecretSizeInvalid
    
    | CryptoError_EcScalarOutOfBounds
    | CryptoError_PointSizeInvalid
    | CryptoError_PointFormatInvalid
    | CryptoError_PointFormatUnsupported
    | CryptoError_PointCoordinatesInvalid
    | CryptoError_ScalarMultiplicationInvalid
    
    | CryptoError_MacKeyInvalid
    | CryptoError_AuthenticationTagSizeInvalid
    
    | CryptoError_PrimeSizeInvalid
    
    | CryptoError_SaltTooSmall
    | CryptoError_OutputLengthTooSmall
    | CryptoError_OutputLengthTooBig
    deriving (Show,Eq,Enum,Data,Typeable)
instance E.Exception CryptoError
data CryptoFailable a =
      CryptoPassed a
    | CryptoFailed CryptoError
    deriving (Show)
instance Eq a => Eq (CryptoFailable a) where
    (==) (CryptoPassed a)  (CryptoPassed b)  = a == b
    (==) (CryptoFailed e1) (CryptoFailed e2) = e1 == e2
    (==) _                 _                 = False
instance Functor CryptoFailable where
    fmap f (CryptoPassed a) = CryptoPassed (f a)
    fmap _ (CryptoFailed r) = CryptoFailed r
instance Applicative CryptoFailable where
    pure a     = CryptoPassed a
    (<*>) fm m = fm >>= \p -> m >>= \r2 -> return (p r2)
instance Monad CryptoFailable where
    return a = CryptoPassed a
    (>>=) m1 m2 = do
        case m1 of
            CryptoPassed a -> m2 a
            CryptoFailed e -> CryptoFailed e
instance MonadFailure CryptoFailable where
    type Failure CryptoFailable = CryptoError
    mFail = CryptoFailed
throwCryptoErrorIO :: CryptoFailable a -> IO a
throwCryptoErrorIO (CryptoFailed e) = E.throwIO e
throwCryptoErrorIO (CryptoPassed r) = return r
throwCryptoError :: CryptoFailable a -> a
throwCryptoError (CryptoFailed e) = E.throw e
throwCryptoError (CryptoPassed r) = r
onCryptoFailure :: (CryptoError -> r) -> (a -> r) -> CryptoFailable a -> r
onCryptoFailure onError _         (CryptoFailed e) = onError e
onCryptoFailure _       onSuccess (CryptoPassed r) = onSuccess r
eitherCryptoError :: CryptoFailable a -> Either CryptoError a
eitherCryptoError (CryptoFailed e) = Left e
eitherCryptoError (CryptoPassed a) = Right a
maybeCryptoError :: CryptoFailable a -> Maybe a
maybeCryptoError (CryptoFailed _) = Nothing
maybeCryptoError (CryptoPassed r) = Just r