{-# LANGUAGE Safe #-}

{- |
Module      : Valida
Description : Core Validator builders, utilities, and combinators
Copyright   : (c) TotallyNotChase, 2021
License     : MIT
Maintainer  : totallynotchase42@gmail.com
Stability   : Stable
Portability : Portable

This module exports the primary validator building functions. It also exports all of "Valida.Combinators".

For a full tutorial, check out the README at <https://github.com/TotallyNotChase/valida#readme>.
Refer to the hackage documentation for function reference and examples.

You can also find more examples within the examples directory in linked github repo.
-}

module Valida
    ( -- * Primary data types

      Validation (..)
    , Validator (runValidator)
      -- * Building and modifying 'Validator's

    , fixV
    , verify
    , (-?>)
      -- * Reassigning errors

    , label
    , (<?>)
      -- | Re-exports of "Valida.Combinators"

    , module Valida.Combinators
    , module Valida.ValidationUtils
    ) where

import Data.Bifunctor  (Bifunctor (first))
import Data.Profunctor (Profunctor (lmap))

import Valida.Combinators
import Valida.Validation      (Validation (..))
import Valida.ValidationUtils
import Valida.Validator       (Validator (..))

{- | An alias to 'lmap' specialized to 'Validator'.

'verify' allows a validator taking input /b/ to work with input /a/, provided a function of type: @a -> b@.

The new 'Validator` first runs the __selector__ on its input to obtain the validation target. Then, it runs the
predicate on the target.

If validation is successful, the the *original* output is put into the 'Validation' result.

==== __Examples__

This is the primary functions to build validators for product types.
To validate a pair, the most basic product type, such that the first element is a non empty string, and the second
element is a number greater than 9, you can use:

>>> let pairValidator = (,) <$> verify (notEmpty "EmptyString") fst <*> verify (failureIf (<10) "LessThan10") snd

You can then run the validator on your input, using 'runValidator':

>>> runValidator pairValidator ("foo", 12)
Success ("foo",12)
>>> runValidator pairValidator ("", 12)
Failure ("EmptyString" :| [])
>>> runValidator pairValidator ("foo", 9)
Failure ("LessThan10" :| [])
>>> runValidator pairValidator ("", 9)
Failure ("EmptyString" :| ["LessThan10"])
-}
verify :: Validator e b x -> (a -> b) -> Validator e a x
verify :: Validator e b x -> (a -> b) -> Validator e a x
verify = ((a -> b) -> Validator e b x -> Validator e a x)
-> Validator e b x -> (a -> b) -> Validator e a x
forall a b c. (a -> b -> c) -> b -> a -> c
flip (a -> b) -> Validator e b x -> Validator e a x
forall (p :: * -> * -> *) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap

-- | A synonym for 'verify' with its arguments flipped.

infix 5 -?>

(-?>) :: (a -> b) -> Validator e b x -> Validator e a x
-?> :: (a -> b) -> Validator e b x -> Validator e a x
(-?>) = (a -> b) -> Validator e b x -> Validator e a x
forall (p :: * -> * -> *) a b c.
Profunctor p =>
(a -> b) -> p b c -> p a c
lmap

{- | Fix a validator's output to be the same as its input.

@fixV . fixV = 'id' . fixV@

@'fmap' ('const' x) .  fixV = 'fmap' ('const' x)@

__Note__: The primitive and derivative combinators already fix the validator output to be the
same as its input.

==== __Examples__

This is useful for regaining the input value in the output position after multiple 'fmap's.

Assume we have a validator that fails when input number is even-

>>> let evenValidator = failureIf even "Even"

This validator, when run, will yield its input value, wrapped in 'Success', if input is not even. 'fixV' would be redundant on this.

However, if the output was 'fmap'ed to be something else-

>>> let evenValidator' = fmap (:[]) evenValidator

Now the output type is `[Int]`. The value of the output is no longer the same as the input. If we needed to get the
original input back into the output, 'fixV' would be the right choice.

>>> let evenValidator'' = fixV evenValidator'

evenValidator'' is now the exact same as evenValidator, which was fixed from the start.

>>> (("foo" <$ failureIf even "Even") <> ("bar" <$ failureIf (<0) "Negative")) `runValidator` 5
Success "bar"
>>> fixV (("foo" <$ failureIf even "Even") <> ("bar" <$ failureIf (<0) "Negative")) `runValidator` 5
Success 5
-}
fixV :: Validator e a x -> Validator e a a
fixV :: Validator e a x -> Validator e a a
fixV (Validator a -> Validation e x
v) = (a -> Validation e a) -> Validator e a a
forall e inp a. (inp -> Validation e a) -> Validator e inp a
Validator ((a -> Validation e a) -> Validator e a a)
-> (a -> Validation e a) -> Validator e a a
forall a b. (a -> b) -> a -> b
$ \a
x -> a
x a -> Validation e x -> Validation e a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ a -> Validation e x
v a
x

---------------------------------------------------------------------

-- Reassigning corresponding error to 'Validator'.

---------------------------------------------------------------------


{- | Relabel a 'Validator' with a different error.

==== __Examples__

>>> let validator = label "NotEven" (failureUnless' even)
>>> runValidator validator 1
Failure "NotEven"

>>> let validator = label "DefinitelyNotEven" (failureUnless even "NotEven")
>>> runValidator validator 1
Failure "DefinitelyNotEven"
-}
label :: e -> Validator x inp a -> Validator e inp a
label :: e -> Validator x inp a -> Validator e inp a
label e
err (Validator inp -> Validation x a
v) = (inp -> Validation e a) -> Validator e inp a
forall e inp a. (inp -> Validation e a) -> Validator e inp a
Validator ((inp -> Validation e a) -> Validator e inp a)
-> (inp -> Validation e a) -> Validator e inp a
forall a b. (a -> b) -> a -> b
$ (x -> e) -> Validation x a -> Validation e a
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first (e -> x -> e
forall a b. a -> b -> a
const e
err) (Validation x a -> Validation e a)
-> (inp -> Validation x a) -> inp -> Validation e a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. inp -> Validation x a
v

-- | A synonym for 'label' with its arguments flipped.

infix 6 <?>

(<?>) :: Validator x inp a -> e -> Validator e inp a
<?> :: Validator x inp a -> e -> Validator e inp a
(<?>) = (e -> Validator x inp a -> Validator e inp a)
-> Validator x inp a -> e -> Validator e inp a
forall a b c. (a -> b -> c) -> b -> a -> c
flip e -> Validator x inp a -> Validator e inp a
forall e x inp a. e -> Validator x inp a -> Validator e inp a
label