@@ -1,4 +1,5 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
2+
23module Data.OpenApi.Compare.Validate.Schema
34 (
45 )
@@ -10,9 +11,9 @@ import Data.Coerce
1011import Data.Foldable (for_, toList)
1112import Data.Functor
1213import Data.HList
14+import Data.List (genericIndex, genericLength, group)
1315import qualified Data.List.NonEmpty as NE
1416import qualified Data.Map as M
15−import Data.List (group, genericLength, genericIndex)
1617import Data.Maybe
1718import Data.OpenApi
1819import Data.OpenApi.Compare.Behavior
@@ -22,24 +23,24 @@ import Data.OpenApi.Compare.Subtree
2223import Data.OpenApi.Compare.Validate.Schema.DNF
2324import Data.OpenApi.Compare.Validate.Schema.Issues
2425import Data.OpenApi.Compare.Validate.Schema.JsonFormula
25−import Data.OpenApi.Compare.Validate.Schema.Traced
26−import Data.OpenApi.Compare.Validate.Schema.TypedJson
2726import Data.OpenApi.Compare.Validate.Schema.Partition
2827import Data.OpenApi.Compare.Validate.Schema.Process
28+import Data.OpenApi.Compare.Validate.Schema.Traced
29+import Data.OpenApi.Compare.Validate.Schema.TypedJson
2930import Data.Ord
3031import Data.Ratio
3132import Data.Semigroup
3233import qualified Data.Set as S
3334import Data.Text (Text)
3435
35−checkFormulas
36− :: (ReassembleHList xs (CheckEnv (Referenced Schema)))
37− => HList xs
38− -> Behavior 'SchemaLevel
39− -> ProdCons (Trace Schema)
40− -> ProdCons (Traced (Definitions Schema))
41− -> ProdCons (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
42− -> SemanticCompatFormula ()
36+checkFormulas ::
37+ (ReassembleHList xs (CheckEnv (Referenced Schema))) =>
38+ HList xs ->
39+ Behavior 'SchemaLevel ->
40+ ProdCons (Trace Schema) ->
41+ ProdCons (Traced (Definitions Schema)) ->
42+ ProdCons (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel) ->
43+ SemanticCompatFormula ()
4344checkFormulas env beh trs defs (ProdCons (fp, ep) (fc, ec)) =
4445 case P.toList ep ++ P.toList ec of
4546 issues@(_ : _) -> for_ issues $ embedFormula beh . anItem
@@ -144,21 +145,21 @@ checkFormulas env beh trs defs (ProdCons (fp, ep) (fc, ec)) =
144145 EnumDoesntSatisfy $ untypeValue e -- what does this look like when partitioned?
145146 issueFromDisjunct mPart ps = NoMatchingCondition mPart $ SomeCondition <$> S.toList ps
146147
147−checkContradiction
148− :: Behavior 'TypedSchemaLevel
149− -> Maybe Partition
150− -> S.Set (Condition t)
151− -> SemanticCompatFormula ()
148+checkContradiction ::
149+ Behavior 'TypedSchemaLevel ->
150+ Maybe Partition ->
151+ S.Set (Condition t) ->
152+ SemanticCompatFormula ()
152153checkContradiction beh mPart _ = issueAt beh $ maybe TypeBecomesEmpty PartitionBecomesEmpty mPart -- TODO #70
153154
154−checkImplication
155− :: (ReassembleHList xs (CheckEnv (Referenced Schema)))
156− => HList xs
157− -> Behavior 'TypedSchemaLevel
158− -> ProdCons (Trace Schema) -- the traces of the root schemas used in this comparison
159− -> S.Set (Condition t)
160− -> Condition t
161− -> SemanticCompatFormula ()
155+checkImplication ::
156+ (ReassembleHList xs (CheckEnv (Referenced Schema))) =>
157+ HList xs ->
158+ Behavior 'TypedSchemaLevel ->
159+ ProdCons (Trace Schema) -> -- the traces of the root schemas used in this comparison
160+ S.Set (Condition t) ->
161+ Condition t ->
162+ SemanticCompatFormula ()
162163checkImplication env beh trs prods cons = case findExactly prods of
163164 Just e
164165 | all (satisfiesTyped e) prods ->
@@ -166,124 +167,104 @@ checkImplication env beh trs prods cons = case findExactly prods of
166167 then pure ()
167168 else issueAt beh (EnumDoesntSatisfy $ untypeValue e)
168169 | otherwise -> pure () -- vacuously true
169−
170170 Nothing -> case cons of
171171 -- the above code didn't catch it, so there's no Exactly condition on the lhs
172172 Exactly e -> issueAt beh (NoMatchingEnum $ untypeValue e)
173−
174173 Maximum m -> foldCheck min m NoMatchingMaximum MatchingMaximumWeak $ \case
175174 Maximum m' -> Just m'
176175 _ -> Nothing
177−
178176 Minimum m -> foldCheck max m (NoMatchingMinimum . coerce) (MatchingMinimumWeak . coerce) $ \case
179177 Minimum m' -> Just m'
180178 _ -> Nothing
181−
182179 MultipleOf m -> foldCheck lcmScientific m NoMatchingMultipleOf MatchingMultipleOfWeak $ \case
183180 MultipleOf m' -> Just m'
184181 _ -> Nothing
185−
186182 NumberFormat f -> case flip any prods $ \case
187− NumberFormat f' -> f == f'
188− _ -> False
189− of
183+ NumberFormat f' -> f == f'
184+ _ -> False of
190185 True -> pure ()
191186 False -> issueAt beh (NoMatchingFormat f)
192−
193187 MaxLength m -> foldCheck min m NoMatchingMaxLength MatchingMaxLengthWeak $ \case
194188 MaxLength m' -> Just m'
195189 _ -> Nothing
196−
197190 MinLength m -> foldCheck max m NoMatchingMinLength MatchingMinLengthWeak $ \case
198191 MinLength m' -> Just m'
199192 _ -> Nothing
200−
201193 Pattern p -> case flip any prods $ \case
202− Pattern p' -> p == p'
203− _ -> False
204− of
194+ Pattern p' -> p == p'
195+ _ -> False of
205196 True -> pure ()
206197 False -> issueAt beh (NoMatchingPattern p) -- TODO: regex comparison #32
207−
208198 StringFormat f -> case flip any prods $ \case
209− StringFormat f' -> f == f'
210− _ -> False
211− of
199+ StringFormat f' -> f == f'
200+ _ -> False of
212201 True -> pure ()
213202 False -> issueAt beh (NoMatchingFormat f)
214−
215203 Items _ cons' -> case foldSome (<>) prods $ \case
216− Items _ rs -> Just (Just (rs NE.:| []), mempty)
217− TupleItems (map snd -> fs) -> Just (mempty, Just (fs NE.:| []))
218− _ -> Nothing
219− of
204+ Items _ rs -> Just (Just (rs NE.:| []), mempty)
205+ TupleItems (map snd -> fs) -> Just (mempty, Just (fs NE.:| []))
206+ _ -> Nothing of
220207 Just (mItems, Just pfs)
221208 | not $ allSame (length <$> pfs) -> pure () -- vacuously
222− | let plen = genericLength (NE.head pfs)
223− -> clarifyIssue (AnItem beh (anIssue TupleToArray)) $ for_ [0 .. plen - 1] $ \i -> do
224− let prod' = tracedConjunct $ case mItems of
225− Just prods' -> ((`genericIndex` i) <$> pfs) <> prods'
226− Nothing -> (`genericIndex` i) <$> pfs
227− checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prod' cons'
209+ | let plen = genericLength (NE.head pfs) ->
210+ clarifyIssue (AnItem beh (anIssue TupleToArray)) $
211+ for_ [0 .. plen - 1] $ \i -> do
212+ let prod' = tracedConjunct $ case mItems of
213+ Just prods' -> ((`genericIndex` i) <$> pfs) <> prods'
214+ Nothing -> (`genericIndex` i) <$> pfs
215+ checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prod' cons'
228216 Just (Just prods', Nothing) -> do
229217 let prod' = tracedConjunct prods'
230218 checkCompatibility (beh >>> step InItems) env $ ProdCons prod' cons'
231219 _ -> clarifyIssue (AnItem beh (anIssue NoMatchingItems)) $ do
232220 checkCompatibility (beh >>> step InItems) env $ ProdCons prodTopSchema cons'
233−
234221 TupleItems (map snd -> fs) -> case foldSome (<>) prods $ \case
235− TupleItems (map snd -> fs') -> Just (Just $ fs' NE.:| [], Just . Max $ genericLength fs', Just . Min $ genericLength fs', mempty)
236− MinItems m' -> Just (mempty, Just . Max $ m', mempty, mempty)
237− MaxItems m' -> Just (mempty, mempty, Just . Min $ m', mempty)
238− Items _ rs -> Just (mempty, mempty, mempty, Just (rs NE.:| []))
239− _ -> Nothing
240− of
241− -- if the length constraints in the producer are contradictory:
242− Just (_, Just (Max lowest), Just (Min highest), _) | lowest > highest -> pure ()
243− -- We have an explicit tuple items clause...
244− Just (Just pfs, Just (Max plen), _, _)
245− | plen /= genericLength fs -- ...of wrong length
246− -> issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
247− | otherwise
248− -> for_ [0 .. plen - 1] $ \i -> do
222+ TupleItems (map snd -> fs') -> Just (Just $ fs' NE.:| [], Just . Max $ genericLength fs', Just . Min $ genericLength fs', mempty)
223+ MinItems m' -> Just (mempty, Just . Max $ m', mempty, mempty)
224+ MaxItems m' -> Just (mempty, mempty, Just . Min $ m', mempty)
225+ Items _ rs -> Just (mempty, mempty, mempty, Just (rs NE.:| []))
226+ _ -> Nothing of
227+ -- if the length constraints in the producer are contradictory:
228+ Just (_, Just (Max lowest), Just (Min highest), _) | lowest > highest -> pure ()
229+ -- We have an explicit tuple items clause...
230+ Just (Just pfs, Just (Max plen), _, _)
231+ | plen /= genericLength fs -> -- ...of wrong length
232+ issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
233+ | otherwise ->
234+ for_ [0 .. plen - 1] $ \i -> do
249235 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct $ (`genericIndex` i) <$> pfs) (fs `genericIndex` i)
250− -- We have a fixed length array in the producer...
251− Just (Nothing, Just (Max plen), Just (Min plen'), mProd)
252− | plen == plen'
253− -> clarifyIssue (AnItem beh (anIssue ArrayToTuple)) $ case mProd of
254− _ | plen /= genericLength fs -> -- ...of wrong length
255− issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
236+ -- We have a fixed length array in the producer...
237+ Just (Nothing, Just (Max plen), Just (Min plen'), mProd)
238+ | plen == plen' ->
239+ clarifyIssue (AnItem beh (anIssue ArrayToTuple)) $ case mProd of
240+ _
241+ | plen /= genericLength fs -> -- ...of wrong length
242+ issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
256243 Just rs -> for_ [0 .. plen - 1] $ \i -> do
257244 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct rs) (fs `genericIndex` i)
258245 -- ...and no "items" schema
259246 Nothing -> clarifyIssue (AnItem beh (anIssue NoMatchingTupleItems)) $ do
260247 for_ [0 .. plen - 1] $ \i -> do
261248 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prodTopSchema (fs `genericIndex` i)
262− _ -> issueAt beh NoMatchingTupleItems
263−
249+ _ -> issueAt beh NoMatchingTupleItems
264250 MaxItems m -> foldCheck min m NoMatchingMaxItems MatchingMaxItemsWeak $ \case
265251 MaxItems m' -> Just m'
266252 TupleItems fs -> Just $ toInteger $ length fs
267253 _ -> Nothing
268−
269254 MinItems m -> foldCheck max m NoMatchingMinItems MatchingMinItemsWeak $ \case
270255 MinItems m' -> Just m'
271256 TupleItems fs -> Just $ toInteger $ length fs
272257 _ -> Nothing
273−
274258 UniqueItems -> case flip any prods $ \case
275− UniqueItems -> True
276− MaxItems 1 -> True
277− TupleItems fs | length fs == 1 -> True
278− _ -> False
279− of
259+ UniqueItems -> True
260+ MaxItems 1 -> True
261+ TupleItems fs | length fs == 1 -> True
262+ _ -> False of
280263 True -> pure ()
281264 False -> issueAt beh NoMatchingUniqueItems
282−
283265 Properties props _ madd -> case foldSome (<>) prods $ \case
284− Properties props' _ madd' -> Just $ (props', madd') NE.:| []
285− _ -> Nothing
286− of
266+ Properties props' _ madd' -> Just $ (props', madd') NE.:| []
267+ _ -> Nothing of
287268 Just pm ->
288269 anyOfAt beh NoMatchingProperties $ -- TODO: could first "concat" the lists
289270 NE.toList pm <&> \(props', madd') -> do
@@ -314,11 +295,9 @@ checkImplication env beh trs prods cons = case findExactly prods of
314295 (Just add', Just add) -> checkCompatibility (beh >>> step InAdditionalProperty) env (ProdCons add' add)
315296 pure ()
316297 Nothing -> issueAt beh NoMatchingProperties
317−
318298 MaxProperties m -> foldCheck min m NoMatchingMaxProperties MatchingMaxPropertiesWeak $ \case
319299 MaxProperties m' -> Just m'
320300 _ -> Nothing
321−
322301 MinProperties m -> foldCheck max m NoMatchingMinProperties MatchingMinPropertiesWeak $ \case
323302 MinProperties m' -> Just m'
324303 _ -> Nothing
@@ -326,14 +305,14 @@ checkImplication env beh trs prods cons = case findExactly prods of
326305 lcmScientific (toRational -> a) (toRational -> b) =
327306 fromRational $ lcm (numerator a) (numerator b) % gcd (denominator a) (denominator b)
328307
329− foldCheck
330− :: Eq a
331− => (a -> a -> a)
332− -> a
333− -> (a -> Issue 'TypedSchemaLevel)
334− -> (ProdCons a -> Issue 'TypedSchemaLevel)
335− -> (forall t. Condition t -> Maybe a)
336− -> SemanticCompatFormula ()
308+ foldCheck ::
309+ Eq a =>
310+ (a -> a -> a) ->
311+ a ->
312+ (a -> Issue 'TypedSchemaLevel) ->
313+ (ProdCons a -> Issue 'TypedSchemaLevel) ->
314+ (forall t. Condition t -> Maybe a) ->
315+ SemanticCompatFormula ()
337316 foldCheck f m missing weak extr = case foldSome f prods extr of
338317 Just m'
339318 | f m' m == m' -> pure ()