@@ -1,19 +1,24 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
1module Data.Schematic.Validation where
3module Data.Schematic.Validation where
5+import Control.Applicative
3import Control.Monad
6import Control.Monad
4import Control.Monad.Validation
7import Control.Monad.Validation
5import Data.Aeson
8import Data.Aeson
6import Data.Aeson.Types
9import Data.Aeson.Types
7import Data.Foldable
11import Data.Foldable
8import Data.Functor.Identity
12import Data.Functor.Identity
9import Data.Monoid
13import Data.Monoid
10import Data.Schematic.Path
14import Data.Schematic.Path
11import Data.Schematic.Schema
15import Data.Schematic.Schema
12import Data.Scientific
16import Data.Scientific
13−import Data.Singletons.Prelude
17+import Data.Singletons.Prelude hiding (Const)
14import Data.Singletons.TypeLits
18import Data.Singletons.TypeLits
15import Data.Text as T
19import Data.Text as T
16import Data.Traversable
20import Data.Traversable
21+import Data.Type.Equality
17import Data.Union
22import Data.Union
18import Data.Vector as V
23import Data.Vector as V
19import Data.Vinyl
24import Data.Vinyl
@@ -161,13 +166,16 @@ validateArrayConstraint (JSONPath path) v = \case
161 warn = vWarning $ mmSingleton path (pure errMsg)
166 warn = vWarning $ mmSingleton path (pure errMsg)
162 unless predicate warn
167 unless predicate warn
169+class Validatable schema where
170+ validateJsonRepr :: [DemotedPathSegment] -> JsonRepr schema -> Validation ()
164validateJsonRepr
172validateJsonRepr
165− :: Sing schema
173+ :: forall schema. SingI schema
166− -> [DemotedPathSegment]
174+ => [DemotedPathSegment]
167 -> JsonRepr schema
175 -> JsonRepr schema
168 -> Validation ()
176 -> Validation ()
169−validateJsonRepr sschema dpath jr = case jr of
177+validateJsonRepr dpath jr = withSingI (sing :: Sing schema) $ case jr of
170− ReprText t -> case sschema of
178+ ReprText t -> case sing :: Sing schema of
171 SSchemaText scs -> do
179 SSchemaText scs -> do
173 process :: Sing (cs :: [TextConstraint]) -> Validation ()
181 process :: Sing (cs :: [TextConstraint]) -> Validation ()
@@ -176,7 +184,7 @@ validateJsonRepr sschema dpath jr = case jr of
176 validateTextConstraint (demotedPathToText dpath) t c
184 validateTextConstraint (demotedPathToText dpath) t c
177 process cs
185 process cs
178 process scs
186 process scs
179− ReprNumber n -> case sschema of
187+ ReprNumber n -> case sing :: Sing schema of
180 SSchemaNumber scs -> do
188 SSchemaNumber scs -> do
182 process :: Sing (cs :: [NumberConstraint]) -> Validation ()
190 process :: Sing (cs :: [NumberConstraint]) -> Validation ()
@@ -187,7 +195,7 @@ validateJsonRepr sschema dpath jr = case jr of
187 process scs
195 process scs
188 ReprNull -> pure ()
196 ReprNull -> pure ()
189 ReprBoolean _ -> pure ()
197 ReprBoolean _ -> pure ()
190− ReprArray v -> case sschema of
198+ ReprArray v -> case sing :: Sing schema of
191 SSchemaArray acs s -> do
199 SSchemaArray acs s -> do
193 process :: Sing (cs :: [ArrayConstraint]) -> Validation ()
201 process :: Sing (cs :: [ArrayConstraint]) -> Validation ()
@@ -198,60 +206,52 @@ validateJsonRepr sschema dpath jr = case jr of
198 process acs
206 process acs
199 for_ (V.indexed v) $ \(ix, jr') -> do
207 for_ (V.indexed v) $ \(ix, jr') -> do
200 let newPath = dpath <> pure (DIx $ fromIntegral ix)
208 let newPath = dpath <> pure (DIx $ fromIntegral ix)
201− validateJsonRepr s newPath jr'
209+ withSingI s $ validateJsonRepr newPath jr'
202− ReprOptional d -> case sschema of
210+ ReprOptional d -> case sing :: Sing schema of
203 SSchemaOptional ss -> case d of
211 SSchemaOptional ss -> case d of
204− Just x -> validateJsonRepr ss dpath x
212+ Just x -> withSingI ss $ validateJsonRepr dpath x
205 Nothing -> pure ()
213 Nothing -> pure ()
206− ReprObject fs -> case sschema of
214+ ReprObject fs -> case sing :: Sing schema of
207 SSchemaObject _ -> go fs
215 SSchemaObject _ -> go fs
209 go :: Rec FieldRepr (ts :: [(Symbol, Schema)] ) -> Validation ()
217 go :: Rec FieldRepr (ts :: [(Symbol, Schema)] ) -> Validation ()
210 go RNil = pure ()
218 go RNil = pure ()
211 go (f@(FieldRepr d) :& ftl) = do
219 go (f@(FieldRepr d) :& ftl) = do
212 let newPath = dpath <> [DKey (knownFieldName f)]
220 let newPath = dpath <> [DKey (knownFieldName f)]
213− validateJsonRepr (knownFieldSchema f) newPath d
221+ withSingI (knownFieldSchema f)
222+ $ validateJsonRepr newPath d
215− ReprUnion _ -> pure () -- FIXME
224+ ReprUnion u -> pure () -- upeel u (sing :: Sing schema)
225+ -- ReprUnion u -> do
227+ -- v :: forall s. JsonRepr s -> Const (Validation ()) s
228+ -- v j = Const $ validateJsonRepr (singByProxy j) dpath j
229+ -- peel :: Union (Const (Validation ())) ts -> Validation ()
230+ -- peel (That u) = peel u
231+ -- peel (This v) = getConst v
233+ -- ReprUnion u -> do
235+ -- peel :: Union JsonRepr ss -> [DemotedSchema] -> Validation ()
236+ -- peel (That u) ds = peel u ds
237+ -- peel (This j) ds = case ds of
238+ -- [] -> error "peeling error: report this as a schematic bug"
239+ -- (ds' :_) -> let s' = toSing ds'
241+ -- SomeSing s -> case s `testEquality` (singByProxy j) of
242+ -- Just Refl -> validateJsonRepr s dpath j
216− -- case sschema of
243+ -- case sschema of
217− -- SSchemaUnion ss -> case ss of
244+ -- SSchemaUnion us -> peel u (fromSing us)
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
234−-- :: Sing (s ': stl)
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
240−-- withUSubset
246+class UPeel ss where
241−-- :: Sing (s ': stl)
247+ upeel :: Union JsonRepr ss -> Sing (SchemaUnion ss :: Schema) -> Validation ()
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
246−toUnion
249+instance (UPeel stl) => UPeel (s ': stl) where
247− :: USubset s' (s ': ss) (RImage s' (s ': ss))
250+ upeel (That u) (SSchemaUnion (SCons _ stl)) = upeel @stl u (SSchemaUnion stl)
248− => Sing (s ': ss)
251+ upeel (This j) (SSchemaUnion (SCons s _)) = withSingI s $ validateJsonRepr mempty j
249− -> Union JsonRepr s'
250− -> JsonRepr ('SchemaUnion (s ': ss))
251−toUnion _ = ReprUnion . urelax
253−umatch' :: UElem a as i => Sing a -> Union f as -> Maybe (f a)
253+umatch' :: UElem a as i => Sing a -> Union JsonRepr as -> Maybe (JsonRepr a)
254−umatch' _ u = umatch u
254+umatch' _ = umatch
256parseAndValidateJson
256parseAndValidateJson
257 :: forall schema
257 :: forall schema
@@ -263,7 +263,7 @@ parseAndValidateJson v =
263 Left s -> DecodingError $ T.pack s
263 Left s -> DecodingError $ T.pack s
264 Right jsonRepr ->
264 Right jsonRepr ->
266− validate = validateJsonRepr (sing :: Sing schema) [] jsonRepr
266+ validate = validateJsonRepr [] jsonRepr
267 res = runIdentity . runValidationTEither $ validate
267 res = runIdentity . runValidationTEither $ validate
268 in case res of
268 in case res of
269 Left em -> ValidationError em
269 Left em -> ValidationError em