valida-1.0.0: Simple applicative validation for product types, batteries included!
Copyright(c) TotallyNotChase 2021
LicenseMIT
Maintainertotallynotchase42@gmail.com
StabilityStable
PortabilityPortable
Safe HaskellSafe
LanguageHaskell2010

Valida.Combinators

Description

This module is re-exported by Valida. You probably don't need to import this.

This module exports the primitive, as well as utility, Validator combinators. As well as the orElse, andAlso, satisfyAny, and satisfyAll functions, and some more utilities.

Note: All the primitive combinators and derivative combinators use the same type for inp and a. In those cases - upon successful validation, the input itself, wrapped in Success, is returned.

Synopsis

Primitive NonEmpty combinators

failureIf :: (a -> Bool) -> e -> Validator (NonEmpty e) a a Source #

Build a rule that fails with given error if the given predicate succeeds.

failureIf predc = failureUnless (not . predc)

Examples

Expand
>>> runValidator (failureIf (>0) "Positive") 5
Failure ("Positive" :| [])
>>> runValidator (failureIf (>0) "Positive") 0
Success 0
>>> runValidator (failureIf (>0) "Positive") (-1)
Success (-1)

failureUnless :: (a -> Bool) -> e -> Validator (NonEmpty e) a a Source #

Build a rule that fails with given error unless the given predicate succeeds.

failureUnless predc = failureIf (not . predc)

Examples

Expand
>>> runValidator (failureUnless (>0) "NonPositive") 5
Success 5
>>> runValidator (failureUnless (>0) "NonPositive") 0
Failure ("NonPositive" :| [])
>>> runValidator (failureUnless (>0) "NonPositive") (-1)
Failure ("NonPositive" :| [])

Primitive Unit combinators

failureIf' :: (a -> Bool) -> Validator () a a Source #

Like failureIf but uses Unit as the Validator error type.

