@@ -2,6 +2,8 @@ module Database.PostgreSQL.Query.TH
2
2
( -- * Deriving instances
3
3
deriveFromRow
4
4
, deriveToRow
5
+ , deriveEntity
6
+ , EntityOptions (.. )
5
7
-- * Embedding sql files
6
8
, embedSql
7
9
, sqlFile
@@ -14,24 +16,31 @@ module Database.PostgreSQL.Query.TH
14
16
import Prelude
15
17
16
18
import Control.Applicative
19
+ import Data.Default
17
20
import Data.FileEmbed ( embedFile )
21
+ import Database.PostgreSQL.Query.Entity ( Entity (.. ) )
22
+ import Database.PostgreSQL.Query.TH.SqlExp
18
23
import Database.PostgreSQL.Simple.FromRow ( FromRow (.. ), field )
19
24
import Database.PostgreSQL.Simple.ToRow ( ToRow (.. ) )
20
25
import Database.PostgreSQL.Simple.Types ( Query (.. ) )
21
26
import Language.Haskell.TH
22
- import Database.PostgreSQL.Query.TH.SqlExp
23
-
24
27
28
+ -- | Return constructor name
25
29
cName :: (Monad m ) => Con -> m Name
26
30
cName (NormalC n _) = return n
27
31
cName (RecC n _) = return n
28
32
cName _ = error " Constructor must be simple"
29
33
34
+ -- | Return count of constructor fields
30
35
cArgs :: (Monad m ) => Con -> m Int
31
36
cArgs (NormalC _ n) = return $ length n
32
37
cArgs (RecC _ n) = return $ length n
33
38
cArgs _ = error " Constructor must be simple"
34
39
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
+
35
44
-- | Derive 'FromRow' instance. i.e. you have type like that
36
45
--
37
46
-- @
@@ -114,6 +123,48 @@ deriveToRow t = do
114
123
(\ e -> AppE (VarE tof) (VarE e))
115
124
v
116
125
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
+
117
168
-- embed sql file as value
118
169
embedSql :: String -- ^ File path
119
170
-> Q Exp
0 commit comments