{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE Safe          #-}

module Valida.ValidationRule
    ( ValidationRule (..)
    , vrule
    ) where

import Data.Typeable (Typeable)

import GHC.Generics (Generic)

import Valida.Validation (Validation (..))

{- | The rule a Validator uses to run validation.

Contains a function that accepts the target type and returns a `Validation` result.

The type- __ValidationRule (NonEmpty String) Int__, designates a rule that verifies the validity of an __Int__, and
uses a value of type __NonEmpty String__ to represent error, in case of failure.
-}
newtype ValidationRule e a
  -- | Builds a 'ValidationRule' from a function to generate error and a validation predicate.

  = ValidationRule
  -- ^ The validation predicate.

    (a -> Validation e ())
  deriving (Typeable, (forall x. ValidationRule e a -> Rep (ValidationRule e a) x)
-> (forall x. Rep (ValidationRule e a) x -> ValidationRule e a)
-> Generic (ValidationRule e a)
forall x. Rep (ValidationRule e a) x -> ValidationRule e a
forall x. ValidationRule e a -> Rep (ValidationRule e a) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall e a x. Rep (ValidationRule e a) x -> ValidationRule e a
forall e a x. ValidationRule e a -> Rep (ValidationRule e a) x
$cto :: forall e a x. Rep (ValidationRule e a) x -> ValidationRule e a
$cfrom :: forall e a x. ValidationRule e a -> Rep (ValidationRule e a) x
Generic)

{- |

[@(<>)@] '(<>)' creates a new `ValidationRule` that only succeeds when both given rule succeed.
Otherwise left-most failure is returned.

__Examples__

>>> runValidator (validate (failureIf even "IsEven" <> failureIf (>9) "GreaterThan9")) 5
Success 5
>>> runValidator (validate (failureIf even "IsEven" <> failureIf (>9) "GreaterThan9")) 4
Failure ("IsEven" :| [])
>>> runValidator (validate (failureIf even "IsEven" <> failureIf (>9) "GreaterThan9")) 15
Failure ("GreaterThan9" :| [])
>>> runValidator (validate (failureIf even "IsEven" <> failureIf (>9) "GreaterThan9")) 12
Failure ("IsEven" :| [])
-}
instance Semigroup (ValidationRule e a) where
    ValidationRule a -> Validation e ()
rl1 <> :: ValidationRule e a -> ValidationRule e a -> ValidationRule e a
<> ValidationRule a -> Validation e ()
rl2 = (a -> Validation e ()) -> ValidationRule e a
forall e a. (a -> Validation e ()) -> ValidationRule e a
ValidationRule
        ((a -> Validation e ()) -> ValidationRule e a)
-> (a -> Validation e ()) -> ValidationRule e a
forall a b. (a -> b) -> a -> b
$ \a
x -> case (a -> Validation e ()
rl1 a
x, a -> Validation e ()
rl2 a
x) of
            (f :: Validation e ()
f@(Failure e
_), Validation e ()
_) -> Validation e ()
f
            (Validation e ()
_, Validation e ()
b)             -> Validation e ()
b

{- |

[@mempty@] 'mempty' is a 'ValidationRule' that always succeeds.

__Examples__

>>> runValidator (validate mempty) 'a'
Success 'a'
-}
instance Monoid (ValidationRule e a) where
    mempty :: ValidationRule e a
mempty = (a -> Validation e ()) -> ValidationRule e a
forall e a. (a -> Validation e ()) -> ValidationRule e a
ValidationRule ((a -> Validation e ()) -> ValidationRule e a)
-> (a -> Validation e ()) -> ValidationRule e a
forall a b. (a -> b) -> a -> b
$ Validation e () -> a -> Validation e ()
forall a b. a -> b -> a
const (Validation e () -> a -> Validation e ())
-> Validation e () -> a -> Validation e ()
forall a b. (a -> b) -> a -> b
$ () -> Validation e ()
forall e a. a -> Validation e a
Success ()

{- | Low level function to manually build a `ValidationRule`. You should use the combinators instead.

==== __Examples__

>>> runValidator (validate (vrule (\x -> if isDigit x then Success () else Failure "NotDigit"))) 'a'
Failure "NotDigit"
>>> runValidator (validate (vrule (\x -> if isDigit x then Success () else Failure "NotDigit"))) '5'
Success '5'
-}
vrule :: (a -> Validation e ()) -> ValidationRule e a
vrule :: (a -> Validation e ()) -> ValidationRule e a
vrule = (a -> Validation e ()) -> ValidationRule e a
forall e a. (a -> Validation e ()) -> ValidationRule e a
ValidationRule