{-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PackageImports #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeSynonymInstances #-} module Data.Swagger.Internal.ParamSchema where import Control.Lens import Data.Aeson import Data.Proxy import GHC.Generics import Data.Int import "unordered-containers" Data.HashSet (HashSet) import Data.Monoid import Data.Set (Set) import Data.Scientific import qualified Data.Text as T import qualified Data.Text.Lazy as TL import Data.Time import Data.Word import Data.Swagger.Internal import Data.Swagger.Lens import Data.Swagger.SchemaOptions -- | Convert a type into a plain @'ParamSchema'@. -- -- An example type and instance: -- -- @ -- {-\# LANGUAGE OverloadedStrings \#-} -- allows to write 'T.Text' literals -- -- import Control.Lens -- -- data Direction = Up | Down -- -- instance ToParamSchema Direction where -- toParamSchema = mempty -- & schemaType .~ SwaggerString -- & schemaEnum .~ [ \"Up\", \"Down\" ] -- @ -- -- Instead of manually writing your @'ToParamSchema'@ instance you can -- use a default generic implementation of @'toParamSchema'@. -- -- To do that, simply add @deriving 'Generic'@ clause to your datatype -- and declare a @'ToParamSchema'@ instance for your datatype without -- giving definition for @'toParamSchema'@. -- -- For instance, the previous example can be simplified into this: -- -- @ -- {-\# LANGUAGE DeriveGeneric \#-} -- -- import GHC.Generics (Generic) -- -- data Direction = Up | Down deriving Generic -- -- instance ToParamSchema Direction -- @ class ToParamSchema a where -- | Convert a type into a plain parameter schema. -- -- >>> encode $ toParamSchema (Proxy :: Proxy Integer) -- "{\"type\":\"integer\"}" toParamSchema :: proxy a -> ParamSchema t default toParamSchema :: (Generic a, GToParamSchema (Rep a)) => proxy a -> ParamSchema t toParamSchema = genericToParamSchema defaultSchemaOptions instance {-# OVERLAPPING #-} ToParamSchema String where toParamSchema _ = mempty & schemaType .~ SwaggerString instance ToParamSchema Bool where toParamSchema _ = mempty & schemaType .~ SwaggerBoolean instance ToParamSchema Integer where toParamSchema _ = mempty & schemaType .~ SwaggerInteger instance ToParamSchema Int where toParamSchema = toParamSchemaBoundedIntegral instance ToParamSchema Int8 where toParamSchema = toParamSchemaBoundedIntegral instance ToParamSchema Int16 where toParamSchema = toParamSchemaBoundedIntegral instance ToParamSchema Int32 where toParamSchema proxy = toParamSchemaBoundedIntegral proxy & schemaFormat ?~ "int32" instance ToParamSchema Int64 where toParamSchema proxy = toParamSchemaBoundedIntegral proxy & schemaFormat ?~ "int64" instance ToParamSchema Word where toParamSchema = toParamSchemaBoundedIntegral instance ToParamSchema Word8 where toParamSchema = toParamSchemaBoundedIntegral instance ToParamSchema Word16 where toParamSchema = toParamSchemaBoundedIntegral instance ToParamSchema Word32 where toParamSchema = toParamSchemaBoundedIntegral instance ToParamSchema Word64 where toParamSchema = toParamSchemaBoundedIntegral -- | Default plain schema for @'Bounded'@, @'Integral'@ types. -- -- >>> encode $ toParamSchemaBoundedIntegral (Proxy :: Proxy Int8) -- "{\"maximum\":127,\"minimum\":-128,\"type\":\"integer\"}" toParamSchemaBoundedIntegral :: forall proxy a t. (Bounded a, Integral a) => proxy a -> ParamSchema t toParamSchemaBoundedIntegral _ = mempty & schemaType .~ SwaggerInteger & schemaMinimum ?~ fromInteger (toInteger (minBound :: a)) & schemaMaximum ?~ fromInteger (toInteger (maxBound :: a)) instance ToParamSchema Char where toParamSchema _ = mempty & schemaType .~ SwaggerString & schemaMaxLength ?~ 1 & schemaMinLength ?~ 1 instance ToParamSchema Scientific where toParamSchema _ = mempty & schemaType .~ SwaggerNumber instance ToParamSchema Double where toParamSchema _ = mempty & schemaType .~ SwaggerNumber & schemaFormat ?~ "double" instance ToParamSchema Float where toParamSchema _ = mempty & schemaType .~ SwaggerNumber & schemaFormat ?~ "float" timeParamSchema :: String -> ParamSchema t timeParamSchema format = mempty & schemaType .~ SwaggerString & schemaFormat ?~ T.pack format -- | Format @"date"@ corresponds to @yyyy-mm-dd@ format. instance ToParamSchema Day where toParamSchema _ = timeParamSchema "date" -- | -- >>> toParamSchema (Proxy :: Proxy LocalTime) ^. schemaFormat -- Just "yyyy-mm-ddThh:MM:ss" instance ToParamSchema LocalTime where toParamSchema _ = timeParamSchema "yyyy-mm-ddThh:MM:ss" -- | -- >>> toParamSchema (Proxy :: Proxy ZonedTime) ^. schemaFormat -- Just "yyyy-mm-ddThh:MM:ss+hhMM" instance ToParamSchema ZonedTime where toParamSchema _ = timeParamSchema "yyyy-mm-ddThh:MM:ss+hhMM" -- | -- >>> toParamSchema (Proxy :: Proxy UTCTime) ^. schemaFormat -- Just "yyyy-mm-ddThh:MM:ssZ" instance ToParamSchema UTCTime where toParamSchema _ = timeParamSchema "yyyy-mm-ddThh:MM:ssZ" instance ToParamSchema NominalDiffTime where toParamSchema _ = toParamSchema (Proxy :: Proxy Integer) instance ToParamSchema T.Text where toParamSchema _ = toParamSchema (Proxy :: Proxy String) instance ToParamSchema TL.Text where toParamSchema _ = toParamSchema (Proxy :: Proxy String) instance ToParamSchema All where toParamSchema _ = toParamSchema (Proxy :: Proxy Bool) instance ToParamSchema Any where toParamSchema _ = toParamSchema (Proxy :: Proxy Bool) instance ToParamSchema a => ToParamSchema (Sum a) where toParamSchema _ = toParamSchema (Proxy :: Proxy a) instance ToParamSchema a => ToParamSchema (Product a) where toParamSchema _ = toParamSchema (Proxy :: Proxy a) instance ToParamSchema a => ToParamSchema (First a) where toParamSchema _ = toParamSchema (Proxy :: Proxy a) instance ToParamSchema a => ToParamSchema (Last a) where toParamSchema _ = toParamSchema (Proxy :: Proxy a) instance ToParamSchema a => ToParamSchema (Dual a) where toParamSchema _ = toParamSchema (Proxy :: Proxy a) instance ToParamSchema a => ToParamSchema [a] where toParamSchema _ = mempty & schemaType .~ SwaggerArray & schemaItems ?~ SwaggerItemsPrimitive Nothing (toParamSchema (Proxy :: Proxy a)) instance ToParamSchema a => ToParamSchema (Set a) where toParamSchema _ = toParamSchema (Proxy :: Proxy [a]) & schemaUniqueItems ?~ True instance ToParamSchema a => ToParamSchema (HashSet a) where toParamSchema _ = toParamSchema (Proxy :: Proxy (Set a)) -- | -- >>> encode $ toParamSchema (Proxy :: Proxy ()) -- "{\"type\":\"string\",\"enum\":[\"_\"]}" instance ToParamSchema () where toParamSchema _ = mempty & schemaType .~ SwaggerString & schemaEnum ?~ ["_"] -- | A configurable generic @'ParamSchema'@ creator. -- -- >>> :set -XDeriveGeneric -- >>> data Color = Red | Blue deriving Generic -- >>> encode $ genericToParamSchema defaultSchemaOptions (Proxy :: Proxy Color) -- "{\"type\":\"string\",\"enum\":[\"Red\",\"Blue\"]}" genericToParamSchema :: forall proxy a t. (Generic a, GToParamSchema (Rep a)) => SchemaOptions -> proxy a -> ParamSchema t genericToParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy (Rep a)) mempty class GToParamSchema (f :: * -> *) where gtoParamSchema :: SchemaOptions -> proxy f -> ParamSchema t -> ParamSchema t instance GToParamSchema f => GToParamSchema (D1 d f) where gtoParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy f) instance GToParamSchema f => GToParamSchema (C1 c (S1 s f)) where gtoParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy f) instance ToParamSchema c => GToParamSchema (K1 i c) where gtoParamSchema _ _ _ = toParamSchema (Proxy :: Proxy c) instance (GEnumParamSchema f, GEnumParamSchema g) => GToParamSchema (f :+: g) where gtoParamSchema opts _ = genumParamSchema opts (Proxy :: Proxy (f :+: g)) class GEnumParamSchema (f :: * -> *) where genumParamSchema :: SchemaOptions -> proxy f -> ParamSchema t -> ParamSchema t instance (GEnumParamSchema f, GEnumParamSchema g) => GEnumParamSchema (f :+: g) where genumParamSchema opts _ = genumParamSchema opts (Proxy :: Proxy f) . genumParamSchema opts (Proxy :: Proxy g) instance Constructor c => GEnumParamSchema (C1 c U1) where genumParamSchema opts _ s = s & schemaType .~ SwaggerString & schemaEnum %~ addEnumValue tag where tag = toJSON (constructorTagModifier opts (conName (Proxy3 :: Proxy3 c f p))) addEnumValue x Nothing = Just [x] addEnumValue x (Just xs) = Just (x:xs) data Proxy3 a b c = Proxy3