Skip to content

Commit 1c864d6

Browse files
committed
add deriveEntity TH function
1 parent 00bb10f commit 1c864d6

File tree

2 files changed

+54
-2
lines changed

2 files changed

+54
-2
lines changed

postgresql-query.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ library
6262
, blaze-builder
6363
, bytestring
6464
, containers
65+
, data-default
6566
, either
6667
, exceptions
6768
, file-embed

src/Database/PostgreSQL/Query/TH.hs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ module Database.PostgreSQL.Query.TH
22
( -- * Deriving instances
33
deriveFromRow
44
, deriveToRow
5+
, deriveEntity
6+
, EntityOptions(..)
57
-- * Embedding sql files
68
, embedSql
79
, sqlFile
@@ -14,24 +16,31 @@ module Database.PostgreSQL.Query.TH
1416
import Prelude
1517

1618
import Control.Applicative
19+
import Data.Default
1720
import Data.FileEmbed ( embedFile )
21+
import Database.PostgreSQL.Query.Entity ( Entity(..) )
22+
import Database.PostgreSQL.Query.TH.SqlExp
1823
import Database.PostgreSQL.Simple.FromRow ( FromRow(..), field )
1924
import Database.PostgreSQL.Simple.ToRow ( ToRow(..) )
2025
import Database.PostgreSQL.Simple.Types ( Query(..) )
2126
import Language.Haskell.TH
22-
import Database.PostgreSQL.Query.TH.SqlExp
23-
2427

28+
-- | Return constructor name
2529
cName :: (Monad m) => Con -> m Name
2630
cName (NormalC n _) = return n
2731
cName (RecC n _) = return n
2832
cName _ = error "Constructor must be simple"
2933

34+
-- | Return count of constructor fields
3035
cArgs :: (Monad m) => Con -> m Int
3136
cArgs (NormalC _ n) = return $ length n
3237
cArgs (RecC _ n) = return $ length n
3338
cArgs _ = error "Constructor must be simple"
3439

40+
cFieldNames :: Con -> [Name]
41+
cFieldNames (RecC _ vst) = map (\(a, _, _) -> a) vst
42+
cFieldNames _ = error "Constructor must be a record (product type with field names)"
43+
3544
-- | Derive 'FromRow' instance. i.e. you have type like that
3645
--
3746
-- @
@@ -114,6 +123,48 @@ deriveToRow t = do
114123
(\e -> AppE (VarE tof) (VarE e))
115124
v
116125

126+
data EntityOptions = EntityOptions
127+
{ eoTableName :: String -> String -- ^ Type name to table name converter
128+
, eoColumnNames :: String -> String -- ^ Record field to column name converter
129+
, eoDeriveClassess :: [Name] -- ^ Typeclasses to derive for Id
130+
, eoIdType :: Name -- ^ Base type for Id
131+
}
132+
133+
instance Default EntityOptions where
134+
def = EntityOptions
135+
{ eoTableName = id
136+
, eoColumnNames = id
137+
, eoDeriveClassess = [''Ord, ''Eq, ''Show]
138+
, eoIdType = ''Integer
139+
}
140+
141+
-- | Derives instance for 'Entity' using type name and field names.
142+
deriveEntity :: EntityOptions -> Name -> Q [Dec]
143+
deriveEntity opts tname = do
144+
TyConI (DataD _ _ _ [tcon] _) <- reify tname
145+
econt <- [t|Entity $(conT tname)|]
146+
ConT entityIdName <- [t|EntityId|]
147+
let tnames = nameBase tname
148+
idname = tnames ++ "Id"
149+
unidname = "get" ++ idname
150+
idtype = ConT (eoIdType opts)
151+
idcon = RecC (mkName idname)
152+
[(mkName unidname, NotStrict, idtype)]
153+
iddec = NewtypeInstD [] entityIdName [ConT tname]
154+
idcon (eoDeriveClassess opts)
155+
tblName = eoTableName opts tnames
156+
fldNames = map (eoColumnNames opts . nameBase) $ cFieldNames tcon
157+
VarE tableName <- [e|tableName|]
158+
VarE fieldNames <- [e|fieldNames|]
159+
let tbldec = FunD tableName [Clause [WildP] (NormalB $ LitE $ stringL tblName) []]
160+
flddec = FunD fieldNames [Clause [WildP] (NormalB $ ListE $ map (LitE . stringL) fldNames) []]
161+
ret = InstanceD [] econt
162+
[ iddec, tbldec, flddec ]
163+
syndec = TySynD (mkName idname) [] (AppT (ConT entityIdName) (ConT tname))
164+
return [ret, syndec]
165+
166+
167+
117168
-- embed sql file as value
118169
embedSql :: String -- ^ File path
119170
-> Q Exp

0 commit comments

Comments
 (0)