@@ -19,7 +19,6 @@ import Control.Monad.Reader
20import Data.Aeson
20import Data.Aeson
21import Data.Foldable (traverse_, for_, sequenceA_)
21import Data.Foldable (traverse_, for_, sequenceA_)
22−import Data.HashMap.Strict (HashMap)
23import qualified Data.HashMap.Strict as HashMap
22import qualified Data.HashMap.Strict as HashMap
24import qualified "unordered-containers" Data.HashSet as HashSet
23import qualified "unordered-containers" Data.HashSet as HashSet
25import Data.Scientific (isInteger)
24import Data.Scientific (isInteger)
@@ -52,8 +51,27 @@ instance Monad ValidationResult where
52 ValidationPassed x >>= f = f x
51 ValidationPassed x >>= f = f x
53 ValidationFailed msg >>= _ = ValidationFailed msg
52 ValidationFailed msg >>= _ = ValidationFailed msg
54+data ValidationConfig = ValidationConfig
55+ { validationPatternChecker :: Pattern -> Text -> Bool
56+ , validationDefinitions :: Definitions Schema
59+-- | Default @'ValidationConfig'@:
62+-- defaultValidationConfig = 'ValidationConfig'
63+-- { 'validationPatternChecker' = \_pattern _str -> True
64+-- , 'validationDefinitions' = mempty
67+defaultValidationConfig :: ValidationConfig
68+defaultValidationConfig = ValidationConfig
69+ { validationPatternChecker = \_pattern _str -> True
70+ , validationDefinitions = mempty
55−newtype Validation a = Validation { runValidation :: ReaderT (HashMap Text Schema) ValidationResult a }
73+newtype Validation a = Validation { runValidation :: ReaderT ValidationConfig ValidationResult a }
56− deriving (Functor, Applicative, Alternative, Monad, MonadReader (HashMap Text Schema))
74+ deriving (Functor, Applicative, Alternative, Monad, MonadReader ValidationConfig)
58invalid :: String -> Validation a
76invalid :: String -> Validation a
59invalid msg = Validation (ReaderT (const (ValidationFailed [msg])))
77invalid msg = Validation (ReaderT (const (ValidationFailed [msg])))
@@ -63,7 +81,7 @@ valid = pure ()
64unref :: Reference -> Validation Schema
82unref :: Reference -> Validation Schema
65unref (Reference ref) = do
83unref (Reference ref) = do
66− ms <- asks (HashMap.lookup ref)
84+ ms <- asks (HashMap.lookup ref . validationDefinitions)
67 case ms of
85 case ms of
68 Nothing -> invalid $ "unknown schema " ++ show ref
86 Nothing -> invalid $ "unknown schema " ++ show ref
69 Just s -> pure s
87 Just s -> pure s
@@ -118,12 +136,17 @@ validateWithSchema schema value
118 *> when' (not . isInteger . (n /)) multipleOf
136 *> when' (not . isInteger . (n /)) multipleOf
119 (\k -> invalid $ "expected a multiple of " ++ show k ++ " but got " ++ show n)
137 (\k -> invalid $ "expected a multiple of " ++ show k ++ " but got " ++ show n)
121− -- TODO: validate schemaPattern
122 validateString s
139 validateString s
123 = when' (fromIntegral (Text.length s) >) maxLength
140 = when' (fromIntegral (Text.length s) >) maxLength
124 (\n -> invalid $ "string exceeds maximum length (should be <=" ++ show n ++ "): " ++ show s)
141 (\n -> invalid $ "string exceeds maximum length (should be <=" ++ show n ++ "): " ++ show s)
125 *> when' (fromIntegral (Text.length s) <) minLength
142 *> when' (fromIntegral (Text.length s) <) minLength
126 (\n -> invalid $ "string is too short (length should be >=" ++ show n ++ "): " ++ show s)
143 (\n -> invalid $ "string is too short (length should be >=" ++ show n ++ "): " ++ show s)
146+ validatePattern = do
147+ check <- asks validationPatternChecker
148+ when' (`check` s) pattern
149+ (\regex -> invalid $ "string does not match pattern " ++ show regex)
128 validateArray xs
151 validateArray xs
129 = when' (fromIntegral (Vector.length xs) >) maxItems
152 = when' (fromIntegral (Vector.length xs) >) maxItems