validate-input 0.1.0.0 → 0.2.0.0
raw patch · 3 files changed
+108/−47 lines, 3 filesdep +QuickCheckdep +hspecdep +validate-inputPVP ok
version bump matches the API change (PVP)
Dependencies added: QuickCheck, hspec, validate-input
API changes (from Hackage documentation)
- Data.Validator: (+>>) :: Monad m => (a -> ValidationT e m a) -> (a -> ValidationT e m a) -> a -> ValidationT e m a
- Data.Validator: ValidationT :: EitherT e m a -> ValidationT e m a
- Data.Validator: newtype ValidationT e m a
- Data.Validator: unValidationT :: ValidationT e m a -> EitherT e m a
+ Data.Validator: (<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c
+ Data.Validator: (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
+ Data.Validator: data ValidationT e m a
+ Data.Validator: type ValidationRule e a = ValidationRuleT e Identity a
+ Data.Validator: type ValidationRuleT e m a = a -> ValidationT e m a
- Data.Validator: conformsPred :: Monad m => (a -> Bool) -> e -> a -> ValidationT e m a
+ Data.Validator: conformsPred :: Monad m => (a -> Bool) -> e -> ValidationRuleT e m a
- Data.Validator: conformsPredM :: Monad m => (a -> m Bool) -> e -> a -> ValidationT e m a
+ Data.Validator: conformsPredM :: Monad m => (a -> m Bool) -> e -> ValidationRuleT e m a
- Data.Validator: largerThan :: (Monad m, Ord a) => a -> e -> a -> ValidationT e m a
+ Data.Validator: largerThan :: (Monad m, Ord a) => a -> e -> ValidationRuleT e m a
- Data.Validator: lengthBetween :: (Monad m, HasLength a) => Int64 -> Int64 -> e -> a -> ValidationT e m a
+ Data.Validator: lengthBetween :: (Monad m, HasLength a) => Int64 -> Int64 -> e -> ValidationRuleT e m a
- Data.Validator: matchesRegex :: (Stringable a, Monad m) => Regex -> e -> a -> ValidationT e m a
+ Data.Validator: matchesRegex :: (Stringable a, Monad m) => Regex -> e -> ValidationRuleT e m a
- Data.Validator: maxLength :: (Monad m, HasLength a) => Int64 -> e -> a -> ValidationT e m a
+ Data.Validator: maxLength :: (Monad m, HasLength a) => Int64 -> e -> ValidationRuleT e m a
- Data.Validator: minLength :: (Monad m, HasLength a) => Int64 -> e -> a -> ValidationT e m a
+ Data.Validator: minLength :: (Monad m, HasLength a) => Int64 -> e -> ValidationRuleT e m a
- Data.Validator: notEmpty :: (Monad m, HasLength a) => e -> a -> ValidationT e m a
+ Data.Validator: notEmpty :: (Monad m, HasLength a) => e -> ValidationRuleT e m a
- Data.Validator: runValidator :: (a -> ValidationM e a) -> a -> Either e a
+ Data.Validator: runValidator :: ValidationRule e a -> a -> Either e a
- Data.Validator: runValidatorT :: Monad m => (a -> ValidationT e m a) -> a -> m (Either e a)
+ Data.Validator: runValidatorT :: Monad m => ValidationRuleT e m a -> a -> m (Either e a)
- Data.Validator: smallerThan :: (Monad m, Ord a) => a -> e -> a -> ValidationT e m a
+ Data.Validator: smallerThan :: (Monad m, Ord a) => a -> e -> ValidationRuleT e m a
- Data.Validator: valueBetween :: (Monad m, Ord a) => a -> a -> e -> a -> ValidationT e m a
+ Data.Validator: valueBetween :: (Monad m, Ord a) => a -> a -> e -> ValidationRuleT e m a
Files
- src/Data/Validator.hs +47/−38
- test/Spec.hs +39/−0
- validate-input.cabal +22/−9
src/Data/Validator.hs view
@@ -1,22 +1,21 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-} module Data.Validator- ( -- * core monad and runners- ValidationM, ValidationT(..)+ ( -- * Core monad and runners+ ValidationM, ValidationT, ValidationRule, ValidationRuleT , runValidator, runValidatorT- -- * combinators- , (+>>)- -- * checks+ -- * Combinators+ , (>=>), (<=<)+ -- * Checks , minLength, maxLength, lengthBetween, notEmpty , largerThan, smallerThan, valueBetween , matchesRegex , conformsPred, conformsPredM- -- * helper classes and types+ -- * Helper classes and types , HasLength(..), Stringable(..) , Int64- -- * reexports+ -- * Regular expression helpers , re, mkRegexQQ, Regex ) where@@ -43,90 +42,100 @@ deriving (Monad, Functor, Applicative, Alternative, MonadPlus, MonadTrans) -- | Run a validation on a type 'a'-runValidator :: (a -> ValidationM e a) -> a -> Either e a+runValidator :: ValidationRule e a -> a -> Either e a runValidator a b = runIdentity $ runValidatorT a b+{-# INLINE runValidator #-} -- | Run a validation on a type 'a'-runValidatorT :: Monad m => (a -> ValidationT e m a) -> a -> m (Either e a)+runValidatorT :: Monad m => ValidationRuleT e m a -> a -> m (Either e a) runValidatorT validationSteps input = runEitherT $ unValidationT (validationSteps input)+{-# INLINE runValidatorT #-} +-- | A validation rule. Combine using @('>=>')@ or @('<=<')@+type ValidationRule e a = ValidationRuleT e Identity a++-- | A validation rule. Combine using @('>=>')@ or @('<=<')@+type ValidationRuleT e m a = a -> ValidationT e m a+ -- | All types that have a length, eg. 'String', '[a]', 'Vector a', etc. class HasLength a where getLength :: a -> Int64 instance HasLength [a] where getLength = fromIntegral . length+ {-# INLINE getLength #-} instance HasLength T.Text where getLength = fromIntegral . T.length+ {-# INLINE getLength #-} instance HasLength TL.Text where getLength = TL.length+ {-# INLINE getLength #-} instance HasLength BS.ByteString where getLength = fromIntegral . BS.length+ {-# INLINE getLength #-} instance HasLength BSL.ByteString where getLength = BSL.length+ {-# INLINE getLength #-} -- | Mark a custom check as failed checkFailed :: Monad m => e -> ValidationT e m a checkFailed = ValidationT . left {-# INLINE checkFailed #-} --- | Combine two checks-(+>>) :: Monad m => (a -> ValidationT e m a) -> (a -> ValidationT e m a) -> a -> ValidationT e m a-(+>>) m1 m2 a =- m1 a >>= m2-{-# INLINE (+>>) #-}- -- | Check that the value is at least N elements long-minLength :: (Monad m, HasLength a) => Int64 -> e -> a -> ValidationT e m a-minLength lowerBound e obj =- largerThan lowerBound e (getLength obj) >> return obj+minLength :: (Monad m, HasLength a) => Int64 -> e -> ValidationRuleT e m a+minLength lowerBound e obj = largerThan lowerBound e (getLength obj) >> return obj+{-# INLINE minLength #-} -- | Check that the value is at maxium N elements long-maxLength :: (Monad m, HasLength a) => Int64 -> e -> a -> ValidationT e m a+maxLength :: (Monad m, HasLength a) => Int64 -> e -> ValidationRuleT e m a maxLength upperBound e obj = smallerThan upperBound e (getLength obj) >> return obj+{-# INLINE maxLength #-} -- | Check that the value's length is between N and M-lengthBetween :: (Monad m, HasLength a) => Int64 -> Int64 -> e -> a -> ValidationT e m a-lengthBetween lowerBound upperBound e obj =- valueBetween lowerBound upperBound e (getLength obj) >> return obj+lengthBetween :: (Monad m, HasLength a) => Int64 -> Int64 -> e -> ValidationRuleT e m a+lengthBetween lowerBound upperBound e obj = valueBetween lowerBound upperBound e (getLength obj) >> return obj+{-# INLINE lengthBetween #-} -- | Specialized minLength with N = 1-notEmpty :: (Monad m, HasLength a) => e -> a -> ValidationT e m a+notEmpty :: (Monad m, HasLength a) => e -> ValidationRuleT e m a notEmpty = minLength 1 {-# INLINE notEmpty #-} -- | Check that a value is larger than N-largerThan :: (Monad m, Ord a) => a -> e -> a -> ValidationT e m a+largerThan :: (Monad m, Ord a) => a -> e -> ValidationRuleT e m a largerThan lowerBound = conformsPred (>= lowerBound)+{-# INLINE largerThan #-} -- | Check that a value is smaller than N-smallerThan :: (Monad m, Ord a) => a -> e -> a -> ValidationT e m a+smallerThan :: (Monad m, Ord a) => a -> e -> ValidationRuleT e m a smallerThan upperBound = conformsPred (<= upperBound)+{-# INLINE smallerThan #-} -- | Check that a value is between M and N-valueBetween :: (Monad m, Ord a) => a -> a -> e -> a -> ValidationT e m a-valueBetween lowerBound upperBound e obj =- (largerThan lowerBound e +>> smallerThan upperBound e) obj+valueBetween :: (Monad m, Ord a) => a -> a -> e -> ValidationRuleT e m a+valueBetween lowerBound upperBound e = largerThan lowerBound e >=> smallerThan upperBound e+{-# INLINE valueBetween #-} -- | Check that a value conforms a predicate-conformsPred :: Monad m => (a -> Bool) -> e -> a -> ValidationT e m a-conformsPred predicate e obj =- do unless (predicate obj) $ checkFailed e- return obj+conformsPred :: Monad m => (a -> Bool) -> e -> ValidationRuleT e m a+conformsPred predicate e obj = unless (predicate obj) (checkFailed e) >> return obj+{-# INLINE conformsPred #-} -- | Check that a value conforms a predicate-conformsPredM :: Monad m => (a -> m Bool) -> e -> a -> ValidationT e m a+conformsPredM :: Monad m => (a -> m Bool) -> e -> ValidationRuleT e m a conformsPredM predicate e obj = do res <- lift $ predicate obj- unless res $ checkFailed e- return obj+ unless res (checkFailed e) >> return obj+{-# INLINE conformsPredM #-} -- | Checks that a value matches a regular expression-matchesRegex :: (Stringable a, Monad m) => Regex -> e -> a -> ValidationT e m a-matchesRegex r = conformsPred (\obj -> obj =~ r)+matchesRegex :: (Stringable a, Monad m) => Regex -> e -> ValidationRuleT e m a+matchesRegex r = conformsPred (=~ r)+{-# INLINE matchesRegex #-}
+ test/Spec.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE ScopedTypeVariables #-}+module Main where++import Data.Validator++import Test.Hspec+import Test.QuickCheck++errMsg :: String+errMsg = "oops"++main :: IO ()+main =+ hspec $+ do describe "conformsPred" $+ do it "should result in error when value does not conform pred" $+ property $ \(i :: Int) ->+ i <= 5 ==> runValidator (conformsPred (>5) errMsg) i == Left errMsg+ it "should result in error when value does conform pred" $+ property $ \(i :: Int) ->+ i > 5 ==> runValidator (conformsPred (>5) errMsg) i == Right i+ describe "minLength" $+ do it "should result in error when string is to small" $+ property $ \(str :: String) (i :: Int64) ->+ fromIntegral (length str) < i ==>+ runValidator (minLength i errMsg) str == Left errMsg+ it "should allow strings with correct size to pass" $+ property $ \(str :: String) (i :: Int64) ->+ fromIntegral (length str) >= i ==>+ runValidator (minLength i errMsg) str == Right str+ describe "maxLength" $+ do it "should result in error when string is to large" $+ property $ \(str :: String) (i :: Int64) ->+ fromIntegral (length str) > i ==>+ runValidator (maxLength i errMsg) str == Left errMsg+ it "should allow strings with correct size to pass" $+ property $ \(str :: String) (i :: Int64) ->+ fromIntegral (length str) <= i ==>+ runValidator (maxLength i errMsg) str == Right str
validate-input.cabal view
@@ -1,7 +1,8 @@ name: validate-input-version: 0.1.0.0+version: 0.2.0.0 synopsis: Input validation combinator library-description: A small Haskell combinator library that provides a simple way of validating user provided data structures.+description: A small Haskell combinator library that provides a simple way of+ validating user provided data structures. homepage: https://github.com/agrafix/validate-input bug-reports: https://github.com/agrafix/validate-input/issues license: MIT@@ -22,13 +23,25 @@ library exposed-modules: Data.Validator build-depends:- base >=4.6 && <5,- bytestring >=0.10,- either >=4.3,- mtl >=2.1,- pcre-heavy >=0.2,- stringable >=0.1,- text >=1.2+ base >=4.6 && <5,+ bytestring >=0.10,+ either >=4.3,+ mtl >=2.1,+ pcre-heavy >=0.2,+ stringable >=0.1,+ text >=1.2 hs-source-dirs: src default-language: Haskell2010 ghc-options: -auto-all -Wall -fno-warn-orphans++test-suite validate-input-tests+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends:+ base >=4.6 && <5,+ hspec >=2.1,+ QuickCheck >=2.7,+ validate-input+ ghc-options: -auto-all -Wall -fno-warn-orphans+ default-language: Haskell2010