-- | Checks product-like entities. The key is some identificator for the product
--element. Each element may be required or optional.
--
--One example of product is request parameters. There are optional and required
--parameters. The client and server have possibly different set of
--parameters. What we must check is if server requires some request parameter,
--then this parameter must be presented by client and their schemas must match.
--
--So when we checking products we are checking from the server's (consumer)
--perspective, ensuring that all parameters are provided by the client (producer)
--and their schemas match.
--
--This module abstracts this logic for arbitrary elements
module Data.OpenApi.Compare.Validate.Products
  ( checkProducts,
    ProductLike (..),
  )
where

import Data.Foldable
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.OpenApi.Compare.Behavior
import Data.OpenApi.Compare.Paths
import Data.OpenApi.Compare.Subtree

-- | Some entity which is product-like
data ProductLike a = ProductLike
  { ProductLike a -> a
productValue :: a
  , ProductLike a -> Bool
required :: Bool
  }

checkProducts ::
  (Ord k, Issuable l) =>
  Paths q r l ->
  -- | No required element found
  (k -> Issue l) ->
  (k -> ProdCons t -> CompatFormula' q AnIssue r ()) ->
  ProdCons (Map k (ProductLike t)) ->
  CompatFormula' q AnIssue r ()
checkProducts :: Paths q r l
-> (k -> Issue l)
-> (k -> ProdCons t -> CompatFormula' q AnIssue r ())
-> ProdCons (Map k (ProductLike t))
-> CompatFormula' q AnIssue r ()
checkProducts Paths q r l
xs k -> Issue l
noElt k -> ProdCons t -> CompatFormula' q AnIssue r ()
check (ProdCons Map k (ProductLike t)
p Map k (ProductLike t)
c) = [(k, ProductLike t)]
-> ((k, ProductLike t) -> CompatFormula' q AnIssue r ())
-> CompatFormula' q AnIssue r ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ (Map k (ProductLike t) -> [(k, ProductLike t)]
forall k a. Map k a -> [(k, a)]
M.toList Map k (ProductLike t)
c) (((k, ProductLike t) -> CompatFormula' q AnIssue r ())
 -> CompatFormula' q AnIssue r ())
-> ((k, ProductLike t) -> CompatFormula' q AnIssue r ())
-> CompatFormula' q AnIssue r ()
forall a b. (a -> b) -> a -> b
$ \(k
key, ProductLike t
consElt) ->
  case k -> Map k (ProductLike t) -> Maybe (ProductLike t)
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup k
key Map k (ProductLike t)
p of
    Maybe (ProductLike t)
Nothing -> case ProductLike t -> Bool
forall a. ProductLike a -> Bool
required ProductLike t
consElt of
      Bool
True -> Paths q r l -> Issue l -> CompatFormula' q AnIssue r ()
forall (l :: BehaviorLevel)
       (q :: BehaviorLevel -> BehaviorLevel -> *) (r :: BehaviorLevel) a.
Issuable l =>
Paths q r l -> Issue l -> CompatFormula' q AnIssue r a
issueAt Paths q r l
xs (Issue l -> CompatFormula' q AnIssue r ())
-> Issue l -> CompatFormula' q AnIssue r ()
forall a b. (a -> b) -> a -> b
$ k -> Issue l
noElt k
key
      Bool
False -> () -> CompatFormula' q AnIssue r ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
    Just ProductLike t
prodElt -> do
      let elts :: ProdCons (ProductLike t)
elts = ProductLike t -> ProductLike t -> ProdCons (ProductLike t)
forall a. a -> a -> ProdCons a
ProdCons ProductLike t
prodElt ProductLike t
consElt
      k -> ProdCons t -> CompatFormula' q AnIssue r ()
check k
key (ProductLike t -> t
forall a. ProductLike a -> a
productValue (ProductLike t -> t) -> ProdCons (ProductLike t) -> ProdCons t
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ProdCons (ProductLike t)
elts)