|
1 | 1 | module Database.PostgreSQL.Query.TH
|
2 |
| - ( -- * Deriving instances |
3 |
| - deriveFromRow |
4 |
| - , deriveToRow |
5 |
| - , deriveEntity |
6 |
| - , deriveEverything |
7 |
| - , EntityOptions(..) |
8 |
| - -- * Embedding sql files |
9 |
| - , embedSql |
10 |
| - , sqlFile |
11 |
| - -- * Sql string interpolation |
12 |
| - , sqlExp |
13 |
| - , sqlExpEmbed |
14 |
| - , sqlExpFile |
15 |
| - ) where |
| 2 | + ( -- * Deriving instances |
| 3 | + deriveEverything |
| 4 | + |
| 5 | + , module Database.PostgreSQL.Query.TH.Entity |
| 6 | + , module Database.PostgreSQL.Query.TH.Enum |
| 7 | + , module Database.PostgreSQL.Query.TH.Row |
| 8 | + , module Database.PostgreSQL.Query.TH.SqlExp |
| 9 | + ) where |
16 | 10 |
|
17 | 11 | import Prelude
|
18 | 12 |
|
19 |
| -import Control.Applicative |
20 |
| -import Control.Monad |
21 |
| -import Data.Default |
22 |
| -import Data.FileEmbed ( embedFile ) |
23 |
| -import Data.String |
24 |
| -import Database.PostgreSQL.Query.Entity ( Entity(..) ) |
| 13 | +import Database.PostgreSQL.Query.TH.Entity |
| 14 | +import Database.PostgreSQL.Query.TH.Enum |
| 15 | +import Database.PostgreSQL.Query.TH.Row |
25 | 16 | import Database.PostgreSQL.Query.TH.SqlExp
|
26 |
| -import Database.PostgreSQL.Query.Types ( FN(..) ) |
27 |
| -import Database.PostgreSQL.Simple.FromRow ( FromRow(..), field ) |
28 |
| -import Database.PostgreSQL.Simple.ToRow ( ToRow(..) ) |
29 |
| -import Database.PostgreSQL.Simple.Types ( Query(..) ) |
30 | 17 | import Language.Haskell.TH
|
31 |
| -import Language.Haskell.TH.Syntax |
32 |
| - |
33 |
| --- | Return constructor name |
34 |
| -cName :: (Monad m) => Con -> m Name |
35 |
| -cName (NormalC n _) = return n |
36 |
| -cName (RecC n _) = return n |
37 |
| -cName _ = error "Constructor must be simple" |
38 |
| - |
39 |
| --- | Return count of constructor fields |
40 |
| -cArgs :: (Monad m) => Con -> m Int |
41 |
| -cArgs (NormalC _ n) = return $ length n |
42 |
| -cArgs (RecC _ n) = return $ length n |
43 |
| -cArgs _ = error "Constructor must be simple" |
44 |
| - |
45 |
| --- | Get field names from record constructor |
46 |
| -cFieldNames :: Con -> [Name] |
47 |
| -cFieldNames (RecC _ vst) = map (\(a, _, _) -> a) vst |
48 |
| -cFieldNames _ = error "Constructor must be a record (product type with field names)" |
49 |
| - |
50 |
| -{-| Derive 'FromRow' instance. i.e. you have type like that |
51 |
| -
|
52 |
| -@ |
53 |
| -data Entity = Entity |
54 |
| - { eField :: Text |
55 |
| - , eField2 :: Int |
56 |
| - , efield3 :: Bool } |
57 |
| -@ |
58 |
| -
|
59 |
| -then 'deriveFromRow' will generate this instance: |
60 |
| -instance FromRow Entity where |
61 |
| -
|
62 |
| -@ |
63 |
| -instance FromRow Entity where |
64 |
| - fromRow = Entity |
65 |
| - \<$> field |
66 |
| - \<*> field |
67 |
| - \<*> field |
68 |
| -@ |
69 |
| -
|
70 |
| -Datatype must have just one constructor with arbitrary count of fields |
71 |
| --} |
72 |
| - |
73 |
| -deriveFromRow :: Name -> Q [Dec] |
74 |
| -deriveFromRow t = do |
75 |
| - TyConI (DataD _ _ _ [con] _) <- reify t |
76 |
| - cname <- cName con |
77 |
| - cargs <- cArgs con |
78 |
| - [d|instance FromRow $(return $ ConT t) where |
79 |
| - fromRow = $(fieldsQ cname cargs)|] |
80 |
| - where |
81 |
| - fieldsQ cname cargs = do |
82 |
| - fld <- [| field |] |
83 |
| - fmp <- [| (<$>) |] |
84 |
| - fap <- [| (<*>) |] |
85 |
| - return $ UInfixE (ConE cname) fmp (fapChain cargs fld fap) |
86 |
| - |
87 |
| - fapChain 0 _ _ = error "there must be at least 1 field in constructor" |
88 |
| - fapChain 1 fld _ = fld |
89 |
| - fapChain n fld fap = UInfixE fld fap (fapChain (n-1) fld fap) |
90 |
| - |
91 |
| -lookupVNameErr :: String -> Q Name |
92 |
| -lookupVNameErr name = |
93 |
| - lookupValueName name >>= |
94 |
| - maybe (error $ "could not find identifier: " ++ name) |
95 |
| - return |
96 |
| - |
97 |
| - |
98 |
| -{-| derives 'ToRow' instance for datatype like |
99 |
| -
|
100 |
| -@ |
101 |
| -data Entity = Entity |
102 |
| - { eField :: Text |
103 |
| - , eField2 :: Int |
104 |
| - , efield3 :: Bool } |
105 |
| -@ |
106 |
| -
|
107 |
| -it will derive instance like that: |
108 |
| -
|
109 |
| -@ |
110 |
| -instance ToRow Entity where |
111 |
| - toRow (Entity e1 e2 e3) = |
112 |
| - [ toField e1 |
113 |
| - , toField e2 |
114 |
| - , toField e3 ] |
115 |
| -@ |
116 |
| --} |
117 |
| - |
118 |
| -deriveToRow :: Name -> Q [Dec] |
119 |
| -deriveToRow t = do |
120 |
| - TyConI (DataD _ _ _ [con] _) <- reify t |
121 |
| - cname <- cName con |
122 |
| - cargs <- cArgs con |
123 |
| - cvars <- sequence |
124 |
| - $ replicate cargs |
125 |
| - $ newName "a" |
126 |
| - [d|instance ToRow $(return $ ConT t) where |
127 |
| - toRow $(return $ ConP cname $ map VarP cvars) = $(toFields cvars)|] |
128 |
| - where |
129 |
| - toFields v = do |
130 |
| - tof <- lookupVNameErr "toField" |
131 |
| - return $ ListE |
132 |
| - $ map |
133 |
| - (\e -> AppE (VarE tof) (VarE e)) |
134 |
| - v |
135 |
| - |
136 |
| --- | Options for deriving `Entity` |
137 |
| -data EntityOptions = EntityOptions |
138 |
| - { eoTableName :: String -> String -- ^ Type name to table name converter |
139 |
| - , eoColumnNames :: String -> String -- ^ Record field to column name converter |
140 |
| - , eoDeriveClasses :: [Name] -- ^ Typeclasses to derive for Id |
141 |
| - , eoIdType :: Name -- ^ Base type for Id |
142 |
| - } |
143 |
| - |
144 |
| -instance Default EntityOptions where |
145 |
| - def = EntityOptions |
146 |
| - { eoTableName = id |
147 |
| - , eoColumnNames = id |
148 |
| - , eoDeriveClasses = [''Ord, ''Eq, ''Show] |
149 |
| - , eoIdType = ''Integer |
150 |
| - } |
151 |
| - |
152 |
| -{- | Derives instance for 'Entity' using type name and field names. Also |
153 |
| -generates type synonim for ID. E.g. code like this: |
154 |
| -
|
155 |
| -@ |
156 |
| -data Agent = Agent |
157 |
| - { aName :: !Text |
158 |
| - , aAttributes :: !HStoreMap |
159 |
| - , aLongWeirdName :: !Int |
160 |
| - } deriving (Ord, Eq, Show) |
161 |
| -
|
162 |
| -$(deriveEntity |
163 |
| - def { eoIdType = ''Id |
164 |
| - , eoTableName = toUnderscore |
165 |
| - , eoColumnNames = toUnderscore . drop 1 |
166 |
| - , eoDeriveClasses = |
167 |
| - [''Show, ''Read, ''Ord, ''Eq |
168 |
| - , ''FromField, ''ToField, ''PathPiece] |
169 |
| - } |
170 |
| - ''Agent ) |
171 |
| -@ |
172 |
| -
|
173 |
| -Will generate code like this: |
174 |
| -
|
175 |
| -@ |
176 |
| -instance Database.PostgreSQL.Query.Entity Agent where |
177 |
| - newtype EntityId Agent |
178 |
| - = AgentId {getAgentId :: Id} |
179 |
| - deriving (Show, Read, Ord, Eq, FromField, ToField, PathPiece) |
180 |
| - tableName _ = "agent" |
181 |
| - fieldNames _ = ["name", "attributes", "long_weird_name"] |
182 |
| -type AgentId = EntityId Agent |
183 |
| -@ |
184 |
| -
|
185 |
| -So, you dont need to write it by hands any more. |
186 |
| -
|
187 |
| -NOTE: 'toUnderscore' is from package 'inflections' here |
188 |
| --} |
189 |
| - |
190 |
| -deriveEntity :: EntityOptions -> Name -> Q [Dec] |
191 |
| -deriveEntity opts tname = do |
192 |
| - TyConI (DataD _ _ _ [tcon] _) <- reify tname |
193 |
| - econt <- [t|Entity $(conT tname)|] |
194 |
| - ConT entityIdName <- [t|EntityId|] |
195 |
| - let tnames = nameBase tname |
196 |
| - idname = tnames ++ "Id" |
197 |
| - unidname = "get" ++ idname |
198 |
| - idtype = ConT (eoIdType opts) |
199 |
| - idcon = RecC (mkName idname) |
200 |
| - [(mkName unidname, NotStrict, idtype)] |
201 |
| - iddec = NewtypeInstD [] entityIdName [ConT tname] |
202 |
| - idcon (eoDeriveClasses opts) |
203 |
| - tblName = fromString $ eoTableName opts tnames |
204 |
| - fldNames = map (fromString . eoColumnNames opts . nameBase) |
205 |
| - $ cFieldNames tcon |
206 |
| - VarE ntableName <- [e|tableName|] |
207 |
| - VarE nfieldNames <- [e|fieldNames|] |
208 |
| - tblExp <- lift (tblName :: FN) |
209 |
| - fldExp <- mapM lift (fldNames :: [FN]) |
210 |
| - let tbldec = FunD ntableName [Clause [WildP] (NormalB tblExp) []] |
211 |
| - flddec = FunD nfieldNames [Clause [WildP] (NormalB $ ListE fldExp) []] |
212 |
| - ret = InstanceD [] econt |
213 |
| - [ iddec, tbldec, flddec ] |
214 |
| - syndec = TySynD (mkName idname) [] (AppT (ConT entityIdName) (ConT tname)) |
215 |
| - return [ret, syndec] |
216 | 18 |
|
217 | 19 | {- | Calls sequently `deriveFromRow` `deriveToRow` `deriveEntity`. E.g. code like this:
|
218 | 20 |
|
@@ -261,18 +63,3 @@ deriveEverything opts tname = fmap concat $ sequence
|
261 | 63 | [ deriveToRow tname
|
262 | 64 | , deriveFromRow tname
|
263 | 65 | , deriveEntity opts tname ]
|
264 |
| - |
265 |
| --- embed sql file as value |
266 |
| -embedSql :: String -- ^ File path |
267 |
| - -> Q Exp |
268 |
| -embedSql path = do |
269 |
| - [e| (Query ( $(embedFile path) )) |] |
270 |
| -{-# DEPRECATED embedSql "use 'sqlExpEmbed' instead" #-} |
271 |
| - |
272 |
| --- embed sql file by pattern. __sqlFile "dir/file"__ is just the same as |
273 |
| --- __embedSql "sql/dir/file.sql"__ |
274 |
| -sqlFile :: String -- ^ sql file pattern |
275 |
| - -> Q Exp |
276 |
| -sqlFile s = do |
277 |
| - embedSql $ "sql/" ++ s ++ ".sql" |
278 |
| -{-# DEPRECATED sqlFile "use 'sqlExpFile' instead" #-} |
0 commit comments