19
19
-- arbitrary programming languages.
20
20
module Servant.Foreign.Internal where
21
21
22
- import Control.Lens (makeLenses , (%~) , (&) , (.~) , (<>~) )
23
- import qualified Data.Char as C
22
+ import Control.Lens (makeLenses , makePrisms , (%~) , (&) , (.~) , (<>~) )
23
+ import qualified Data.Char as C
24
24
import Data.Proxy
25
25
import Data.Text
26
- import Data.Text.Encoding (decodeUtf8 )
27
- import GHC.Exts (Constraint )
26
+ import Data.Text.Encoding (decodeUtf8 )
27
+ import GHC.Exts (Constraint )
28
28
import GHC.TypeLits
29
- import qualified Network.HTTP.Types as HTTP
30
- import Prelude hiding (concat )
29
+ import qualified Network.HTTP.Types as HTTP
30
+ import Prelude hiding (concat )
31
31
import Servant.API
32
32
33
+ type FunctionName = [Text ]
34
+
33
35
-- | Function name builder that simply concat each part together
34
36
concatCase :: FunctionName -> Text
35
37
concatCase = concat
@@ -49,36 +51,50 @@ camelCase = camelCase' . Prelude.map (replace "-" "")
49
51
capitalize name = C. toUpper (Data.Text. head name) `cons` Data.Text. tail name
50
52
51
53
type ForeignType = Text
54
+
52
55
type Arg = (Text , ForeignType )
53
56
54
- newtype Segment = Segment { _segment :: SegmentType }
57
+ data SegmentType
58
+ = Static Text
59
+ -- ^ a static path segment. like "/foo"
60
+ | Cap Arg
61
+ -- ^ a capture. like "/:userid"
55
62
deriving (Eq , Show )
56
63
57
- data SegmentType = Static Text -- ^ a static path segment. like "/foo"
58
- | Cap Arg -- ^ a capture. like "/:userid"
64
+ makePrisms ''SegmentType
65
+
66
+ newtype Segment = Segment { unSegment :: SegmentType }
59
67
deriving (Eq , Show )
60
68
69
+ makePrisms ''Segment
70
+
61
71
type Path = [Segment ]
62
72
63
- data ArgType =
64
- Normal
73
+ data ArgType
74
+ = Normal
65
75
| Flag
66
76
| List
67
77
deriving (Eq , Show )
68
78
79
+ makePrisms ''ArgType
80
+
69
81
data QueryArg = QueryArg
70
82
{ _argName :: Arg
71
83
, _argType :: ArgType
72
84
} deriving (Eq , Show )
73
85
86
+ makeLenses ''QueryArg
87
+
74
88
data HeaderArg = HeaderArg
75
- { headerArg :: Arg
76
- }
89
+ { headerArg :: Arg }
77
90
| ReplaceHeaderArg
78
- { headerArg :: Arg
79
- , headerPattern :: Text
80
- } deriving (Eq , Show )
91
+ { headerArg :: Arg
92
+ , headerPattern :: Text
93
+ } deriving (Eq , Show )
81
94
95
+ makeLenses ''HeaderArg
96
+
97
+ makePrisms ''HeaderArg
82
98
83
99
data Url = Url
84
100
{ _path :: Path
@@ -88,20 +104,17 @@ data Url = Url
88
104
defUrl :: Url
89
105
defUrl = Url [] []
90
106
91
- type FunctionName = [ Text ]
107
+ makeLenses ''Url
92
108
93
109
data Req = Req
94
110
{ _reqUrl :: Url
95
111
, _reqMethod :: HTTP. Method
96
112
, _reqHeaders :: [HeaderArg ]
97
113
, _reqBody :: Maybe ForeignType
98
114
, _reqReturnType :: ForeignType
99
- , _funcName :: FunctionName
115
+ , _reqFuncName :: FunctionName
100
116
} deriving (Eq , Show )
101
117
102
- makeLenses ''QueryArg
103
- makeLenses ''Segment
104
- makeLenses ''Url
105
118
makeLenses ''Req
106
119
107
120
isCapture :: Segment -> Bool
@@ -155,105 +168,104 @@ type family Elem (a :: *) (ls::[*]) :: Constraint where
155
168
-- >
156
169
--
157
170
class HasForeignType lang a where
158
- typeFor :: Proxy lang -> Proxy a -> ForeignType
171
+ typeFor :: Proxy lang -> Proxy a -> ForeignType
159
172
160
173
data NoTypes
161
174
162
- instance HasForeignType NoTypes a where
163
- typeFor _ _ = empty
175
+ instance HasForeignType NoTypes ftype where
176
+ typeFor _ _ = empty
177
+
178
+ type HasNoForeignType = HasForeignType NoTypes
164
179
165
180
class HasForeign lang (layout :: * ) where
166
181
type Foreign layout :: *
167
182
foreignFor :: Proxy lang -> Proxy layout -> Req -> Foreign layout
168
183
169
184
instance (HasForeign lang a , HasForeign lang b )
170
- => HasForeign lang (a :<|> b ) where
185
+ => HasForeign lang (a :<|> b ) where
171
186
type Foreign (a :<|> b ) = Foreign a :<|> Foreign b
172
187
173
188
foreignFor lang Proxy req =
174
189
foreignFor lang (Proxy :: Proxy a ) req
175
190
:<|> foreignFor lang (Proxy :: Proxy b ) req
176
191
177
- instance (KnownSymbol sym , HasForeignType lang a , HasForeign lang sublayout )
178
- => HasForeign lang (Capture sym a :> sublayout ) where
192
+ instance (KnownSymbol sym , HasForeignType lang ftype , HasForeign lang sublayout )
193
+ => HasForeign lang (Capture sym ftype :> sublayout ) where
179
194
type Foreign (Capture sym a :> sublayout ) = Foreign sublayout
180
195
181
196
foreignFor lang Proxy req =
182
197
foreignFor lang (Proxy :: Proxy sublayout ) $
183
198
req & reqUrl. path <>~ [Segment (Cap arg)]
184
- & funcName %~ (++ [" by" , str])
185
-
199
+ & reqFuncName %~ (++ [" by" , str])
186
200
where
187
- str = pack . symbolVal $ (Proxy :: Proxy sym )
188
- arg = (str, typeFor lang (Proxy :: Proxy a ))
201
+ str = pack . symbolVal $ (Proxy :: Proxy sym )
202
+ arg = (str, typeFor lang (Proxy :: Proxy ftype ))
189
203
190
204
instance (Elem JSON list , HasForeignType lang a , ReflectMethod method )
191
- => HasForeign lang (Verb method status list a ) where
205
+ => HasForeign lang (Verb method status list a ) where
192
206
type Foreign (Verb method status list a ) = Req
193
207
194
208
foreignFor lang Proxy req =
195
- req & funcName %~ (methodLC : )
209
+ req & reqFuncName %~ (methodLC : )
196
210
& reqMethod .~ method
197
211
& reqReturnType .~ retType
198
212
where
199
- retType = typeFor lang (Proxy :: Proxy a )
200
- method = reflectMethod (Proxy :: Proxy method )
201
- methodLC = toLower $ decodeUtf8 method
213
+ retType = typeFor lang (Proxy :: Proxy a )
214
+ method = reflectMethod (Proxy :: Proxy method )
215
+ methodLC = toLower $ decodeUtf8 method
202
216
203
217
instance (KnownSymbol sym , HasForeignType lang a , HasForeign lang sublayout )
204
- => HasForeign lang (Header sym a :> sublayout ) where
218
+ => HasForeign lang (Header sym a :> sublayout ) where
205
219
type Foreign (Header sym a :> sublayout ) = Foreign sublayout
206
220
207
221
foreignFor lang Proxy req =
208
222
foreignFor lang subP $ req
209
223
& reqHeaders <>~ [HeaderArg arg]
210
-
211
224
where
212
- hname = pack . symbolVal $ (Proxy :: Proxy sym )
213
- arg = (hname, typeFor lang (Proxy :: Proxy a ))
214
- subP = Proxy :: Proxy sublayout
225
+ hname = pack . symbolVal $ (Proxy :: Proxy sym )
226
+ arg = (hname, typeFor lang (Proxy :: Proxy a ))
227
+ subP = Proxy :: Proxy sublayout
215
228
216
229
instance (KnownSymbol sym , HasForeignType lang a , HasForeign lang sublayout )
217
- => HasForeign lang (QueryParam sym a :> sublayout ) where
230
+ => HasForeign lang (QueryParam sym a :> sublayout ) where
218
231
type Foreign (QueryParam sym a :> sublayout ) = Foreign sublayout
219
232
220
233
foreignFor lang Proxy req =
221
234
foreignFor lang (Proxy :: Proxy sublayout ) $
222
235
req & reqUrl. queryStr <>~ [QueryArg arg Normal ]
223
236
224
237
where
225
- str = pack . symbolVal $ (Proxy :: Proxy sym )
226
- arg = (str, typeFor lang (Proxy :: Proxy a ))
238
+ str = pack . symbolVal $ (Proxy :: Proxy sym )
239
+ arg = (str, typeFor lang (Proxy :: Proxy a ))
227
240
228
- instance (KnownSymbol sym , HasForeignType lang [a ], HasForeign lang sublayout )
229
- => HasForeign lang (QueryParams sym a :> sublayout ) where
241
+ instance
242
+ (KnownSymbol sym , HasForeignType lang [a ], HasForeign lang sublayout )
243
+ => HasForeign lang (QueryParams sym a :> sublayout ) where
230
244
type Foreign (QueryParams sym a :> sublayout ) = Foreign sublayout
231
-
232
245
foreignFor lang Proxy req =
233
246
foreignFor lang (Proxy :: Proxy sublayout ) $
234
247
req & reqUrl. queryStr <>~ [QueryArg arg List ]
235
-
236
248
where
237
- str = pack . symbolVal $ (Proxy :: Proxy sym )
238
- arg = (str, typeFor lang (Proxy :: Proxy [a ]))
249
+ str = pack . symbolVal $ (Proxy :: Proxy sym )
250
+ arg = (str, typeFor lang (Proxy :: Proxy [a ]))
239
251
240
- instance (KnownSymbol sym , HasForeignType lang a , a ~ Bool , HasForeign lang sublayout )
241
- => HasForeign lang (QueryFlag sym :> sublayout ) where
252
+ instance
253
+ (KnownSymbol sym , HasForeignType lang Bool , HasForeign lang sublayout )
254
+ => HasForeign lang (QueryFlag sym :> sublayout ) where
242
255
type Foreign (QueryFlag sym :> sublayout ) = Foreign sublayout
243
256
244
257
foreignFor lang Proxy req =
245
258
foreignFor lang (Proxy :: Proxy sublayout ) $
246
259
req & reqUrl. queryStr <>~ [QueryArg arg Flag ]
247
-
248
260
where
249
- str = pack . symbolVal $ (Proxy :: Proxy sym )
250
- arg = (str, typeFor lang (Proxy :: Proxy a ))
261
+ str = pack . symbolVal $ (Proxy :: Proxy sym )
262
+ arg = (str, typeFor lang (Proxy :: Proxy Bool ))
251
263
252
264
instance HasForeign lang Raw where
253
265
type Foreign Raw = HTTP. Method -> Req
254
266
255
267
foreignFor _ Proxy req method =
256
- req & funcName %~ ((toLower $ decodeUtf8 method) : )
268
+ req & reqFuncName %~ ((toLower $ decodeUtf8 method) : )
257
269
& reqMethod .~ method
258
270
259
271
instance (Elem JSON list , HasForeignType lang a , HasForeign lang sublayout )
@@ -271,19 +283,21 @@ instance (KnownSymbol path, HasForeign lang sublayout)
271
283
foreignFor lang Proxy req =
272
284
foreignFor lang (Proxy :: Proxy sublayout ) $
273
285
req & reqUrl. path <>~ [Segment (Static str)]
274
- & funcName %~ (++ [str])
275
-
286
+ & reqFuncName %~ (++ [str])
276
287
where
277
- str = Data.Text. map (\ c -> if c == ' .' then ' _' else c)
278
- . pack . symbolVal $ (Proxy :: Proxy path )
288
+ str =
289
+ Data.Text. map (\ c -> if c == ' .' then ' _' else c)
290
+ . pack . symbolVal $ (Proxy :: Proxy path )
279
291
280
- instance HasForeign lang sublayout => HasForeign lang (RemoteHost :> sublayout ) where
292
+ instance HasForeign lang sublayout
293
+ => HasForeign lang (RemoteHost :> sublayout ) where
281
294
type Foreign (RemoteHost :> sublayout ) = Foreign sublayout
282
295
283
296
foreignFor lang Proxy req =
284
297
foreignFor lang (Proxy :: Proxy sublayout ) req
285
298
286
- instance HasForeign lang sublayout => HasForeign lang (IsSecure :> sublayout ) where
299
+ instance HasForeign lang sublayout
300
+ => HasForeign lang (IsSecure :> sublayout ) where
287
301
type Foreign (IsSecure :> sublayout ) = Foreign sublayout
288
302
289
303
foreignFor lang Proxy req =
@@ -302,7 +316,8 @@ instance HasForeign lang sublayout =>
302
316
303
317
foreignFor lang Proxy = foreignFor lang (Proxy :: Proxy sublayout )
304
318
305
- instance HasForeign lang sublayout => HasForeign lang (HttpVersion :> sublayout ) where
319
+ instance HasForeign lang sublayout
320
+ => HasForeign lang (HttpVersion :> sublayout ) where
306
321
type Foreign (HttpVersion :> sublayout ) = Foreign sublayout
307
322
308
323
foreignFor lang Proxy req =
@@ -317,10 +332,15 @@ class GenerateList reqs where
317
332
instance GenerateList Req where
318
333
generateList r = [r]
319
334
320
- instance (GenerateList start , GenerateList rest ) => GenerateList (start :<|> rest ) where
335
+ instance (GenerateList start , GenerateList rest )
336
+ => GenerateList (start :<|> rest ) where
321
337
generateList (start :<|> rest) = (generateList start) ++ (generateList rest)
322
338
323
339
-- | Generate the necessary data for codegen as a list, each 'Req'
324
340
-- describing one endpoint from your API type.
325
- listFromAPI :: (HasForeign lang api , GenerateList (Foreign api )) => Proxy lang -> Proxy api -> [Req ]
341
+ listFromAPI
342
+ :: (HasForeign lang api , GenerateList (Foreign api ))
343
+ => Proxy lang
344
+ -> Proxy api
345
+ -> [Req ]
326
346
listFromAPI lang p = generateList (foreignFor lang p defReq)
0 commit comments