{-# LANGUAGE CPP #-} module Composite.Swagger.TH where import Composite.CoRecord (Field) import Composite.Swagger.Base (wrappedSchema) import Data.Proxy (Proxy (Proxy)) import Data.Swagger (ToSchema, declareNamedSchema) import Language.Haskell.TH ( Dec, Name, Q, mkName , bang, bangType, clause, conT, cxt, funD, instanceD, newtypeD, normalB, recC, varBangType , wildP , noSourceStrictness, noSourceUnpackedness ) -- |TH splice which makes it more convenient to define 'ToSchema' instance for 'Record' types. -- -- For example: -- -- @ -- type MyRecord = '[FFoo, FBar] -- newtype MyRecordWrapper = MyRecordWrapper { unMyRecordWrapper :: Record MyRecord } -- makeToSchema "MyRecordWrapper" ''MyRecordWrapper -- @ -- -- is equivalent to: -- -- @ -- instance ToSchema MyRecordWrapper where -- declareNamedSchema = wrappedSchema (Proxy :: Proxy MyRecordWrapper) "MyRecordWrapper" -- @ makeToSchema :: String -> Name -> Q [Dec] makeToSchema schemaName wrapperName = sequence [ instanceD (cxt []) [t| ToSchema $(conT wrapperName) |] [ funD 'declareNamedSchema [ clause [wildP] (normalB [| wrappedSchema (Proxy :: Proxy $(conT wrapperName)) schemaName |]) [] ] ] ] -- |TH splice which makes it more convenient to define 'ToSchema' instance for 'Record' types. -- -- For example: -- -- @ -- type MyRecord = '[FFoo, FBar] -- makeToSchemaWrapper "MyRecordWrapper" ''MyRecord -- @ -- -- is equivalent to: -- -- @ -- newtype MyRecordWrapper = MyRecordWrapper { unMyRecordWrapper :: Record MyRecord } -- instance ToSchema MyRecordWrapper where -- declareNamedSchema = wrappedSchema (Proxy :: Proxy MyRecordWrapper) "MyRecordWrapper" -- @ makeToSchemaWrapper :: String -> Name -> Q [Dec] makeToSchemaWrapper wrapperNameStr fieldsTyName = do let wrapperName = mkName wrapperNameStr extractorName = mkName $ "un" <> wrapperNameStr fieldTy = [t| Field $(conT fieldsTyName) |] wrapperNewtype <- newtypeD (cxt []) wrapperName [] -- TyVarBndrs Nothing -- kind (recC wrapperName [varBangType extractorName (bangType (bang noSourceUnpackedness noSourceStrictness) fieldTy)]) #if MIN_VERSION_template_haskell(2,12,0) [] -- deriving context #else (cxt []) #endif wrapperInstances <- makeToSchema wrapperNameStr wrapperName pure $ wrapperNewtype:wrapperInstances