failureIf' predc = failureUnless' (not . predc)
label (const (err :| [])) (failureIf' predc) = failureIf predc err

Examples

Expand
>>> runValidator (failureIf' (>0)) 5
Failure ()
>>> runValidator (failureIf' (>0)) 0
Success 0
>>> runValidator (failureIf' (>0)) (-1)
Success (-1)

failureUnless' :: (a -> Bool) -> Validator () a a Source #

Like failureUnless but uses Unit as the Validator error type.

failureUnless' predc = failureIf' (not . predc)
label (const (err :| [])) (failureUnless' predc) = failureUnless predc err

Examples

Expand
>>> runValidator (failureUnless' (>0)) 5
Success 5
>>> runValidator (failureUnless' (>0)) 0
Failure ()
>>> runValidator (failureUnless' (>0)) (-1)
Failure ()

Negating Validator

negateV :: e -> Validator e1 a x -> Validator e a a Source #

Build a validator that succeeds if given validator fails and vice versa.

Note: This will set the output of the Validator to be the same as its input, thereby ignoring the original output.

Examples

Expand
>>> let vald = negateV "NonPositive" (failureIf (>0) "Positive")
>>> runValidator vald 5
Success 5
>>> runValidator vald 0
Failure "NonPositive"
>>> runValidator vald (-1)
Failure "NonPositive"

negateV' :: Validator e a x -> Validator () a a Source #

Like negateV but uses Unit as the Validator error type.

Combining Validators

andAlso :: Validator e inp a -> Validator e inp a -> Validator e inp a Source #

Build a validator that only succeeds if both of the given validators succeed. The first (left-most) failure is yielded. If both succeed, the right-most Success result is returned. Other validator is not used if first one fails.

This is the same as the semigroup operation (i.e (<>)) on Validator.

vald1 `andAlso` (vald2 `andAlso` vald3) = (vald1 `andAlso` vald2) `andAlso` vald3
mempty `andAlso` vald = vald
vald `andAlso` mempty = vald

Examples

Expand
>>> let vald = failureIf (>0) "Positive" `andAlso` failureIf even "Even"
>>> runValidator vald 5
Failure ("Positive" :| [])
>>> runValidator vald (-2)
Failure ("Even" :| [])
>>> runValidator vald (-1)
Success (-1)

orElse :: Semigroup e => Validator e inp a -> Validator e inp a -> Validator e inp a Source #

Build a validator that succeeds if either of the given validators succeed. The first (left-most) Success is returned. If both fail, the errors are combined. Other validator is not used if first one succeeds.

vald1 `orElse` (vald2 `orElse` vald3) = (vald1 `orElse` vald2) `orElse` vald3
failV e `orElse` vald = vald
vald `orElse` failV e = vald

Examples

Expand
>>> let vald = failureIf (>0) "Positive" `orElse` failureIf even "Even"
>>> runValidator vald 5
Success 5
>>> runValidator vald 4
Failure ("Positive" :| ["Even"])
>>> runValidator vald 0
Success 0
>>> runValidator vald (-1)
Success (-1)

failV :: Monoid e => Validator e inp a Source #

A Validator that always fails with supplied error. This is the identity of orElse (i.e (</>)).

failV `orElse` vald = vald
vald `orElse` failV = vald

Examples

Expand
>>> runValidator (failV :: Validator String Int Int) 42
Failure ""

satisfyAll :: Foldable t => t (Validator e inp a) -> Validator e inp a Source #

Build a validator that only succeeds if all of the given validators succeed. The first (left-most) failure is yielded. If all succeed, the right-most Success result is returned. Remaining validators are not used once one fails.

satisfyAll = fold
satisfyAll = foldl1 andAlso
satisfyAll = foldr1 andAlso
satisfyAll = foldl andAlso mempty
satisfyAll = foldr andAlso mempty

satisfyAny :: (Foldable t, Semigroup e) => t (Validator e inp a) -> Validator e inp a Source #

Build a validator that succeeds if any of the given validators succeed. If all fail, the errors are combined. The first (left-most) Success is returned. If all fail, the errors are combined. Remaining validators are not used once one succeeds.

satisfyAny = foldl1 orElse
satisfyAny = foldr1 orElse
satisfyAny = foldl orElse failV
satisfyAny = foldr orElse failV

(</>) :: Semigroup e => Validator e inp a -> Validator e inp a -> Validator e inp a infixr 5 Source #

A synonym for orElse. Satisfies associativity law and hence forms a semigroup.

Common derivates of primitive NonEmpty combinators

atleastContains :: Foldable t => (a -> Bool) -> e -> Validator (NonEmpty e) (t a) (t a) Source #

Build an any rule.

atleastContains x = failureUnless (any x)

lengthAbove :: Foldable t => Int -> e -> Validator (NonEmpty e) (t a) (t a) Source #

Build a minimum length (inclusive) rule.

lengthAbove x = minLengthOf (x + 1)
lengthAbove x = failureUnless ((>n) . length)

lengthBelow :: Foldable t => Int -> e -> Validator (NonEmpty e) (t a) (t a) Source #

Build a maximum length (inclusive) rule.

lengthBelow x = maxLengthOf (x - 1)
lengthBelow x = failureUnless ((<n) . length)

lengthWithin :: Foldable t => (Int, Int) -> e -> Validator (NonEmpty e) (t a) (t a) Source #

Build an inRange rule for length.

lengthWithin (min, max) = minLengthOf min `andAlso` maxLengthOf max
lengthWithin r = failureUnless (inRange r . length)

maxLengthOf :: Foldable t => Int -> e -> Validator (NonEmpty e) (t a) (t a) Source #

Build a maximum length (inclusive) rule.

maxLengthOf n = failureUnless ((<=n) . length)

maxValueOf :: Ord a => a -> e -> Validator (NonEmpty e) a a Source #

Build a maximum value (inclusive) rule.

maxValueOf x = failureUnless (<=x)

minLengthOf :: Foldable t => Int -> e -> Validator (NonEmpty e) (t a) (t a) Source #

Build a minimum length (inclusive) rule.

minLengthOf x = failureUnless ((>=n) . length)

minValueOf :: Ord a => a -> e -> Validator (NonEmpty e) a a Source #

Build a minimum value (inclusive) rule.

minValueOf x = failureUnless (>=x)

mustBe :: Eq a => a -> e -> Validator (NonEmpty e) a a Source #

Build an equality rule for value.

mustBe x = failureUnless (==x)

mustContain :: (Foldable t, Eq a) => a -> e -> Validator (NonEmpty e) (t a) (t a) Source #

Build an elem rule.

mustContain x = atleastContains (==x)
mustContain x = failureUnless (elem x)

notEmpty :: Foldable t => e -> Validator (NonEmpty e) (t a) (t a) Source #

Build a maximum length rule.

notEmpty = minLengthOf 1
notEmpty = failureIf null

ofLength :: Foldable t => Int -> e -> Validator (NonEmpty e) (t a) (t a) Source #

Build an equality rule for length.

ofLength x = failureUnless ((==x) . length)

onlyContains :: Foldable t => (a -> Bool) -> e -> Validator (NonEmpty e) (t a) (t a) Source #

Build an all rule.

onlyContains x = failureUnless (all x)

valueAbove :: Ord a => a -> e -> Validator (NonEmpty e) a a Source #

Build a minimum value (exclusive) rule.

valueAbove x = minValueOf (x + 1)
valueAbove x = failureUnless (>x)

valueBelow :: Ord a => a -> e -> Validator (NonEmpty e) a a Source #

Build a maximum value (exclusive) rule.

valueBelow x = minValueOf (x - 1)
valueBelow x = failureUnless (<x)

valueWithin :: Ord a => (a, a) -> e -> Validator (NonEmpty e) a a Source #

Build an inRange rule for value.

valueWithin (m, n) = minValueOf m `andAlso` maxValueOf n
valueWithin (m, n) = failureUnless (x -> m <= x && x <= n)

Common derivates of primitive Unit combinators

atleastContains' :: Foldable t => (a -> Bool) -> Validator () (t a) (t a) Source #

Like atleastContains but uses Unit as the Validator error type.

lengthAbove' :: Foldable t => Int -> Validator () (t a) (t a) Source #

Like lengthAbove but uses Unit as the Validator error type.

lengthBelow' :: Foldable t => Int -> Validator () (t a) (t a) Source #

Like lengthBelow but uses Unit as the Validator error type.

lengthWithin' :: Foldable t => (Int, Int) -> Validator () (t a) (t a) Source #

Like lengthWithin but uses Unit as the Validator error type.

maxLengthOf' :: Foldable t => Int -> Validator () (t a) (t a) Source #

Like maxLengthOf but uses Unit as the Validator error type.

maxValueOf' :: Ord a => a -> Validator () a a Source #

Like maxValueOf but uses Unit as the Validator error type.

minLengthOf' :: Foldable t => Int -> Validator () (t a) (t a) Source #

Like minLengthOf but uses Unit as the Validator error type.

minValueOf' :: Ord a => a -> Validator () a a Source #

Like minValueOf but uses Unit as the Validator error type.

mustBe' :: Eq a => a -> Validator () a a Source #

Like mustBe but uses Unit as the Validator error type.

mustContain' :: (Foldable t, Eq a) => a -> Validator () (t a) (t a) Source #

Like mustContain but uses Unit as the Validator error type.

notEmpty' :: Foldable t => Validator () (t a) (t a) Source #

Like notEmpty but uses Unit as the Validator error type.

ofLength' :: Foldable t => Int -> Validator () (t a) (t a) Source #

Like ofLength but uses Unit as the Validator error type.

onlyContains' :: Foldable t => (a -> Bool) -> Validator () (t a) (t a) Source #

Like onlyContains but uses Unit as the Validator error type.

valueAbove' :: Ord a => a -> Validator () a a Source #

Like valueAbove but uses Unit as the Validator error type.

valueBelow' :: Ord a => a -> Validator () a a Source #

Like valueBelow but uses Unit as the Validator error type.

valueWithin' :: Ord a => (a, a) -> Validator () a a Source #

Like valueWithin but uses Unit as the Validator error type.

Type specific Validators

optionally :: Validator e a x -> Validator e (Maybe a) (Maybe a) Source #

Build a validator that runs given validator only if input is Just.

Yields Success when input is Nothing.

Examples

Expand
>>> runValidator (optionally (failureIf even "Even")) (Just 5)
Success (Just 5)
>>> runValidator (optionally (failureIf even "Even"))) (Just 6)
Failure ("Even" :| [])
>>> runValidator (optionally (failureIf even "Even")) Nothing
Success Nothing