proto3-suite-0.7.0: src/Proto3/Suite/DotProto/Generate/Swagger.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MonoLocalBinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- | This module provides helper functions to generate Swagger schemas that
-- describe JSONPB encodings for protobuf types.
module Proto3.Suite.DotProto.Generate.Swagger
( ppSchema
, asProxy
, insOrdFromList
)
where
#if MIN_VERSION_swagger2(2,4,0)
import Control.Lens ((&), (?~))
#else
import Control.Lens ((&), (.~), (?~))
#endif
import Data.Aeson (Value (String))
import Data.Aeson.Encode.Pretty (encodePretty)
import qualified Data.ByteString.Lazy.Char8 as LC8
import Data.Hashable (Hashable)
import Data.HashMap.Strict.InsOrd (InsOrdHashMap)
import qualified Data.HashMap.Strict.InsOrd
import Data.Swagger
import qualified Data.Text as T
import Data.Proxy
import qualified Data.Vector as V
import GHC.Exts (Proxy#, proxy#)
import Google.Protobuf.Wrappers.Polymorphic (Wrapped(..))
import Proto3.Suite (Enumerated (..), Finite (..),
Fixed (..), Named (..),
Nested (..), NestedVec (..),
PackedVec (..), Signed (..),
UnpackedVec (..), enumerate)
import qualified Proto3.Suite.Types
import Proto3.Suite.DotProto.Generate.Swagger.Wrappers ()
-- | Convenience re-export so that users of generated code don't have to add
-- an explicit dependency on @insert-ordered-containers@
insOrdFromList :: (Eq k, Hashable k) => [(k, v)] -> InsOrdHashMap k v
insOrdFromList = Data.HashMap.Strict.InsOrd.fromList
-- Distinctions between varint and fixed-width formats do not matter to JSONPB.
deriving newtype instance ToSchema a => ToSchema (Fixed a)
-- Zig-zag encoding issues do not matter to JSONPB.
deriving newtype instance ToSchema a => ToSchema (Signed a)
-- Packed/unpacked distinctions do not matter to JSONPB.
deriving via (V.Vector a) instance ToSchema a => ToSchema (NestedVec a)
deriving via (V.Vector a) instance ToSchema a => ToSchema (PackedVec a)
deriving via (V.Vector a) instance ToSchema a => ToSchema (UnpackedVec a)
-- Unless and until the overlapping instances for @Maybe (Wrapped _)@
-- are selected, the schema is unaffected by 'Wrapped'.
deriving newtype instance ToSchema a => ToSchema (Wrapped a)
instance ToSchema (Proto3.Suite.Types.String a) where
declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy String)
instance ToSchema (Proto3.Suite.Types.Bytes a) where
declareNamedSchema _ = pure (NamedSchema Nothing byteSchema)
-- Note that the context is @ToSchema (Maybe a)@, NOT @ToSchema a@.
-- This design keeps this instance from bypassing overlapping
-- instances such as @ToSchema (Maybe (Wrapped Bool))@ that
-- are included by cabal flag @-fswagger-wrapper-format@.
-- We use MonoLocalBinds to avoid the resultant compiler warning.
deriving via (Maybe a) instance ToSchema (Maybe a) => ToSchema (Nested a)
{-| This is a convenience function that uses type inference to select the
correct instance of `ToSchema` to use for fields of a message
-}
asProxy :: (Proxy a -> b) -> Proxy a
asProxy _ = Proxy
-- | Pretty-prints a schema. Useful when playing around with schemas in the
-- REPL.
ppSchema :: ToSchema a => Proxy a -> IO ()
ppSchema = LC8.putStrLn . encodePretty . toSchema
-- | JSONPB schemas for protobuf enumerations
instance (Finite e, Named e) => ToSchema (Enumerated e) where
declareNamedSchema _ = do
let enumName = nameOf (proxy# :: Proxy# e)
let dropPrefix = T.drop (T.length enumName)
let enumMemberNames = dropPrefix . fst <$> enumerate (proxy# :: Proxy# e)
return $ NamedSchema (Just enumName)
$ mempty
#if MIN_VERSION_swagger2(2,4,0)
& type_ ?~ SwaggerString
#else
& type_ .~ SwaggerString
#endif
& enum_ ?~ fmap String enumMemberNames