module Database.PostgreSQL.Query.TH.Entity
( EntityOptions(..)
, deriveEntity
) where
import Data.Default
import Database.PostgreSQL.Query.Entity.Class
import Database.PostgreSQL.Query.Import
import Database.PostgreSQL.Query.TH.Common
import Database.PostgreSQL.Query.Types ( FN(..), textFN )
import Database.PostgreSQL.Simple.FromField
import Database.PostgreSQL.Simple.ToField
import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Text.Inflections
import qualified Data.Text as T
data EntityOptions = EntityOptions
{ eoTableName :: Text -> FN
, eoColumnNames :: Text -> FN
, eoDeriveClasses :: [Name]
, eoIdType :: Name
} deriving (Generic)
#if !MIN_VERSION_inflections(0,3,0)
toUnderscore' :: Text -> Text
toUnderscore' = T.pack . toUnderscore . T.unpack
#else
toUnderscore' :: Text -> Text
toUnderscore' = either error' id . toUnderscore
where
error' er = error $ "toUnderscore: " ++ show er
#endif
instance Default EntityOptions where
def = EntityOptions
{ eoTableName = textFN . toUnderscore'
, eoColumnNames = textFN . toUnderscore'
, eoDeriveClasses = [ ''Ord, ''Eq, ''Show
, ''FromField, ''ToField ]
, eoIdType = ''Integer
}
deriveEntity :: EntityOptions -> Name -> Q [Dec]
deriveEntity opts tname = do
tcon <- dataConstructors <$> reify tname >>= \case
[a] -> return a
x -> fail $ "expected exactly 1 data constructor, but " ++ show (length x) ++ " got"
econt <- [t|Entity $(conT tname)|]
ConT entityIdName <- [t|EntityId|]
let tnames = nameBase tname
idname = tnames ++ "Id"
unidname = "get" ++ idname
idtype = ConT (eoIdType opts)
#if MIN_VERSION_template_haskell(2,12,0)
idcon = RecC (mkName idname)
[(mkName unidname, Bang NoSourceUnpackedness NoSourceStrictness, idtype)]
iddec = NewtypeInstD [] entityIdName [ConT tname] Nothing
idcon [DerivClause Nothing (map ConT $ eoDeriveClasses opts)]
#elif MIN_VERSION_template_haskell(2,11,0)
idcon = RecC (mkName idname)
[(mkName unidname, Bang NoSourceUnpackedness NoSourceStrictness, idtype)]
iddec = NewtypeInstD [] entityIdName [ConT tname] Nothing
idcon (map ConT $ eoDeriveClasses opts)
#else
idcon = RecC (mkName idname)
[(mkName unidname, NotStrict, idtype)]
iddec = NewtypeInstD [] entityIdName [ConT tname]
idcon (eoDeriveClasses opts)
#endif
tblName = eoTableName opts $ T.pack tnames
fldNames = map (eoColumnNames opts . T.pack . nameBase)
$ cFieldNames tcon
VarE ntableName <- [e|tableName|]
VarE nfieldNames <- [e|fieldNames|]
tblExp <- lift (tblName :: FN)
fldExp <- mapM lift (fldNames :: [FN])
let tbldec = FunD ntableName [Clause [WildP] (NormalB tblExp) []]
flddec = FunD nfieldNames [Clause [WildP] (NormalB $ ListE fldExp) []]
#if MIN_VERSION_template_haskell(2,11,0)
ret = InstanceD Nothing [] econt [ iddec, tbldec, flddec ]
#else
ret = InstanceD [] econt [ iddec, tbldec, flddec ]
#endif
syndec = TySynD (mkName idname) [] (AppT (ConT entityIdName) (ConT tname))
return [ret, syndec]