1{-# OPTIONS_GHC -Wno-orphans #-}
3module OpenAPI.Checker.Subtree
19 , SemanticCompatFormula
26 , invertIssueOrientation
27 , invertIssueOrientationP
33 -- * Structural helpers
52import Control.Comonad.Env
53import Control.Monad.Identity
54import Control.Monad.State
56import Data.Functor.Compose
58import qualified Data.HashMap.Strict.InsOrd as IOHM
62import qualified Data.Set as S
64import OpenAPI.Checker.Behavior
65import OpenAPI.Checker.Formula
66import OpenAPI.Checker.Memo
67import OpenAPI.Checker.Paths
68import qualified OpenAPI.Checker.PathsPrefixTree as P
71 (Typeable Step, Typeable a, Typeable b, Ord (Step a b), Show (Step a b)) =>
72 Steppable (a :: Type) (b :: Type)
74 -- | How to get from an @a@ node to a @b@ node
79instance Steppable TraceRoot OpenApi where
80 data Step TraceRoot OpenApi
83 deriving stock (Eq, Ord, Show)
85type Trace = Paths Step TraceRoot
87type instance AdditionalQuiverConstraints Step _ _ = ()
89type Traced' a b = Env (Trace a) b
91type Traced a = Traced' a a
93pattern Traced :: Trace a -> b -> Traced' a b
94pattern Traced t x = EnvT t (Identity x)
96{-# COMPLETE Traced #-}
98traced :: Trace a -> a -> Traced a
101retraced :: (Trace a -> Trace a') -> Traced' a b -> Traced' a' b
102retraced f (Traced a b) = Traced (f a) b
104stepTraced :: Steppable a a' => Step a a' -> Traced' a b -> Traced' a' b
105stepTraced s = retraced (>>> step s)
107data ProdCons a = ProdCons
111 deriving stock (Eq, Ord, Show, Functor, Foldable, Traversable)
115 => (HList xs -> ProdCons x -> CompatFormula' q AnIssue r a)
116 -> (HList xs -> ProdCons x -> CompatFormula' q AnIssue r a)
117swapProdCons f e (ProdCons p c) =
118 invertIssueOrientation $
119 f (swapEnvRoles e) (ProdCons c p)
120{-# INLINE swapProdCons #-}
122type family IsProdCons (x :: Type) :: Bool where
123 IsProdCons (ProdCons _) = 'True
124 IsProdCons _ = 'False
126type SwapEnvElementRoles x = SwapEnvElementRoles' x (IsProdCons x)
128class IsProdCons x ~ f => SwapEnvElementRoles' (x :: Type) f where
129 swapEnvElementRoles :: x -> x
131instance SwapEnvElementRoles' (ProdCons x) 'True where
132 swapEnvElementRoles (ProdCons p c) = ProdCons c p
134instance IsProdCons x ~ 'False => SwapEnvElementRoles' x 'False where
135 swapEnvElementRoles = id
137class SwapEnvRoles xs where
138 swapEnvRoles :: HList xs -> HList xs
140instance SwapEnvRoles '[] where
143instance (SwapEnvElementRoles x, SwapEnvRoles xs) => SwapEnvRoles (x ': xs) where
144 swapEnvRoles (HCons x xs) = HCons (swapEnvElementRoles x) (swapEnvRoles xs)
146instance Applicative ProdCons where
147 pure x = ProdCons x x
148 ProdCons fp fc <*> ProdCons xp xc = ProdCons (fp xp) (fc xc)
150newtype CompatM a = CompatM
152 :: StateT (MemoState VarRef) Identity a
158 , MonadState (MemoState VarRef)
161type CompatFormula' q f r = Compose CompatM (FormulaF q f r)
163type SemanticCompatFormula = CompatFormula' Behave AnIssue 'APILevel
165type StructuralCompatFormula = CompatFormula' VoidQuiver Proxy ()
169deriving stock instance Eq (VoidQuiver a b)
171type instance AdditionalQuiverConstraints VoidQuiver _ _ = ()
173deriving stock instance Ord (VoidQuiver a b)
175deriving stock instance Show (VoidQuiver a b)
177class (Typeable t, Issuable (SubtreeLevel t)) => Subtree (t :: Type) where
178 type CheckEnv t :: [Type]
179 type SubtreeLevel t :: BehaviorLevel
181 checkStructuralCompatibility
182 :: HList (CheckEnv t)
183 -> ProdCons (Traced t)
184 -> StructuralCompatFormula ()
186 checkSemanticCompatibility
187 :: HList (CheckEnv t)
188 -> Behavior (SubtreeLevel t)
189 -> ProdCons (Traced t)
190 -> SemanticCompatFormula ()
192{-# WARNING checkStructuralCompatibility "You should not be calling this directly. Use 'checkSubstructure'" #-}
194{-# WARNING checkSemanticCompatibility "You should not be calling this directly. Use 'checkCompatibility'" #-}
198 (ReassembleHList xs (CheckEnv t), Subtree t)
199 => Behavior (SubtreeLevel t)
201 -> ProdCons (Traced t)
202 -> SemanticCompatFormula ()
203checkCompatibility bhv e = memo bhv SemanticMemoKey $ \pc ->
204 case runCompatFormula $ checkSubstructure e pc of
205 Left _ -> checkSemanticCompatibility (reassemble e) bhv pc
207{-# INLINE checkCompatibility #-}
210 :: (ReassembleHList xs (CheckEnv t), Subtree t)
212 -> ProdCons (Traced t)
213 -> StructuralCompatFormula ()
214checkSubstructure e = memo Root StructuralMemoKey $ checkStructuralCompatibility (reassemble e)
215{-# INLINE checkSubstructure #-}
218 :: (Subtree a, ReassembleHList xs (CheckEnv a))
220 -> ProdCons (Maybe (Traced a))
221 -> StructuralCompatFormula ()
222structuralMaybe e = structuralMaybeWith (checkSubstructure e)
223{-# INLINE structuralMaybe #-}
226 :: (ProdCons a -> StructuralCompatFormula ())
227 -> ProdCons (Maybe a)
228 -> StructuralCompatFormula ()
229structuralMaybeWith f (ProdCons (Just a) (Just b)) = f $ ProdCons a b
230structuralMaybeWith _ (ProdCons Nothing Nothing) = pure ()
231structuralMaybeWith _ _ = structuralIssue
232{-# INLINE structuralMaybeWith #-}
235 :: (Subtree a, ReassembleHList xs (CheckEnv a))
237 -> ProdCons [Traced a]
238 -> StructuralCompatFormula ()
239structuralList _ (ProdCons [] []) = pure ()
240structuralList e (ProdCons (a : aa) (b : bb)) = do
241 checkSubstructure e $ ProdCons a b
242 structuralList e $ ProdCons aa bb
244structuralList _ _ = structuralIssue
245{-# INLINE structuralList #-}
247structuralEq :: (Eq a, Comonad w) => ProdCons (w a) -> StructuralCompatFormula ()
248structuralEq (ProdCons a b) = if extract a == extract b then pure () else structuralIssue
249{-# INLINE structuralEq #-}
252 :: (ReassembleHList (k ': xs) (CheckEnv v), Ord k, Subtree v, Hashable k, Typeable k, Show k)
254 -> ProdCons (Traced (IOHM.InsOrdHashMap k v))
255 -> StructuralCompatFormula ()
257 iohmStructuralWith (\k -> checkSubstructure (k `HCons` e))
258{-# INLINE iohmStructural #-}
260instance (Typeable k, Typeable v, Ord k, Show k) => Steppable (IOHM.InsOrdHashMap k v) v where
261 data Step (IOHM.InsOrdHashMap k v) v = InsOrdHashMapKeyStep k
262 deriving stock (Eq, Ord, Show)
265 :: (Ord k, Hashable k, Typeable k, Typeable v, Show k)
266 => (k -> ProdCons (Traced v) -> StructuralCompatFormula ())
267 -> ProdCons (Traced (IOHM.InsOrdHashMap k v))
268 -> StructuralCompatFormula ()
269iohmStructuralWith f pc = do
270 let ProdCons pEKeys cEKeys = S.fromList . IOHM.keys . extract <$> pc
276 f eKey $ stepTraced (InsOrdHashMapKeyStep eKey) . fmap (IOHM.lookupDefault (error "impossible") eKey) <$> pc)
278{-# INLINE iohmStructuralWith #-}
281 :: CompatFormula' q f r a
282 -> Either (P.PathsPrefixTree q f r) a
283runCompatFormula (Compose f) =
284 calculate . runIdentity . runMemo 0 . unCompatM $ f
285{-# INLINE runCompatFormula #-}
287embedFormula :: Paths q r l -> CompatFormula' q f l a -> CompatFormula' q f r a
288embedFormula bhv (Compose x) = Compose $ mapErrors (P.embed bhv) <$> x
290issueAt :: Issuable l => Paths q r l -> Issue l -> CompatFormula' q AnIssue r a
291issueAt xs issue = Compose $ pure $ anError $ AnItem xs $ anIssue issue
292{-# INLINE issueAt #-}
294anIssue :: Issuable l => Issue l -> AnIssue l
295anIssue = AnIssue Forward
296{-# INLINE anIssue #-}
298anItem :: AnItem q AnIssue r -> CompatFormula' q AnIssue r a
299anItem = Compose . pure . anError
302invertIssueOrientation :: CompatFormula' q AnIssue r a -> CompatFormula' q AnIssue r a
303invertIssueOrientation (Compose x) =
304 Compose $ mapErrors invertIssueOrientationP <$> x
306invertIssueOrientationP :: P.PathsPrefixTree q AnIssue r -> P.PathsPrefixTree q AnIssue r
307invertIssueOrientationP = P.map (\(AnIssue ori i) -> AnIssue (toggleOrientation ori) i)
309structuralIssue :: StructuralCompatFormula a
310structuralIssue = Compose $ pure $ anError $ AnItem Root Proxy
316 -> [CompatFormula' q AnIssue r a]
317 -> CompatFormula' q AnIssue r a
320 Compose $ (`eitherOf` AnItem xs (anIssue issue)) <$> traverse getCompose fs
322-- | If the given formula contains any issues, add another issue on top. Otherwise succeed.
324 :: AnItem q AnIssue r
325 -> CompatFormula' q AnIssue r a
326 -> CompatFormula' q AnIssue r a
328 Compose ((`eitherOf` item) <$> pure <$> getCompose f) *> f
331 :: MonadState (MemoState VarRef) m
332 => KnotTier (FormulaF q f r ()) VarRef m
335 { onKnotFound = modifyMemoNonce succ
336 , onKnotUsed = \i -> pure $ variable i
337 , tieKnot = \i x -> pure $ maxFixpoint i x
341 :: (Typeable (l :: k), Typeable q, Typeable f, Typeable k, Typeable a)
344 -> (ProdCons (Traced a) -> CompatFormula' q f r ())
345 -> (ProdCons (Traced a) -> CompatFormula' q f r ())
346memo bhv k f pc = Compose $ do
351 formula <- getCompose $ f pc
352 pure $ mapErrors (P.takeSubtree bhv) formula)
354 pure $ mapErrors (P.embed bhv) formula'
356data MemoKey = SemanticMemoKey | StructuralMemoKey
357 deriving stock (Eq, Ord)
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