1module Data.Schematic.Validation where
4import Control.Monad.Validation
8import Data.Functor.Identity
10import Data.Schematic.Path
11import Data.Schematic.Schema
13import Data.Singletons.Prelude
14import Data.Singletons.TypeLits
16import Data.Traversable
18import Data.Vector as V
20import Data.Vinyl.TypeLevel
25type Validation a = ValidationT ErrorMap Identity a
27type ErrorMap = MonoidMap Text [Text]
31 | DecodingError Text -- static
32 | ValidationError ErrorMap -- runtime
33 deriving (Show, Eq, Functor, Foldable, Traversable)
35instance (TopLevel a, SingI a, FromJSON (JsonRepr a))
36 => FromJSON (ParseResult (JsonRepr a)) where
37 parseJSON = pure . parseAndValidateJson @a
39isValid :: ParseResult a -> Bool
40isValid (Valid _) = True
43isDecodingError :: ParseResult a -> Bool
44isDecodingError (DecodingError _) = True
45isDecodingError _ = False
47isValidationError :: ParseResult a -> Bool
48isValidationError (ValidationError _) = True
49isValidationError _ = False
54 -> Sing (tcs :: TextConstraint)
56validateTextConstraint (JSONPath path) t = \case
59 nlen = withKnownNat n $ natVal n
60 predicate = nlen == (fromIntegral $ T.length t)
61 errMsg = "length of " <> path <> " should be == " <> T.pack (show nlen)
62 warn = vWarning $ mmSingleton path (pure errMsg)
66 nlen = withKnownNat n $ natVal n
67 predicate = nlen < (fromIntegral $ T.length t)
68 errMsg = "length of " <> path <> " should be < " <> T.pack (show nlen)
69 warn = vWarning $ mmSingleton path (pure errMsg)
73 nlen = withKnownNat n $ natVal n
74 predicate = nlen <= (fromIntegral $ T.length t)
75 errMsg = "length of " <> path <> " should be <= " <> T.pack (show nlen)
76 warn = vWarning $ mmSingleton path (pure errMsg)
80 nlen = withKnownNat n $ natVal n
81 predicate = nlen > (fromIntegral $ T.length t)
82 errMsg = "length of " <> path <> " should be > " <> T.pack (show nlen)
83 warn = vWarning $ mmSingleton path (pure errMsg)
87 nlen = withKnownNat n $ natVal n
88 predicate = nlen >= (fromIntegral $ T.length t)
89 errMsg = "length of " <> path <> " should be >= " <> T.pack (show nlen)
90 warn = vWarning $ mmSingleton path (pure errMsg)
94 regex = withKnownSymbol r $ fromSing r
95 predicate = matchTest (makeRegex (T.unpack regex) :: Regex) (T.unpack t)
96 errMsg = path <> " must match " <> regex
97 warn = vWarning $ mmSingleton path (pure errMsg)
101 matching :: Sing (s :: [Symbol]) -> Bool
102 matching SNil = False
103 matching (SCons s@SSym fs) = T.pack (symbolVal s) == t || matching fs
104 errMsg = path <> " must be one of " <> T.pack (show (fromSing ss))
105 warn = vWarning $ mmSingleton path (pure errMsg)
106 unless (matching ss) warn
108validateNumberConstraint
111 -> Sing (tcs :: NumberConstraint)
113validateNumberConstraint (JSONPath path) num = \case
116 nlen = withKnownNat n $ natVal n
117 predicate = fromIntegral nlen == num
118 errMsg = path <> " should be == " <> T.pack (show nlen)
119 warn = vWarning $ mmSingleton path (pure errMsg)
120 unless predicate warn
123 nlen = withKnownNat n $ natVal n
124 predicate = num > fromIntegral nlen
125 errMsg = path <> " should be > " <> T.pack (show nlen)
126 warn = vWarning $ mmSingleton path (pure errMsg)
127 unless predicate warn
130 nlen = withKnownNat n $ natVal n
131 predicate = num >= fromIntegral nlen
132 errMsg = path <> " should be >= " <> T.pack (show nlen)
133 warn = vWarning $ mmSingleton path (pure errMsg)
134 unless predicate warn
137 nlen = withKnownNat n $ natVal n
138 predicate = num < fromIntegral nlen
139 errMsg = path <> " should be < " <> T.pack (show nlen)
140 warn = vWarning $ mmSingleton path (pure errMsg)
141 unless predicate warn
144 nlen = withKnownNat n $ natVal n
145 predicate = num <= fromIntegral nlen
146 errMsg = path <> " should be <= " <> T.pack (show nlen)
147 warn = vWarning $ mmSingleton path (pure errMsg)
148 unless predicate warn
150validateArrayConstraint
153 -> Sing (tcs :: ArrayConstraint)
155validateArrayConstraint (JSONPath path) v = \case
158 nlen = withKnownNat n $ natVal n
159 predicate = nlen == fromIntegral (V.length v)
160 errMsg = "length of " <> path <> " should be == " <> T.pack (show nlen)
161 warn = vWarning $ mmSingleton path (pure errMsg)
162 unless predicate warn
166 -> [DemotedPathSegment]
169validateJsonRepr sschema dpath jr = case jr of
170 ReprText t -> case sschema of
171 SSchemaText scs -> do
173 process :: Sing (cs :: [TextConstraint]) -> Validation ()
174 process SNil = pure ()
175 process (SCons c cs) = do
176 validateTextConstraint (demotedPathToText dpath) t c
179 ReprNumber n -> case sschema of
180 SSchemaNumber scs -> do
182 process :: Sing (cs :: [NumberConstraint]) -> Validation ()
183 process SNil = pure ()
184 process (SCons c cs) = do
185 validateNumberConstraint (demotedPathToText dpath) n c
189 ReprBoolean _ -> pure ()
190 ReprArray v -> case sschema of
191 SSchemaArray acs s -> do
193 process :: Sing (cs :: [ArrayConstraint]) -> Validation ()
194 process SNil = pure ()
195 process (SCons c cs) = do
196 validateArrayConstraint (demotedPathToText dpath) v c
199 for_ (V.indexed v) $ \(ix, jr') -> do
200 let newPath = dpath <> pure (DIx $ fromIntegral ix)
201 validateJsonRepr s newPath jr'
202 ReprOptional d -> case sschema of
203 SSchemaOptional ss -> case d of
204 Just x -> validateJsonRepr ss dpath x
206 ReprObject fs -> case sschema of
207 SSchemaObject _ -> go fs
209 go :: Rec FieldRepr (ts :: [(Symbol, Schema)] ) -> Validation ()
211 go (f@(FieldRepr d) :& ftl) = do
212 let newPath = dpath <> [DKey (knownFieldName f)]
213 validateJsonRepr (knownFieldSchema f) newPath d
215 ReprUnion _ -> pure () -- FIXME
217 -- SSchemaUnion ss -> case ss of
218 -- SCons s stl -> case umatch' s u of
219 -- Nothing -> case urestrict u of
221 -- fail "impossible to produce subUnion, please report this as a bug"
224 -- JSONPath path = demotedPathToText dpath
226 -- SNil -> void $ vWarning $ mmSingleton path
227 -- $ pure "union handling error, please report this as bug"
229 -- validateJsonRepr (SSchemaUnion (SCons s' stl')) dpath
230 -- $ toUnion (SCons s' stl') x
231 -- Just x -> validateJsonRepr s dpath x
235-- -> ( USubset stl (s ': stl) (RImage stl (s ': stl))
236-- => Union f (s ': stl)
237-- -> Maybe (Union f stl) )
238-- subUnion (SCons s stl) = urestrict
242-- -> (USubset stl (s ': stl) (RImage stl (s ': stl)) => Maybe (Union f stl))
243-- -> Maybe (Union f stl)
244-- withUSubset (SCons s stl) r = r
247 :: USubset s' (s ': ss) (RImage s' (s ': ss))
250 -> JsonRepr ('SchemaUnion (s ': ss))
251toUnion _ = ReprUnion . urelax
253umatch' :: UElem a as i => Sing a -> Union f as -> Maybe (f a)
254umatch' _ u = umatch u
258 . (FromJSON (JsonRepr schema), TopLevel schema, SingI schema)
260 -> ParseResult (JsonRepr schema)
261parseAndValidateJson v =
262 case parseEither parseJSON v of
263 Left s -> DecodingError $ T.pack s
266 validate = validateJsonRepr (sing :: Sing schema) [] jsonRepr
267 res = runIdentity . runValidationTEither $ validate
269 Left em -> ValidationError em
270 Right () -> Valid jsonRepr
272parseAndValidateJsonBy
273 :: (FromJSON (JsonRepr schema), TopLevel schema, SingI schema)
276 -> ParseResult (JsonRepr schema)
277parseAndValidateJsonBy _ = parseAndValidateJson
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list