@@ -18,10 +18,14 @@ module OpenAPI.Checker.Subtree
1818 , CompatFormula'
1919 , SemanticCompatFormula
2020 , ProdCons (..)
21− , HasUnsupportedFeature (..)
2221 , swapProdCons
2322 , runCompatFormula
2423 , issueAt
24+ , anItem
25+ , anIssue
26+ , invertIssueOrientation
27+ , invertIssueOrientationP
28+ , embedFormula
2529 , anyOfAt
2630 , structuralIssue
2731
@@ -53,7 +57,6 @@ import Data.HList
5357import qualified Data.HashMap.Strict.InsOrd as IOHM
5458import Data.Hashable
5559import Data.Kind
56−import Data.Monoid
5760import Data.OpenApi
5861import qualified Data.Set as S
5962import Data.Typeable
@@ -106,8 +109,38 @@ data ProdCons a = ProdCons
106109 }
107110 deriving stock (Eq, Ord, Show, Functor, Foldable, Traversable)
108111
109−swapProdCons :: ProdCons a -> ProdCons a
110−swapProdCons (ProdCons a b) = ProdCons b a
112+swapProdCons
113+ :: SwapEnvRoles xs
114+ => (HList xs -> ProdCons x -> CompatFormula' q AnIssue r a)
115+ -> (HList xs -> ProdCons x -> CompatFormula' q AnIssue r a)
116+swapProdCons f e (ProdCons p c) =
117+ invertIssueOrientation $
118+ f (swapEnvRoles e) (ProdCons c p)
119+{-# INLINE swapProdCons #-}
120+
121+type family IsProdCons (x :: Type) :: Bool where
122+ IsProdCons (ProdCons _) = 'True
123+ IsProdCons _ = 'False
124+
125+type SwapEnvElementRoles x = SwapEnvElementRoles' x (IsProdCons x)
126+
127+class IsProdCons x ~ f => SwapEnvElementRoles' (x :: Type) f where
128+ swapEnvElementRoles :: x -> x
129+
130+instance SwapEnvElementRoles' (ProdCons x) 'True where
131+ swapEnvElementRoles (ProdCons p c) = ProdCons c p
132+
133+instance IsProdCons x ~ 'False => SwapEnvElementRoles' x 'False where
134+ swapEnvElementRoles = id
135+
136+class SwapEnvRoles xs where
137+ swapEnvRoles :: HList xs -> HList xs
138+
139+instance SwapEnvRoles '[] where
140+ swapEnvRoles = id
141+
142+instance (SwapEnvElementRoles x, SwapEnvRoles xs) => SwapEnvRoles (x ': xs) where
143+ swapEnvRoles (HCons x xs) = HCons (swapEnvElementRoles x) (swapEnvRoles xs)
111144
112145instance Applicative ProdCons where
113146 pure x = ProdCons x x
@@ -145,14 +178,12 @@ class (Typeable t, Issuable (SubtreeLevel t)) => Subtree (t :: Type) where
145178 type SubtreeLevel t :: BehaviorLevel
146179
147180 checkStructuralCompatibility
148− :: (HasAll (CheckEnv t) xs)
149− => HList xs
181+ :: HList (CheckEnv t)
150182 -> ProdCons (Traced t)
151183 -> StructuralCompatFormula ()
152184
153185 checkSemanticCompatibility
154− :: (HasAll (CheckEnv t) xs)
155− => HList xs
186+ :: HList (CheckEnv t)
156187 -> Behavior (SubtreeLevel t)
157188 -> ProdCons (Traced t)
158189 -> SemanticCompatFormula ()
@@ -162,29 +193,33 @@ class (Typeable t, Issuable (SubtreeLevel t)) => Subtree (t :: Type) where
162193{-# WARNING checkSemanticCompatibility "You should not be calling this directly. Use 'checkCompatibility'" #-}
163194
164195checkCompatibility
165− :: (HasAll (CheckEnv t) xs, Subtree t)
166− => HList xs
167− -> Behavior (SubtreeLevel t)
196+ :: forall t xs.
197+ (ReassembleHList xs (CheckEnv t), Subtree t)
198+ => Behavior (SubtreeLevel t)
199+ -> HList xs
168200 -> ProdCons (Traced t)
169201 -> SemanticCompatFormula ()
170−checkCompatibility e bhv = memo bhv SemanticMemoKey $ \pc ->
202+checkCompatibility bhv e = memo bhv SemanticMemoKey $ \pc ->
171203 case runCompatFormula $ checkSubstructure e pc of
172− Left _ -> checkSemanticCompatibility e bhv pc
204+ Left _ -> checkSemanticCompatibility (reassemble e) bhv pc
173205 Right () -> pure ()
206+{-# INLINE checkCompatibility #-}
174207
175208checkSubstructure
176− :: (HasAll (CheckEnv t) xs, Subtree t)
209+ :: (ReassembleHList xs (CheckEnv t), Subtree t)
177210 => HList xs
178211 -> ProdCons (Traced t)
179212 -> StructuralCompatFormula ()
180−checkSubstructure e = memo Root StructuralMemoKey $ checkStructuralCompatibility e
213+checkSubstructure e = memo Root StructuralMemoKey $ checkStructuralCompatibility (reassemble e)
214+{-# INLINE checkSubstructure #-}
181215
182216structuralMaybe
183− :: (Subtree a, HasAll (CheckEnv a) xs)
217+ :: (Subtree a, ReassembleHList xs (CheckEnv a))
184218 => HList xs
185219 -> ProdCons (Maybe (Traced a))
186220 -> StructuralCompatFormula ()
187221structuralMaybe e = structuralMaybeWith (checkSubstructure e)
222+{-# INLINE structuralMaybe #-}
188223
189224structuralMaybeWith
190225 :: (ProdCons a -> StructuralCompatFormula ())
@@ -193,9 +228,10 @@ structuralMaybeWith
193228structuralMaybeWith f (ProdCons (Just a) (Just b)) = f $ ProdCons a b
194229structuralMaybeWith _ (ProdCons Nothing Nothing) = pure ()
195230structuralMaybeWith _ _ = structuralIssue
231+{-# INLINE structuralMaybeWith #-}
196232
197233structuralList
198− :: (Subtree a, HasAll (CheckEnv a) xs)
234+ :: (Subtree a, ReassembleHList xs (CheckEnv a))
199235 => HList xs
200236 -> ProdCons [Traced a]
201237 -> StructuralCompatFormula ()
@@ -205,17 +241,20 @@ structuralList e (ProdCons (a : aa) (b : bb)) = do
205241 structuralList e $ ProdCons aa bb
206242 pure ()
207243structuralList _ _ = structuralIssue
244+{-# INLINE structuralList #-}
208245
209246structuralEq :: (Eq a, Comonad w) => ProdCons (w a) -> StructuralCompatFormula ()
210247structuralEq (ProdCons a b) = if extract a == extract b then pure () else structuralIssue
248+{-# INLINE structuralEq #-}
211249
212250iohmStructural
213− :: (HasAll (CheckEnv v) (k ': xs), Ord k, Subtree v, Hashable k, Typeable k, Show k)
251+ :: (ReassembleHList (k ': xs) (CheckEnv v), Ord k, Subtree v, Hashable k, Typeable k, Show k)
214252 => HList xs
215253 -> ProdCons (Traced (IOHM.InsOrdHashMap k v))
216254 -> StructuralCompatFormula ()
217255iohmStructural e =
218256 iohmStructuralWith (\k -> checkSubstructure (k `HCons` e))
257+{-# INLINE iohmStructural #-}
219258
220259instance (Typeable k, Typeable v, Ord k, Show k) => Steppable (IOHM.InsOrdHashMap k v) v where
221260 data Step (IOHM.InsOrdHashMap k v) v = InsOrdHashMapKeyStep k
@@ -235,41 +274,36 @@ iohmStructuralWith f pc = do
235274 (\eKey ->
236275 f eKey $ stepTraced (InsOrdHashMapKeyStep eKey) . fmap (IOHM.lookupDefault (error "impossible") eKey) <$> pc)
237276 else structuralIssue
238−
239−class HasUnsupportedFeature x where
240− hasUnsupportedFeature :: x -> Bool
241−
242−instance HasUnsupportedFeature () where
243− hasUnsupportedFeature () = False
244−
245−instance
246− (HasUnsupportedFeature a, HasUnsupportedFeature b)
247− => HasUnsupportedFeature (Either a b)
248− where
249− hasUnsupportedFeature (Left x) = hasUnsupportedFeature x
250− hasUnsupportedFeature (Right x) = hasUnsupportedFeature x
251−
252−instance Issuable l => HasUnsupportedFeature (Issue l) where
253− hasUnsupportedFeature = issueIsUnsupported
254−
255−instance HasUnsupportedFeature (AnIssue l) where
256− hasUnsupportedFeature (AnIssue issue) = hasUnsupportedFeature issue
257−
258−instance
259− (forall x. HasUnsupportedFeature (f x))
260− => HasUnsupportedFeature (P.PathsPrefixTree q f r)
261− where
262− hasUnsupportedFeature =
263− getAny . P.foldWith (\_ fa -> Any $ hasUnsupportedFeature fa)
277+{-# INLINE iohmStructuralWith #-}
264278
265279runCompatFormula
266280 :: CompatFormula' q f r a
267281 -> Either (P.PathsPrefixTree q f r) a
268282runCompatFormula (Compose f) =
269283 calculate . runIdentity . runMemo 0 . unCompatM $ f
284+{-# INLINE runCompatFormula #-}
285+
286+embedFormula :: Paths q r l -> CompatFormula' q f l a -> CompatFormula' q f r a
287+embedFormula bhv (Compose x) = Compose $ mapErrors (P.embed bhv) <$> x
270288
271289issueAt :: Issuable l => Paths q r l -> Issue l -> CompatFormula' q AnIssue r a
272−issueAt xs issue = Compose $ pure $ anError $ AnItem xs $ AnIssue issue
290+issueAt xs issue = Compose $ pure $ anError $ AnItem xs $ anIssue issue
291+{-# INLINE issueAt #-}
292+
293+anIssue :: Issuable l => Issue l -> AnIssue l
294+anIssue = AnIssue Forward
295+{-# INLINE anIssue #-}
296+
297+anItem :: AnItem q AnIssue r -> CompatFormula' q AnIssue r a
298+anItem = Compose . pure . anError
299+{-# INLINE anItem #-}
300+
301+invertIssueOrientation :: CompatFormula' q AnIssue r a -> CompatFormula' q AnIssue r a
302+invertIssueOrientation (Compose x) =
303+ Compose $ mapErrors invertIssueOrientationP <$> x
304+
305+invertIssueOrientationP :: P.PathsPrefixTree q AnIssue r -> P.PathsPrefixTree q AnIssue r
306+invertIssueOrientationP = P.map (\(AnIssue ori i) -> AnIssue (toggleOrientation ori) i)
273307
274308structuralIssue :: StructuralCompatFormula a
275309structuralIssue = Compose $ pure $ anError $ AnItem Root Proxy
@@ -282,7 +316,7 @@ anyOfAt
282316 -> CompatFormula' q AnIssue r a
283317anyOfAt _ _ [x] = x
284318anyOfAt xs issue fs =
285− Compose $ (`eitherOf` AnItem xs (AnIssue issue)) <$> sequenceA (getCompose <$> fs)
319+ Compose $ (`eitherOf` AnItem xs (anIssue issue)) <$> sequenceA (getCompose <$> fs)
286320
287321fixpointKnot
288322 :: MonadState (MemoState VarRef) m