packages feed

json-spec-openapi 0.3.1.2 → 1.0.0.0

raw patch · 4 files changed

+1116/−180 lines, 4 filesdep ~json-specPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: json-spec

API changes (from Hackage documentation)

- Data.JsonSpec.OpenApi: instance (Data.JsonSpec.OpenApi.Defs defs, Data.JsonSpec.OpenApi.Schemaable spec) => Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonLet defs spec)
- Data.JsonSpec.OpenApi: instance (Data.JsonSpec.OpenApi.Defs defs, GHC.Internal.TypeLits.KnownSymbol name) => Data.JsonSpec.OpenApi.Refable ('Data.JsonSpec.Spec.JsonLet defs ('Data.JsonSpec.Spec.JsonRef name))
- Data.JsonSpec.OpenApi: instance (Data.JsonSpec.OpenApi.Defs more, Data.JsonSpec.OpenApi.Schemaable spec, GHC.Internal.TypeLits.KnownSymbol name) => Data.JsonSpec.OpenApi.Defs ('(name, spec) : more)
- Data.JsonSpec.OpenApi: instance (Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonObject more), Data.JsonSpec.OpenApi.Refable spec, GHC.Internal.TypeLits.KnownSymbol key) => Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonObject ('Data.JsonSpec.Spec.Optional key spec : more))
- Data.JsonSpec.OpenApi: instance (Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonObject more), Data.JsonSpec.OpenApi.Refable spec, GHC.Internal.TypeLits.KnownSymbol key) => Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonObject ('Data.JsonSpec.Spec.Required key spec : more))
- Data.JsonSpec.OpenApi: instance (Data.JsonSpec.OpenApi.Schemaable left, Data.JsonSpec.OpenApi.Schemaable right) => Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonEither left right)
- Data.JsonSpec.OpenApi: instance (GHC.Internal.TypeLits.KnownSymbol name, Data.JsonSpec.OpenApi.Schemaable def, Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonLet more ('Data.JsonSpec.Spec.JsonRef target))) => Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonLet ('(name, def) : more) ('Data.JsonSpec.Spec.JsonRef target))
- Data.JsonSpec.OpenApi: instance (GHC.Internal.TypeLits.KnownSymbol target, Data.JsonSpec.OpenApi.Schemaable def, Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonLet more def)) => Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonLet ('(target, def) : more) ('Data.JsonSpec.Spec.JsonRef target))
- Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Defs '[]
- Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Schemaable 'Data.JsonSpec.Spec.JsonBool
- Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Schemaable 'Data.JsonSpec.Spec.JsonDateTime
- Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Schemaable 'Data.JsonSpec.Spec.JsonInt
- Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Schemaable 'Data.JsonSpec.Spec.JsonNum
- Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Schemaable 'Data.JsonSpec.Spec.JsonRaw
- Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Schemaable 'Data.JsonSpec.Spec.JsonString
- Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonObject '[])
- Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Schemaable a => Data.JsonSpec.OpenApi.Refable a
- Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Schemaable spec => Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonArray spec)
- Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Schemaable spec => Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonNullable spec)
- Data.JsonSpec.OpenApi: instance GHC.Internal.TypeError.Unsatisfiable (((((((Data.JsonSpec.OpenApi.T "`JsonRef \"" 'GHC.Internal.TypeError.:<>: Data.JsonSpec.OpenApi.T target) 'GHC.Internal.TypeError.:<>: Data.JsonSpec.OpenApi.T "\"` is not defined.\n") 'GHC.Internal.TypeError.:$$: Data.JsonSpec.OpenApi.T "You are trying to use a JsonRef as the \"top level\" ") 'GHC.Internal.TypeError.:$$: Data.JsonSpec.OpenApi.T "schema. We try to satisfy this request by looking up ") 'GHC.Internal.TypeError.:$$: Data.JsonSpec.OpenApi.T "the reference and inlining it. However in this case you ") 'GHC.Internal.TypeError.:$$: Data.JsonSpec.OpenApi.T "are trying to reference a schema which is not defined, ") 'GHC.Internal.TypeError.:$$: Data.JsonSpec.OpenApi.T "so this won't work.\n") => Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonLet '[] ('Data.JsonSpec.Spec.JsonRef target))
- Data.JsonSpec.OpenApi: instance GHC.Internal.TypeLits.KnownSymbol name => Data.JsonSpec.OpenApi.Refable ('Data.JsonSpec.Spec.JsonRef name)
- Data.JsonSpec.OpenApi: instance GHC.Internal.TypeLits.KnownSymbol tag => Data.JsonSpec.OpenApi.Schemaable ('Data.JsonSpec.Spec.JsonTag tag)
+ Data.JsonSpec.OpenApi: instance (Data.JsonSpec.OpenApi.Defs defs more, Data.JsonSpec.OpenApi.Inlineable defs spec, GHC.Internal.TypeLits.KnownSymbol name) => Data.JsonSpec.OpenApi.Defs defs ('(name, spec) : more)
+ Data.JsonSpec.OpenApi: instance (Data.JsonSpec.OpenApi.Defs newDefs newDefs, Data.JsonSpec.OpenApi.Refable (Data.JsonSpec.OpenApi.Concat newDefs defs) spec) => Data.JsonSpec.OpenApi.Refable defs ('Data.JsonSpec.Spec.JsonLet newDefs spec)
+ Data.JsonSpec.OpenApi: instance (Data.JsonSpec.OpenApi.Inlineable (Data.JsonSpec.OpenApi.Concat newDefs defs) spec, Data.JsonSpec.OpenApi.Defs newDefs newDefs) => Data.JsonSpec.OpenApi.Inlineable defs ('Data.JsonSpec.Spec.JsonLet newDefs spec)
+ Data.JsonSpec.OpenApi: instance (Data.JsonSpec.OpenApi.Inlineable defs ('Data.JsonSpec.Spec.JsonObject ('Data.JsonSpec.Spec.Optional key spec : more)), GHC.Internal.TypeLits.KnownSymbol key) => Data.JsonSpec.OpenApi.Inlineable defs ('Data.JsonSpec.Spec.JsonObject ('Data.JsonSpec.Spec.Required key spec : more))
+ Data.JsonSpec.OpenApi: instance (Data.JsonSpec.OpenApi.Inlineable defs ('Data.JsonSpec.Spec.JsonObject more), Data.JsonSpec.OpenApi.Refable defs spec, GHC.Internal.TypeLits.KnownSymbol key) => Data.JsonSpec.OpenApi.Inlineable defs ('Data.JsonSpec.Spec.JsonObject ('Data.JsonSpec.Spec.Optional key spec : more))
+ Data.JsonSpec.OpenApi: instance (Data.JsonSpec.OpenApi.Refable defs left, Data.JsonSpec.OpenApi.Refable defs right) => Data.JsonSpec.OpenApi.Inlineable defs ('Data.JsonSpec.Spec.JsonEither left right)
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Defs defs '[]
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Deref defs defs target => Data.JsonSpec.OpenApi.Inlineable defs ('Data.JsonSpec.Spec.JsonRef target)
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Deref defs more target => Data.JsonSpec.OpenApi.Deref defs ('(miss, spec) : more) target
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Inlineable '[] spec => Data.JsonSpec.OpenApi.Schemaable spec
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Inlineable defs 'Data.JsonSpec.Spec.JsonBool
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Inlineable defs 'Data.JsonSpec.Spec.JsonDateTime
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Inlineable defs 'Data.JsonSpec.Spec.JsonInt
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Inlineable defs 'Data.JsonSpec.Spec.JsonNum
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Inlineable defs 'Data.JsonSpec.Spec.JsonRaw
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Inlineable defs 'Data.JsonSpec.Spec.JsonString
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Inlineable defs ('Data.JsonSpec.Spec.JsonObject '[])
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Inlineable defs a => Data.JsonSpec.OpenApi.Refable defs a
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Inlineable defs spec => Data.JsonSpec.OpenApi.Deref defs ('(target, spec) : more) target
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.NotDereferenceable defs target => Data.JsonSpec.OpenApi.Deref defs '[] target
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Refable defs spec => Data.JsonSpec.OpenApi.Inlineable defs ('Data.JsonSpec.Spec.JsonArray spec)
+ Data.JsonSpec.OpenApi: instance Data.JsonSpec.OpenApi.Refable defs spec => Data.JsonSpec.OpenApi.Inlineable defs ('Data.JsonSpec.Spec.JsonNullable spec)
+ Data.JsonSpec.OpenApi: instance GHC.Internal.TypeLits.KnownSymbol name => Data.JsonSpec.OpenApi.Refable defs ('Data.JsonSpec.Spec.JsonRef name)
+ Data.JsonSpec.OpenApi: instance GHC.Internal.TypeLits.KnownSymbol tag => Data.JsonSpec.OpenApi.Inlineable defs ('Data.JsonSpec.Spec.JsonTag tag)
+ Data.JsonSpec.OpenApi: type family Rename (spec :: Specification) :: Specification

Files

json-spec-openapi.cabal view
@@ -1,6 +1,6 @@ cabal-version:       3.0 name:                json-spec-openapi-version:             0.3.1.2+version:             1.0.0.0 synopsis:            json-spec-openapi description:   This package provides a way to produce@@ -64,7 +64,7 @@     , aeson                     >= 2.2.1.0  && < 2.3     , base                      >= 4.19.0.0 && < 4.21     , insert-ordered-containers >= 0.2.5.3  && < 0.3-    , json-spec                 >= 0.5.0.0  && < 1.1+    , json-spec                 >= 0.5.0.0  && < 1.2     , lens                      >= 5.2.3    && < 5.4     , openapi3                  >= 3.2.4    && < 3.3     , text                      >= 2.1      && < 2.2@@ -82,7 +82,8 @@   import: dependencies, warnings   exposed-modules:          Data.JsonSpec.OpenApi-  -- other-modules:       +  other-modules:       +    Data.JsonSpec.OpenApi.Rename   -- other-extensions:       hs-source-dirs:      src   default-language:    Haskell2010
src/Data/JsonSpec/OpenApi.hs view
@@ -1,9 +1,11 @@ {-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-}@@ -95,36 +97,47 @@   Schemaable,   EncodingSchema(..),   DecodingSchema(..),+  Rename, ) where   import Control.Lens (At(at), (&), over, set) import Data.Aeson (ToJSON(toJSON)) import Data.Functor.Identity (Identity(runIdentity))-import Data.JsonSpec (FieldSpec(Optional, Required),-  HasJsonDecodingSpec(DecodingSpec), HasJsonEncodingSpec(EncodingSpec),-  Specification(JsonArray, JsonBool, JsonDateTime, JsonEither, JsonInt,-  JsonLet, JsonNullable, JsonNum, JsonObject, JsonRaw, JsonRef,-  JsonString, JsonTag))-import Data.OpenApi (AdditionalProperties(AdditionalPropertiesAllowed),-  HasAdditionalProperties(additionalProperties), HasEnum(enum_),-  HasFormat(format), HasItems(items), HasOneOf(oneOf),-  HasProperties(properties), HasRequired(required), HasType(type_),-  NamedSchema(NamedSchema), OpenApiItems(OpenApiItemsObject),-  OpenApiType(OpenApiArray, OpenApiBoolean, OpenApiInteger, OpenApiNull,-  OpenApiNumber, OpenApiObject, OpenApiString), Reference(Reference),-  Referenced(Inline, Ref), ToSchema(declareNamedSchema), Definitions,-  Schema)+import Data.JsonSpec+  ( FieldSpec(Optional, Required), HasJsonDecodingSpec(DecodingSpec)+  , HasJsonEncodingSpec(EncodingSpec)+  , Specification+    ( JsonArray, JsonBool, JsonDateTime, JsonEither, JsonInt, JsonLet+    , JsonNullable, JsonNum, JsonObject, JsonRaw, JsonRef, JsonString, JsonTag+    )+  )+import Data.JsonSpec.OpenApi.Rename (Rename)+import Data.OpenApi+  ( AdditionalProperties(AdditionalPropertiesAllowed)+  , HasAdditionalProperties(additionalProperties), HasEnum(enum_)+  , HasFormat(format), HasItems(items), HasOneOf(oneOf)+  , HasProperties(properties), HasRequired(required), HasType(type_)+  , NamedSchema(NamedSchema), OpenApiItems(OpenApiItemsObject)+  , OpenApiType+    ( OpenApiArray, OpenApiBoolean, OpenApiInteger, OpenApiNull, OpenApiNumber+    , OpenApiObject, OpenApiString+    )+  , Reference(Reference), Referenced(Inline), ToSchema(declareNamedSchema)+  , Definitions, Schema+  ) import Data.OpenApi.Declare (DeclareT(runDeclareT), MonadDeclare(declare)) import Data.String (IsString(fromString)) import Data.Text (Text) import Data.Typeable (Proxy(Proxy), Typeable)-import GHC.TypeError (ErrorMessage((:$$:), (:<>:)), Unsatisfiable,-  unsatisfiable)+import GHC.TypeError (ErrorMessage((:$$:), (:<>:)), Unsatisfiable, unsatisfiable) import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)-import Prelude (Applicative(pure), Bool(False), Functor(fmap), Maybe(Just,-  Nothing), Monoid(mempty), ($), (.))+import Prelude+  ( Applicative(pure), Bool(False), Functor(fmap), Maybe(Just, Nothing)+  , Monoid(mempty), ($), (.)+  ) import qualified Data.HashMap.Strict.InsOrd as HMI+import qualified Data.OpenApi as OA import qualified GHC.TypeError as TE  @@ -192,37 +205,12 @@       been defined. -} toOpenApiSchema-  :: (Schemaable spec)+  :: forall spec.+     (Schemaable spec)   => Proxy (spec :: Specification)   -> (Definitions Schema, Schema)-toOpenApiSchema spec =-  runIdentity (runDeclareT (schemaable spec) mempty)---{-|-  Specifications that can be turned into OpenApi Schemas or a reference-  to a schema.--}-class Refable (spec :: Specification) where-  refable-    :: (MonadDeclare (Definitions Schema) m)-    => Proxy spec-    -> m (Referenced Schema)-instance (KnownSymbol name) => Refable (JsonRef name) where-  refable Proxy =-    pure (ref (sym @name))-instance-    ( Defs defs-    , KnownSymbol name-    )-  =>-    Refable (JsonLet defs (JsonRef name))-  where-    refable Proxy = do-      mkDefs (Proxy @defs)-      refable (Proxy @(JsonRef name))-instance {-# overlaps #-} (Schemaable a) => Refable a where-  refable = fmap Inline . schemaable+toOpenApiSchema Proxy =+  runIdentity (runDeclareT (schemaable @spec) mempty)   {-|@@ -236,180 +224,229 @@ class Schemaable (spec :: Specification) where   schemaable     :: (MonadDeclare (Definitions Schema) m)-    => Proxy spec-    -> m Schema-instance (KnownSymbol tag) => Schemaable ('JsonTag tag) where-  schemaable Proxy =+    => m Schema++instance (Inlineable '[] spec) => Schemaable spec where+  schemaable = inlineable @'[] @spec++class+    Inlineable+      (defs :: [ (Symbol, Specification) ])+      (spec :: Specification)+  where+    inlineable+      :: (MonadDeclare (Definitions Schema) m)+      => m Schema+instance (KnownSymbol tag) => Inlineable defs ('JsonTag tag) where+  inlineable =     pure $       mempty & set enum_ (Just [toJSON (sym @tag :: Text)])-instance Schemaable 'JsonString where-  schemaable Proxy =+instance Inlineable defs JsonString where+  inlineable =     pure $       mempty & set type_ (Just OpenApiString)-instance {- Schemaable ('JsonEither left right) -}-    ( Schemaable left-    , Schemaable right+instance {- Inlineable defs ('JsonEither left right) -}+    ( Refable defs left+    , Refable defs right     )   =>-    Schemaable ('JsonEither left right)+    Inlineable defs ('JsonEither left right)   where-    schemaable Proxy = do-      schemaLeft <- schemaable (Proxy @left)-      schemaRight <- schemaable (Proxy @right)+    inlineable = do+      schemaLeft <- refable @defs @left+      schemaRight <- refable @defs @right       pure $         mempty           & set oneOf (Just-              [ Inline schemaLeft-              , Inline schemaRight+              [ schemaLeft+              , schemaRight               ]           )-instance Schemaable 'JsonNum where-  schemaable Proxy =+instance Inlineable defs JsonNum where+  inlineable =     pure $       mempty & set type_ (Just OpenApiNumber)-instance Schemaable 'JsonInt where-  schemaable Proxy =+instance Inlineable defs JsonInt where+  inlineable =     pure $       mempty & set type_ (Just OpenApiInteger)-instance Schemaable ('JsonObject '[]) where-  schemaable Proxy =+instance Inlineable defs (JsonObject '[]) where+  inlineable =     pure $       mempty         & set type_ (Just OpenApiObject)         & set additionalProperties (Just (AdditionalPropertiesAllowed False))-instance {- Schemaable ('JsonObject ( Optional key spec : more )) -}-    ( Schemaable ('JsonObject more)-    , Refable spec+instance {- Inlineable defs (JsonObject ( Optional key spec : more )) -}+    ( Inlineable defs (JsonObject more)+    , Refable defs spec     , KnownSymbol key     )   =>-    Schemaable ('JsonObject ( Optional key spec : more ))+    Inlineable defs (JsonObject ( Optional key spec : more ))   where-    schemaable Proxy = do-      propertySchema <- refable (Proxy @spec)-      more <- schemaable (Proxy @('JsonObject more))+    inlineable = do+      propertySchema <- refable @defs @spec+      more <- inlineable @defs @('JsonObject more)       pure $         more           & over               properties               (set (at (sym @key)) (Just propertySchema))-instance {- Schemaable ('JsonObject ( Required key spec : more )) -}-    ( Schemaable ('JsonObject more)-    , Refable spec+instance {- Inlineable defs (JsonObject ( Required key spec : more )) -}+    ( Inlineable defs (JsonObject ( Optional key spec : more ))     , KnownSymbol key     )   =>-    Schemaable (JsonObject ( Required key spec : more ))+    Inlineable defs (JsonObject ( Required key spec : more ))   where-    schemaable Proxy = do-      schema <- schemaable (Proxy @(JsonObject ( Optional key spec : more )))+    inlineable = do+      schema <- inlineable @defs @(JsonObject ( Optional key spec : more ))       pure $         schema           & over required (sym @key:)-instance (Schemaable spec) => Schemaable ('JsonArray spec) where-  schemaable Proxy = do-    elementSchema <- schemaable (Proxy @spec)-    pure $-      mempty-        & set type_ (Just OpenApiArray)-        & set items (Just (OpenApiItemsObject (Inline elementSchema)))-instance Schemaable 'JsonBool where-  schemaable Proxy =+instance {- Inlineable defs (JsonArray spec) -}+    (Refable defs spec)+  =>+    Inlineable defs (JsonArray spec)+  where+    inlineable = do+      elementSchema <- refable @defs @spec+      pure $+        mempty+          & set type_ (Just OpenApiArray)+          & set items (Just (OpenApiItemsObject elementSchema))+instance Inlineable defs JsonBool where+  inlineable =     pure $       mempty & set type_ (Just OpenApiBoolean)-instance Schemaable 'JsonDateTime where-  schemaable Proxy =+instance Inlineable defs JsonDateTime where+  inlineable =     pure $       mempty         & set type_ (Just OpenApiString)         & set format (Just "date-time")-instance {- Undefined Let -}-    Unsatisfiable (-      T "`JsonRef \"" :<>: T target :<>: T "\"` is not defined.\n"-      :$$: T "You are trying to use a JsonRef as the \"top level\" "-      :$$: T "schema. We try to satisfy this request by looking up "-      :$$: T "the reference and inlining it.  However in this case you "-      :$$: T "are trying to reference a schema which is not defined, "-      :$$: T "so this won't work.\n"+instance (Refable defs spec) => Inlineable defs (JsonNullable spec) where+  inlineable = do+    schema <- refable @defs @spec+    pure $+      mempty+        & set oneOf (Just+            [ Inline (mempty & set type_ (Just OpenApiNull))+            , schema+            ]+        )+instance Inlineable defs JsonRaw where+  inlineable =+    pure $+      mempty+      & set type_ (Just OpenApiObject)+instance {- Inlineable defs (JsonLet newDefs spec) -}+    ( Inlineable (Concat newDefs defs) spec+    , Defs newDefs newDefs     )   =>-    Schemaable (JsonLet '[] (JsonRef target))+    Inlineable defs (JsonLet newDefs spec)   where-    schemaable = unsatisfiable-instance {- Schemaable (JsonLet ( '(target, def) ': more) (JsonRef target)) -}-    {-# overlaps #-}-    ( KnownSymbol target-    , Schemaable def-    , Schemaable (JsonLet more def)+    inlineable = do+      mkDefs @newDefs @newDefs+      inlineable @(Concat newDefs defs) @spec+instance {- Inlineable defs (JsonRef target) -}+    ( Deref defs defs target     )   =>-    Schemaable (JsonLet ( '(target, def) ': more) (JsonRef target))+    Inlineable defs (JsonRef target)   where-    schemaable Proxy = do-      defSchema <- schemaable (Proxy @def)-      declare (HMI.singleton (sym @target) defSchema)-      schemaable (Proxy @(JsonLet more def))-instance {- Schemaable (JsonLet ( '(name, def) ': more) (JsonRef target)) -}-    {-# overlaps #-}-    ( KnownSymbol name-    , Schemaable def-    , Schemaable (JsonLet more (JsonRef target))+    inlineable = deref @defs @defs @target+++{-|+  Specifications that can be turned into OpenApi Schemas or a reference+  to a schema.+-}+class+    Refable+      (defs :: [(Symbol, Specification)])+      (spec :: Specification)+  where+    refable+      :: (MonadDeclare (Definitions Schema) m)+      => m (Referenced Schema)+instance {-# overlappable #-} (Inlineable defs a) => Refable defs a where+  refable = fmap Inline (inlineable @defs @a)+instance+    ( Defs newDefs newDefs+    , Refable (Concat newDefs defs) spec     )   =>-    Schemaable (JsonLet ( '(name, def) ': more) (JsonRef target))+    Refable defs (JsonLet newDefs spec)   where-    schemaable Proxy = do-      defSchema <- schemaable (Proxy @def)-      declare (HMI.singleton (sym @name) defSchema)-      schemaable (Proxy @(JsonLet more (JsonRef target)))-instance {- Schemaable (JsonLet defs spec) -}-    {-# overlaps #-}-    ( Defs defs-    , Schemaable spec+    refable = do+      mkDefs @newDefs @newDefs+      refable @(Concat newDefs defs) @spec+instance (KnownSymbol name) => Refable defs (JsonRef name) where+  refable =+    pure (ref (sym @name))+++class+    Deref+      (defs   :: [(Symbol, Specification)])+      (search :: [(Symbol, Specification)])+      (target :: Symbol)+  where+    deref+      :: MonadDeclare (Definitions Schema) m+      => m Schema+instance (NotDereferenceable defs target) => Deref defs '[] target where+  deref = unsatisfiable+instance {- Deref defs ( '(target, spec) ': more) target -}+    ( Inlineable defs spec     )   =>-    Schemaable (JsonLet defs spec)+    Deref defs ( '(target, spec) ': more) target   where-    schemaable Proxy = do-      mkDefs (Proxy @defs)-      schemaable (Proxy @spec)-instance (Schemaable spec) => Schemaable (JsonNullable spec) where-  schemaable Proxy = do-    schema <- schemaable (Proxy @spec)-    pure $-      mempty-        & set oneOf (Just-            [ Inline (mempty & set type_ (Just OpenApiNull))-            , Inline schema-            ]-        )-instance Schemaable JsonRaw where-  schemaable Proxy =-    pure $-      mempty-      & set type_ (Just OpenApiObject)+    deref = inlineable @defs @spec+instance {- Deref defs ( '(miss, spec) ': more) target -}+    {-# overlaps #-}+    (Deref defs more target)+  =>+    Deref defs ( '(miss, spec) ': more) target+  where+    deref =+      deref @defs @more @target+type NotDereferenceable defs target =+  Unsatisfiable (+    TE.Text+      "Symbol not defined (in a position \+      \where we must dereference the referenced schema)."+      :$$: TE.Text "The symbol is: " :<>: TE.ShowType target+      :$$: TE.Text "The definitions in scope are: " :<>: TE.ShowType defs+  )   {-| Go through and make a declaration for each item in a JsonLet.  -}-class Defs (defs :: [(Symbol, Specification)]) where-  mkDefs-    :: (MonadDeclare (Definitions Schema) m)-    => Proxy defs-    -> m ()-instance Defs '[] where-  mkDefs Proxy = pure ()-instance-    ( Defs more-    , Schemaable spec+class+    Defs+      (allDefs :: [(Symbol, Specification)])+      (defs :: [(Symbol, Specification)])+  where+    mkDefs+      :: (MonadDeclare (Definitions Schema) m)+      => m ()+instance Defs defs '[] where+  mkDefs = pure ()+instance {- Defs defs ( '(name, spec) ': more) -}+    ( Defs defs more+    , Inlineable defs spec     , KnownSymbol name     )   =>-    Defs ( '(name, spec) ': more)+    Defs defs ( '(name, spec) ': more)   where-    mkDefs Proxy = do-      schema <- schemaable (Proxy @spec)+    mkDefs = do+      schema <- inlineable @defs @spec       declare (HMI.singleton (sym @name) schema)-      mkDefs (Proxy @more)+      mkDefs @defs @more   {-|@@ -475,10 +512,16 @@   ref :: Text -> Referenced a-ref = Ref . Reference+ref = OA.Ref . Reference  -{-| Shorthand for building custom type errors.  -}-type T (msg :: Symbol) = TE.Text msg+type family+    Concat+      (a :: [(Symbol, Specification)])+      (b :: [(Symbol, Specification)])+      :: [(Symbol, Specification)]+  where+    Concat '[] b = b+    Concat (a : more) b = a : Concat more b  
+ src/Data/JsonSpec/OpenApi/Rename.hs view
@@ -0,0 +1,611 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++module Data.JsonSpec.OpenApi.Rename (+  Rename+) where++import Data.JsonSpec+  ( FieldSpec(Optional, Required)+  , Specification+    ( JsonArray, JsonBool, JsonDateTime, JsonEither, JsonInt, JsonLet+    , JsonNullable, JsonNum, JsonObject, JsonRaw, JsonRef, JsonString, JsonTag+    )+  )+import GHC.TypeError (ErrorMessage((:$$:)))+import GHC.TypeLits (type (+), AppendSymbol, Nat, Symbol)+import qualified GHC.TypeError as TE+++{-|+  Resolve OpenApi name conflicts.++  The json-spec mechanism for giving names to things is more powerful than+  OpenApi mechanism for naming things. 'JsonLet' allows for nested scopes,+  where deeper names can shadow other names. OpenApi on the other hand only has+  a flat space to name schemas, so every name is "globally scoped", resulting+  in possible name conflicts when mapping 'JsonLet' names to OpenApi names.++  This type family resolves the conflict by renaming the 'JsonLet' names+  in your 'Specification' so that they are all unique, so they won't+  conflict when mapping them into the global OpenApi schema namespace.++  We do not apply this type family by default because it has the potential to+  add significant compilation time if your 'Specification's are large. If you+  happen to know that your 'Specification' contains no name conflicts then you+  can avoid paying that cost.++  It isn't perfect. I've tried to strike a balance between implementation+  complexity and avoiding unnecessary renames.++  Essentially, if a duplicate name is detected, I append a @".<n>"@ to the+  name, where @<n>@ is an integer. So if you are using names that /already/+  follow this format you might get into trouble.++  For instance, this 'Specification' will fail to rename properly:+  > JsonLet+  >   '[ '("foo", JsonString)+  >    , '("foo.1", JsonString)+  >    ]+  >    ( JsonObject+  >        '[ "field1" ::: JsonRef "foo"+  >         , "field2" ::: JsonLet '[ '("foo", JsonInt)] (JsonRef "foo")+  >         ]+  >    )++  because the "foo" in "field2" will be renamed to "foo.1", causing a+  new conflict with the existing "foo.1".+-}+type family Rename (spec :: Specification) :: Specification where+  Rename spec =+    Fst (FoldRename (G '[]) (A '[]) spec)+++type family+    Fst+      (a :: (Specification, Global))+      :: Specification+  where+    Fst '(spec, global) = spec+++type family+    FoldRename+      (global :: Global)+      (active :: Active)+      (spec   :: Specification)+      :: (Specification, Global)+  where+    FoldRename global active JsonString = '(JsonString, global)++    FoldRename global active JsonNum = '(JsonNum, global)++    FoldRename global active JsonInt = '(JsonInt, global)++    FoldRename global active JsonBool = '(JsonBool, global)++    FoldRename global active (JsonTag tag) = '(JsonTag tag, global)++    FoldRename global active JsonDateTime = '(JsonDateTime, global)++    FoldRename global active JsonRaw = '(JsonRaw, global)++    FoldRename global active (JsonNullable spec) =+      MapSpec+        JsonNullable+        (FoldRename global active spec)++    FoldRename global active (JsonArray spec) =+      MapSpec JsonArray (FoldRename global active spec)++    FoldRename global active (JsonObject fields) =+      RenameObject global active fields++    FoldRename global active (JsonEither left right) =+      RenameEither global active left right++    FoldRename global active (JsonLet defs spec) =+      RenameLet+        (UpdateGlobals global defs)+        active+        defs+        spec++    FoldRename global (A active) (JsonRef name) =+      '(JsonRef (LookupNewName name active), global)+++type family+    RenameObject+      (global :: Global)+      (active :: Active)+      (fields :: [FieldSpec])+      :: (Specification, Global)+  where+    RenameObject global active fields =+      RenameObject2+        (FoldReflectFields '( '[], global) active fields)+++type family+    RenameObject2+      (acc :: ([FieldSpec], Global))+      :: (Specification, Global)+  where+    RenameObject2 '(fields, global) =+      '(JsonObject (Reverse '[] fields), global)+++type family+    FoldReflectFields+      (   acc :: ([FieldSpec], Global))+      (active :: Active)+      (fields :: [FieldSpec])+      :: ([FieldSpec], Global)+  where+    FoldReflectFields '(acc, global) active '[] = '(acc, global)+    FoldReflectFields '(acc, global) active (field : more) =+      FoldReflectFields2+        acc+        active+        (ReflectField global active field)+        more+++type family+    FoldReflectFields2+      (   acc :: [FieldSpec])+      (active :: Active)+      ( field :: (FieldSpec, Global))+      (  more :: [FieldSpec])+      :: ([FieldSpec], Global)+  where+    FoldReflectFields2 acc active '(field, global) more =+      FoldReflectFields+        '(field : acc, global)+        active+        more+++type family+    ReflectField+      (global :: Global)+      (active :: Active)+      (field :: FieldSpec)+      :: (FieldSpec, Global)+  where+    ReflectField global active (Required name spec) =+      ReflectRequiredField+        name+        (FoldRename global active spec)+    ReflectField global active (Optional name spec) =+      ReflectOptionalField+        name+        (FoldRename global active spec)+++type family+    ReflectRequiredField+      (name :: Symbol)+      (spec :: (Specification, Global))+      :: (FieldSpec, Global)+  where+    ReflectRequiredField name '(spec, global) =+      '(Required name spec, global)+++type family+    ReflectOptionalField+      (name :: Symbol)+      (spec :: (Specification, Global))+      :: (FieldSpec, Global)+  where+    ReflectOptionalField name '(spec, global) =+      '(Optional name spec, global)+++type family+    RenameEither+      (global :: Global)+      (active :: Active)+      (  left :: Specification)+      ( right :: Specification)+      :: (Specification, Global)+  where+    RenameEither global active left right =+      RenameEither2+        active+        (FoldRename global active left)+        right+++type family+    RenameEither2+      (active :: Active)+      (  left :: (Specification, Global))+      ( right :: Specification)+      :: (Specification, Global)+  where+    RenameEither2 active '(left, global) right =+      RenameEither3 left (FoldRename global active right)+++type family+    RenameEither3+      (left :: Specification)+      (right :: (Specification, Global))+      :: (Specification, Global)+  where+    RenameEither3 left '(right, global) =+      '(JsonEither left right, global)+++type family+    UpdateGlobals+      (global :: Global)+      (  defs :: [(Symbol, Specification)])+      :: Global+  where+    UpdateGlobals global '[] = global+    UpdateGlobals (G global) ( '(name, spec) : more) =+      UpdateGlobals+        (G (IncrementName global name))+        more+++type family+    IncrementName+      (global :: [NameState])+      (  name :: Symbol)+      :: [NameState]+  where+    IncrementName '[] name = '[N name 0]+    IncrementName (N name ord : more) name =+      N name (ord + 1) : more+    IncrementName (N miss ord : more) name =+      N miss ord : IncrementName more name+++type family+    RenameLet+      (global :: Global) -- updated+      (active :: Active)+      (defs :: [(Symbol, Specification)])+      (spec :: Specification)+      :: (Specification, Global)+  where+    RenameLet global active defs spec =+      RenameLet2+        global+        (UpdateActives global active defs)+        defs+        spec+++type family+    RenameLet2+      (global :: Global) -- updated+      (active :: Active) -- updated+      (defs :: [(Symbol, Specification)])+      (spec :: Specification)+  where+    RenameLet2 global active defs spec =+      RenameLet3+        global+        active+        (RenameDefs global defs)+        spec+++type family+    RenameLet3+      (global :: Global)+      (active :: Active)+      (  defs :: [(Symbol, Specification)])+      (  spec :: Specification)+      :: (Specification, Global)+  where+    RenameLet3 global active defs spec =+      RenameLet4+        (ReflectDefs global active defs)+        active+        spec+++type family+    RenameLet4+      (     r :: (Global, [(Symbol, Specification)]))+      (active :: Active)+      (  spec :: Specification)+      :: (Specification, Global)+  where+    RenameLet4 '(global, defs) active spec =+      RenameLet5+        (FoldRename global active spec)+        defs+++type family+    RenameLet5+      (r :: (Specification, Global))+      (defs :: [(Symbol, Specification)])+      :: (Specification, Global)+  where+    RenameLet5 '(spec, global) defs =+      '(JsonLet defs spec, global)+++type family+    UpdateActives+      (global :: Global) -- already updated+      (active :: Active)+      (  defs :: [(Symbol, Specification)])+      :: Active+  where+    UpdateActives global active '[] = active+    UpdateActives global (A active) ( '(name, spec) : more) =+      UpdateActives+        global+        (A (SetActive global active name))+        more+++type family+    SetActive+      (global :: Global)+      (active :: [NameState])+      (  name :: Symbol)+      :: [NameState]+  where+    SetActive+        (G (N name ord : moreG))+        (N name x : moreA)+        name+      =+        (N name ord : moreA)+    SetActive+        (G (N name ord : moreG))+        (N miss x : moreA)+        name+      =+        N miss x : SetActive (G (N name ord : moreG)) moreA name+    SetActive+        (G (N name ord : moreG))+        '[]+        name+      =+        '[N name ord]+    SetActive+        (G (N miss x : moreG))+        active+        name+      =+        SetActive (G moreG) active name+++type family+    ReflectDefs+      (global :: Global)+      (active :: Active)+      (  defs :: [(Symbol, Specification)])+      :: (Global, [(Symbol, Specification)])+  where+    ReflectDefs global active defs =+      FoldReflectDefs '(global, '[]) active defs+++type family+    FoldReflectDefs+      (acc :: (Global, [(Symbol, Specification)]))+      (active :: Active)+      (defs :: [(Symbol, Specification)])+      :: (Global, [(Symbol, Specification)])+  where+    FoldReflectDefs acc active '[] = acc+    FoldReflectDefs+        '(global, acc)+        active+        ( '(name, spec) : more )+      =+        FoldReflectDefs2+          (FoldRename global active spec)+          active+          name+          acc+          more+++type family+    FoldReflectDefs2+      (  spec :: (Specification, Global))+      (active :: Active)+      (  name :: Symbol)+      (   acc :: [(Symbol, Specification)])+      (  more :: [(Symbol, Specification)])+      :: (Global, [(Symbol, Specification)])+  where+    FoldReflectDefs2 '(spec, global) active name acc more =+      FoldReflectDefs '(global, '(name, spec) : acc) active more+++{-| Update the LHS of the definitions, to match the already updated globals -}+type family+    RenameDefs+      (global :: Global)+      (  defs :: [(Symbol, Specification)])+      :: [(Symbol, Specification)]+  where+    RenameDefs globals '[] = '[]+    RenameDefs (G globals) ( '(name, spec) : more) =+      '(LookupNewName name globals, spec) : RenameDefs (G globals) more+++type family+    LookupNewName+      (target :: Symbol)+      ( names :: [NameState])+      :: Symbol+  where+    LookupNewName target (N target 0 : more) = target+    LookupNewName target (N target ord : more) =+      AppendSymbol target ( AppendSymbol "." ( ToSymbol ord))+    LookupNewName target (N miss ord : more) =+      LookupNewName target more+++type family+    ToSymbol+      (o :: Nat)+      :: Symbol+  where+    {-+      We _could_ do some fancy stuff with type level mod and division+      over Nat to handle an almost arbitrary number of name conflicts,+      but for now handling 50 levels of name conflict seems sufficient.+    -}+    ToSymbol 1 = "1"+    ToSymbol 2 = "2"+    ToSymbol 3 = "3"+    ToSymbol 4 = "4"+    ToSymbol 5 = "5"+    ToSymbol 6 = "6"+    ToSymbol 7 = "7"+    ToSymbol 8 = "8"+    ToSymbol 9 = "9"+    ToSymbol 10 = "10"+    ToSymbol 11 = "11"+    ToSymbol 12 = "12"+    ToSymbol 13 = "13"+    ToSymbol 14 = "14"+    ToSymbol 15 = "15"+    ToSymbol 16 = "16"+    ToSymbol 17 = "17"+    ToSymbol 18 = "18"+    ToSymbol 19 = "19"+    ToSymbol 20 = "20"+    ToSymbol 21 = "21"+    ToSymbol 22 = "22"+    ToSymbol 23 = "23"+    ToSymbol 24 = "24"+    ToSymbol 25 = "25"+    ToSymbol 26 = "26"+    ToSymbol 27 = "27"+    ToSymbol 28 = "28"+    ToSymbol 29 = "29"+    ToSymbol 30 = "30"+    ToSymbol 31 = "31"+    ToSymbol 32 = "32"+    ToSymbol 33 = "33"+    ToSymbol 34 = "34"+    ToSymbol 35 = "35"+    ToSymbol 36 = "36"+    ToSymbol 37 = "37"+    ToSymbol 38 = "38"+    ToSymbol 39 = "39"+    ToSymbol 40 = "40"+    ToSymbol 41 = "41"+    ToSymbol 42 = "42"+    ToSymbol 43 = "43"+    ToSymbol 44 = "44"+    ToSymbol 45 = "45"+    ToSymbol 46 = "46"+    ToSymbol 47 = "47"+    ToSymbol 48 = "48"+    ToSymbol 49 = "49"+    ToSymbol 50 = "50"+    ToSymbol 51 = "51"+    ToSymbol 52 = "52"+    ToSymbol 53 = "53"+    ToSymbol 54 = "54"+    ToSymbol 55 = "55"+    ToSymbol 56 = "56"+    ToSymbol 57 = "57"+    ToSymbol 58 = "58"+    ToSymbol 59 = "59"+    ToSymbol 60 = "60"+    ToSymbol 61 = "61"+    ToSymbol 62 = "62"+    ToSymbol 63 = "63"+    ToSymbol 64 = "64"+    ToSymbol 65 = "65"+    ToSymbol 66 = "66"+    ToSymbol 67 = "67"+    ToSymbol 68 = "68"+    ToSymbol 69 = "69"+    ToSymbol 70 = "70"+    ToSymbol 71 = "71"+    ToSymbol 72 = "72"+    ToSymbol 73 = "73"+    ToSymbol 74 = "74"+    ToSymbol 75 = "75"+    ToSymbol 76 = "76"+    ToSymbol 77 = "77"+    ToSymbol 78 = "78"+    ToSymbol 79 = "79"+    ToSymbol 80 = "80"+    ToSymbol 81 = "81"+    ToSymbol 82 = "82"+    ToSymbol 83 = "83"+    ToSymbol 84 = "84"+    ToSymbol 85 = "85"+    ToSymbol 86 = "86"+    ToSymbol 87 = "87"+    ToSymbol 88 = "88"+    ToSymbol 89 = "89"+    ToSymbol 90 = "90"+    ToSymbol 91 = "91"+    ToSymbol 92 = "92"+    ToSymbol 93 = "93"+    ToSymbol 94 = "94"+    ToSymbol 95 = "95"+    ToSymbol 96 = "96"+    ToSymbol 97 = "97"+    ToSymbol 98 = "98"+    ToSymbol 99 = "99"+    ToSymbol 100 = "100"++    ToSymbol a =+      TE.TypeError (+        TE.Text      "The author of json-spec-openapi was lazy and did"+        :$$: TE.Text "not foresee that anyone would write a specification"+        :$$: TE.Text "that would have to handle more than 100 collisions"+        :$$: TE.Text "for a single name when converting to an OpenApi"+        :$$: TE.Text "spec. Please submit a bug report on GitHub and"+        :$$: TE.Text "I'll add a fix."+      )+++type family+    MapSpec+      (f :: Specification -> Specification)+      (a :: (Specification, Global))+      :: (Specification, Global)+  where+    MapSpec f '(a, b) = '(f a, b)+++type family+    Reverse+      (acc  :: [FieldSpec])+      (list :: [FieldSpec])+      :: [FieldSpec]+  where+    Reverse acc '[] = acc+    Reverse acc (a : more) = Reverse (a : acc) more+++data NameState = N+  { _name :: Symbol+  ,  _ord :: Nat+  }+++newtype Global = G [NameState]+++newtype Active = A [NameState]++
test/test.hs view
@@ -7,6 +7,7 @@ {-# LANGUAGE TypeAbstractions #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE ViewPatterns #-} @@ -14,19 +15,25 @@  import Control.Lens (At(at), (&), set) import Data.Aeson (ToJSON(toJSON), FromJSON)-import Data.JsonSpec (Field(Field), FieldSpec(Optional,-  Required), HasJsonDecodingSpec(DecodingSpec, fromJSONStructure),-  HasJsonEncodingSpec(EncodingSpec, toJSONStructure), SpecJSON(SpecJSON),-  Specification(JsonArray, JsonBool, JsonDateTime, JsonEither, JsonInt,-  JsonLet, JsonNullable, JsonNum, JsonObject, JsonRaw, JsonRef,-  JsonString, JsonTag), unField)-import Data.JsonSpec.OpenApi (EncodingSchema, toOpenApiSchema)+import Data.JsonSpec+  ( Field(Field), FieldSpec(Optional, Required)+  , HasJsonDecodingSpec(DecodingSpec, fromJSONStructure)+  , HasJsonEncodingSpec(EncodingSpec, toJSONStructure), SpecJSON(SpecJSON)+  , Specification+    ( JsonArray, JsonBool, JsonDateTime, JsonEither, JsonInt, JsonLet+    , JsonNullable, JsonNum, JsonObject, JsonRaw, JsonRef, JsonString, JsonTag+    )+  , (:::), (::?), unField+  )+import Data.JsonSpec.OpenApi (EncodingSchema, Rename, toOpenApiSchema) import Data.OpenApi (Definitions, ToSchema) import Data.Proxy (Proxy(Proxy)) import Data.Text (Text) import Data.Time (UTCTime)-import Prelude (Applicative(pure), Bool(False), Functor(fmap),-  Maybe(Just), Monoid(mempty), ($), Eq, IO, Show)+import Prelude+  ( Applicative(pure), Bool(False), Functor(fmap), Maybe(Just), Monoid(mempty)+  , ($), (.), Eq, IO, Show+  ) import Test.Hspec (describe, hspec, it, shouldBe) import qualified Data.Aeson as Aeson import qualified Data.HashMap.Strict.InsOrd as HMI@@ -78,9 +85,12 @@                         & set OA.properties (                             mempty                               & set (at "tag") (Just (OA.Inline (-                                  mempty & set OA.enum_ (Just [toJSON ("a" :: Text)])+                                  mempty+                                    & set OA.enum_ (Just [toJSON ("a" :: Text)])                                 )))-                              & set (at "content") (Just (OA.Inline stringSchema))+                              & set (at "content") (Just (+                                  OA.Inline stringSchema+                                ))                           )                         & set OA.required ["tag", "content"]                         & set@@ -92,9 +102,12 @@                         & set OA.properties (                             mempty                               & set (at "tag") (Just (OA.Inline (-                                  mempty & set OA.enum_ (Just [toJSON ("b" :: Text)])+                                  mempty+                                    & set OA.enum_ (Just [toJSON ("b" :: Text)])                                 )))-                              & set (at "content") (Just (OA.Inline stringSchema))+                              & set (at "content") (Just (+                                  OA.Inline stringSchema+                                ))                           )                         & set OA.required ["tag", "content"]                         & set@@ -191,7 +204,9 @@               in                 mempty                   & set OA.type_ (Just OA.OpenApiArray)-                  & set OA.items (Just (OA.OpenApiItemsObject (OA.Inline elementSchema)))+                  & set OA.items (Just (+                      OA.OpenApiItemsObject (OA.Inline elementSchema)+                    ))             )          in@@ -353,7 +368,273 @@                     (Just (OA.AdditionalPropertiesAllowed False))             )         in-          actual `shouldBe` expected+          Aeson.encode actual `shouldBe` Aeson.encode expected++      it "Mutual recursion" $+        let+          barSchema :: OA.Schema+          barSchema =+            mempty+              & set OA.type_ (Just OA.OpenApiObject)+              & set+                  OA.additionalProperties+                  (Just (OA.AdditionalPropertiesAllowed False))+              & set+                  OA.properties+                  (+                    HMI.fromList+                      [ ( "recbar"+                        , OA.Inline (+                            mempty+                              & set OA.type_ (Just OA.OpenApiArray)+                              & set+                                  OA.items+                                  (+                                    Just+                                    . OA.OpenApiItemsObject+                                    . OA.Ref+                                    . OA.Reference+                                    $ "foo"+                                  )+                          )+                        )+                      , ( "valbar"+                        , OA.Inline stringSchema+                        )+                      ]+                  )+              & set OA.required ["recbar", "valbar"]++          expected :: (Definitions OA.Schema, OA.Schema)+          expected =+            ( HMI.fromList+                [ ( "foo"+                  , mempty+                      & set OA.type_ (Just OA.OpenApiObject)+                      & set+                          OA.additionalProperties+                          (Just (OA.AdditionalPropertiesAllowed False))+                      & set+                          OA.properties+                          (+                            HMI.fromList+                              [ ( "recfoo"+                                , OA.Inline (+                                    mempty+                                      & set OA.type_ (Just OA.OpenApiArray)+                                      & set+                                          OA.items+                                          (+                                            Just+                                            . OA.OpenApiItemsObject+                                            . OA.Ref+                                            . OA.Reference+                                            $ "bar"+                                          )+                                  )+                                )+                              , ( "valfoo"+                                , OA.Inline (+                                    mempty+                                      & set OA.type_ (Just OA.OpenApiInteger)+                                  )+                                )+                              ]+                          )+                      & set OA.required ["recfoo", "valfoo"]+                  )+                , ( "bar"+                  , barSchema+                  )+                ]+            , barSchema+            )++          actual :: (Definitions OA.Schema, OA.Schema)+          actual =+            toOpenApiSchema (Proxy @(+              JsonLet+                '[ '( "foo"+                    , JsonObject+                        '[ "recfoo" ::: JsonArray (JsonRef "bar")+                         , "valfoo" ::: JsonInt+                         ]+                    )+                 , '( "bar"+                    , JsonObject+                        '[ "recbar" ::: JsonArray (JsonRef "foo")+                         , "valbar" ::: JsonString+                         ]+                    )+                 ]+                 (JsonRef "bar")+            ))+        in+          Aeson.encode actual `shouldBe` Aeson.encode expected++      it "Nested name conflict" $+        let+          expected :: (Definitions OA.Schema, OA.Schema)+          expected =+            ( mempty+                & set (at "foo.1") (Just (stringSchema))+                & set (at "foo") (Just (stringSchema))+            , stringSchema+            )++          actual :: (Definitions OA.Schema, OA.Schema)+          actual =+            toOpenApiSchema (Proxy @(Rename (+              JsonLet+                '[ '("foo"+                    , JsonLet+                        '[ '( "foo"+                            , JsonString+                            )+                         ]+                         (JsonRef "foo")+                    )+                 ]+                 (JsonRef "foo")+            )))+        in+          Aeson.encode actual `shouldBe` Aeson.encode expected++      it "Nested name conflict 2" $+        let+          expectedFooSchema :: OA.Schema+          expectedFooSchema =+            mempty+              & set OA.type_ (Just OA.OpenApiObject)+              & set+                  OA.additionalProperties+                  (Just (OA.AdditionalPropertiesAllowed False))+              & set OA.properties (+                  mempty+                    & set (at "field1") (Just (+                        OA.Ref (OA.Reference "foo.1")+                      ))+                    & set (at "field2") (Just (+                        OA.Ref (OA.Reference "bar")+                      ))+                )++          expected :: (Definitions OA.Schema, OA.Schema)+          expected =+            ( mempty+                & set (at "bar") (Just stringSchema)+                & set (at "foo.1") (Just stringSchema)+                & set (at "foo") (Just expectedFooSchema)+            , expectedFooSchema+            )++          actual :: (Definitions OA.Schema, OA.Schema)+          actual =+            toOpenApiSchema (Proxy @(Rename (+              JsonLet+                '[ '("foo"+                    , JsonLet+                        '[ '( "foo"+                            , JsonString+                            )+                         ]+                         (+                           JsonObject+                             '[ ("field1" ::? JsonRef "foo")+                              , ("field2" ::? JsonRef "bar")+                              ]+                         )+                    )+                 , '( "bar"+                    , JsonString+                    )+                 ]+                 (JsonRef "foo")+            )))+        in+          Aeson.encode actual `shouldBe` Aeson.encode expected++      it "Nested name conflict 3" $+        let+          expected :: (Definitions OA.Schema, OA.Schema)+          expected =+            ( mempty+                & set (at "foo.1") (Just stringSchema)+                & set (at "foo.2") (Just (+                    mempty & set OA.type_ (Just OA.OpenApiInteger)+                  ))+                & set (at "foo.3") (Just (+                    mempty & set OA.type_ (Just OA.OpenApiBoolean)+                  ))+                & set (at "foo") (Just (+                    mempty+                      & set OA.type_ (Just OA.OpenApiObject)+                      & set OA.properties (HMI.fromList+                          [ ("field1", OA.Ref (OA.Reference "foo.1"))+                          , ("field2", OA.Ref (OA.Reference "foo.2"))+                          , ("field3", OA.Ref (OA.Reference "foo.3"))+                          ]+                        )+                      & set OA.additionalProperties (Just (+                          OA.AdditionalPropertiesAllowed False+                        ))+                      & set OA.required ["field1", "field2", "field3"]+                  ))+                & set (at "foo.4") (Just (+                    mempty+                      & set OA.oneOf (+                          Just+                            [ OA.Inline $+                                mempty & set OA.type_ (Just OA.OpenApiNull)+                            , OA.Inline $ stringSchema+                            ]+                        )+                  ))+            , mempty+                & set OA.type_ (Just OA.OpenApiObject)+                & set OA.properties (HMI.fromList+                    [ ("field1", OA.Ref (OA.Reference "foo"))+                    , ("field2", OA.Ref (OA.Reference "foo.4"))+                    ]+                  )+                & set OA.required ["field1", "field2"]+                & set OA.additionalProperties (Just (+                    OA.AdditionalPropertiesAllowed False+                  ))+            )++          actual :: (Definitions OA.Schema, OA.Schema)+          actual =+            toOpenApiSchema (Proxy @(Rename (+              JsonLet+                '[ '( "foo"+                    , JsonLet+                        '[ '("foo", JsonString) ]+                         (+                           JsonObject+                            '[ "field1" ::: JsonRef "foo"+                             , "field2" ::: JsonLet+                                             '[ '("foo", JsonInt) ]+                                              (JsonRef "foo")+                             , "field3" ::: JsonLet+                                             '[ '("foo", JsonBool) ]+                                              (JsonRef "foo")+                             ]++                         )+                    )+                 ]+                 (+                   JsonObject+                     '[ "field1" ::: JsonRef "foo"+                      , "field2" ::: JsonLet+                                      '[ '("foo", JsonNullable JsonString) ]+                                       (JsonRef "foo")+                      ]+                 )+            )))+        in+          Aeson.encode actual `shouldBe` Aeson.encode expected      describe "EncodingSchema" $       it "works" $