@@ -27,49 +27,50 @@ newtype FunctionName = FunctionName { unFunctionName :: [Text] }
2727
2828makePrisms ''FunctionName
2929
30−newtype ForeignType = ForeignType { unForeignType :: Text }
31− deriving (Show, Eq, IsString, Monoid)
32−
33−makePrisms ''ForeignType
34−
3530newtype PathSegment = PathSegment { unPathSegment :: Text }
3631 deriving (Show, Eq, IsString, Monoid)
3732
3833makePrisms ''PathSegment
3934
40−data Arg = Arg
41− { _aName :: PathSegment
42− , _aType :: ForeignType }
43− deriving (Show, Eq)
35+data Arg f = Arg
36+ { _argName :: PathSegment
37+ , _argType :: f }
38+
39+deriving instance Eq f => Eq (Arg f)
40+deriving instance Show f => Show (Arg f)
4441
4542makeLenses ''Arg
4643
47−aPath :: Getter Arg Text
48−aPath = aName . _PathSegment
44+argPath :: Getter (Arg f) Text
45+argPath = argName . _PathSegment
4946
50−data SegmentType
47+data SegmentType f
5148 = Static PathSegment
5249 -- ^ a static path segment. like "/foo"
53− | Cap Arg
50+ | Cap (Arg f)
5451 -- ^ a capture. like "/:userid"
55− deriving (Show, Eq)
52+
53+deriving instance Eq f => Eq (SegmentType f)
54+deriving instance Show f => Show (SegmentType f)
5655
5756makePrisms ''SegmentType
5857
59−newtype Segment = Segment { unSegment :: SegmentType }
60− deriving (Eq, Show)
58+newtype Segment f = Segment { unSegment :: SegmentType f }
59+
60+deriving instance Eq f => Eq (Segment f)
61+deriving instance Show f => Show (Segment f)
6162
6263makePrisms ''Segment
6364
64−isCapture :: Segment -> Bool
65+isCapture :: Segment f -> Bool
6566isCapture (Segment (Cap _)) = True
6667isCapture _ = False
6768
68−captureArg :: Segment -> Arg
69+captureArg :: Segment f -> Arg f
6970captureArg (Segment (Cap s)) = s
7071captureArg _ = error "captureArg called on non capture"
7172
72−type Path = [Segment]
73+type Path f = [Segment f]
7374
7475data ArgType
7576 = Normal
@@ -79,47 +80,59 @@ data ArgType
7980
8081makePrisms ''ArgType
8182
82−data QueryArg = QueryArg
83− { _argName :: Arg
84− , _argType :: ArgType
85− } deriving (Eq, Show)
83+data QueryArg f = QueryArg
84+ { _queryArgName :: Arg f
85+ , _queryArgType :: ArgType
86+ }
87+
88+deriving instance Eq f => Eq (QueryArg f)
89+deriving instance Show f => Show (QueryArg f)
8690
8791makeLenses ''QueryArg
8892
89−data HeaderArg = HeaderArg
90− { _headerArg :: Arg }
93+data HeaderArg f = HeaderArg
94+ { _headerArg :: Arg f }
9195 | ReplaceHeaderArg
92− { _headerArg :: Arg
96+ { _headerArg :: Arg f
9397 , _headerPattern :: Text
94− } deriving (Eq, Show)
98+ }
99+
100+deriving instance Eq f => Eq (HeaderArg f)
101+deriving instance Show f => Show (HeaderArg f)
95102
96103makeLenses ''HeaderArg
97104
98105makePrisms ''HeaderArg
99106
100−data Url = Url
101− { _path :: Path
102− , _queryStr :: [QueryArg]
103− } deriving (Eq, Show)
107+data Url f = Url
108+ { _path :: Path f
109+ , _queryStr :: [QueryArg f]
110+ }
111+
112+deriving instance Eq f => Eq (Url f)
113+deriving instance Show f => Show (Url f)
104114
105−defUrl :: Url
115+defUrl :: Url f
106116defUrl = Url [] []
107117
108118makeLenses ''Url
109119
110−data Req = Req
111− { _reqUrl :: Url
120+data Req f = Req
121+ { _reqUrl :: Url f
112122 , _reqMethod :: HTTP.Method
113− , _reqHeaders :: [HeaderArg]
114− , _reqBody :: Maybe ForeignType
115− , _reqReturnType :: ForeignType
123+ , _reqHeaders :: [HeaderArg f]
124+ , _reqBody :: Maybe f
125+ , _reqReturnType :: Maybe f
116126 , _reqFuncName :: FunctionName
117− } deriving (Eq, Show)
127+ }
128+
129+deriving instance Eq f => Eq (Req f)
130+deriving instance Show f => Show (Req f)
118131
119132makeLenses ''Req
120133
121−defReq :: Req
122−defReq = Req defUrl "GET" [] Nothing (ForeignType "") (FunctionName [])
134+defReq :: Req ftype
135+defReq = Req defUrl "GET" [] Nothing Nothing (FunctionName [])
123136
124137-- | To be used exclusively as a "negative" return type/constraint
125138-- by @'Elem`@ type family.
@@ -132,158 +145,158 @@ type family Elem (a :: *) (ls::[*]) :: Constraint where
132145
133146-- | 'HasForeignType' maps Haskell types with types in the target
134147-- language of your backend. For example, let's say you're
135−-- implementing a backend to some language __X__:
148+-- implementing a backend to some language __X__, and you want
149+-- a Text representation of each input/output type mentioned in the API:
136150--
137151-- > -- First you need to create a dummy type to parametrize your
138152-- > -- instances.
139153-- > data LangX
140154-- >
141155-- > -- Otherwise you define instances for the types you need
142−-- > instance HasForeignType LangX Int where
143−-- > typeFor _ _ = "intX"
156+-- > instance HasForeignType LangX Text Int where
157+-- > typeFor _ _ _ = "intX"
144158-- >
145159-- > -- Or for example in case of lists
146−-- > instance HasForeignType LangX a => HasForeignType LangX [a] where
147−-- > typeFor lang _ = "listX of " <> typeFor lang (Proxy :: Proxy a)
160+-- > instance HasForeignType LangX Text a => HasForeignType LangX Text [a] where
161+-- > typeFor lang type _ = "listX of " <> typeFor lang ftype (Proxy :: Proxy a)
148162--
149163-- Finally to generate list of information about all the endpoints for
150164-- an API you create a function of a form:
151165--
152−-- > getEndpoints :: (HasForeign LangX api, GenerateList (Foreign api))
153−-- > => Proxy api -> [Req]
154−-- > getEndpoints api = listFromAPI (Proxy :: Proxy LangX) api
166+-- > getEndpoints :: (HasForeign LangX Text api, GenerateList Text (Foreign Text api))
167+-- > => Proxy api -> [Req Text]
168+-- > getEndpoints api = listFromAPI (Proxy :: Proxy LangX) (Proxy :: Proxy Text) api
155169--
156170-- > -- If language __X__ is dynamically typed then you can use
157−-- > -- a predefined NoTypes parameter
158−-- > getEndpoints :: (HasForeign NoTypes api, GenerateList (Foreign api))
159−-- > => Proxy api -> [Req]
160−-- > getEndpoints api = listFromAPI (Proxy :: Proxy NoTypes) api
171+-- > -- a predefined NoTypes parameter with the () output type:
172+--
173+-- > getEndpoints :: (HasForeign NoTypes () api, GenerateList Text (Foreign () api))
174+-- > => Proxy api -> [Req ()]
175+-- > getEndpoints api = listFromAPI (Proxy :: Proxy NoTypes) (Proxy :: Proxy ()) api
161176-- >
162177--
163−class HasForeignType lang a where
164− typeFor :: Proxy lang -> Proxy a -> ForeignType
178+class HasForeignType lang ftype a where
179+ typeFor :: Proxy lang -> Proxy ftype -> Proxy a -> ftype
165180
166181data NoTypes
167182
168−instance HasForeignType NoTypes ftype where
169− typeFor _ _ = ForeignType empty
170−
171−type HasNoForeignType = HasForeignType NoTypes
183+instance HasForeignType NoTypes () ftype where
184+ typeFor _ _ _ = ()
172185
173−class HasForeign lang (layout :: *) where
174− type Foreign layout :: *
175− foreignFor :: Proxy lang -> Proxy layout -> Req -> Foreign layout
186+class HasForeign lang ftype (layout :: *) where
187+ type Foreign ftype layout :: *
188+ foreignFor :: Proxy lang -> Proxy ftype -> Proxy layout -> Req ftype -> Foreign ftype layout
176189
177−instance (HasForeign lang a, HasForeign lang b)
178− => HasForeign lang (a :<|> b) where
179− type Foreign (a :<|> b) = Foreign a :<|> Foreign b
190+instance (HasForeign lang ftype a, HasForeign lang ftype b)
191+ => HasForeign lang ftype (a :<|> b) where
192+ type Foreign ftype (a :<|> b) = Foreign ftype a :<|> Foreign ftype b
180193
181− foreignFor lang Proxy req =
182− foreignFor lang (Proxy :: Proxy a) req
183− :<|> foreignFor lang (Proxy :: Proxy b) req
194+ foreignFor lang ftype Proxy req =
195+ foreignFor lang ftype (Proxy :: Proxy a) req
196+ :<|> foreignFor lang ftype (Proxy :: Proxy b) req
184197
185−instance (KnownSymbol sym, HasForeignType lang ftype, HasForeign lang sublayout)
186− => HasForeign lang (Capture sym ftype :> sublayout) where
187− type Foreign (Capture sym a :> sublayout) = Foreign sublayout
198+instance (KnownSymbol sym, HasForeignType lang ftype t, HasForeign lang ftype sublayout)
199+ => HasForeign lang ftype (Capture sym t :> sublayout) where
200+ type Foreign ftype (Capture sym a :> sublayout) = Foreign ftype sublayout
188201
189− foreignFor lang Proxy req =
190− foreignFor lang (Proxy :: Proxy sublayout) $
202+ foreignFor lang Proxy Proxy req =
203+ foreignFor lang Proxy (Proxy :: Proxy sublayout) $
191204 req & reqUrl . path <>~ [Segment (Cap arg)]
192205 & reqFuncName . _FunctionName %~ (++ ["by", str])
193206 where
194207 str = pack . symbolVal $ (Proxy :: Proxy sym)
195− ftype = typeFor lang (Proxy :: Proxy ftype)
208+ ftype = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy t)
196209 arg = Arg
197− { _aName = PathSegment str
198− , _aType = ftype }
210+ { _argName = PathSegment str
211+ , _argType = ftype }
199212
200−instance (Elem JSON list, HasForeignType lang a, ReflectMethod method)
201− => HasForeign lang (Verb method status list a) where
202− type Foreign (Verb method status list a) = Req
213+instance (Elem JSON list, HasForeignType lang ftype a, ReflectMethod method)
214+ => HasForeign lang ftype (Verb method status list a) where
215+ type Foreign ftype (Verb method status list a) = Req ftype
203216
204− foreignFor lang Proxy req =
217+ foreignFor lang Proxy Proxy req =
205218 req & reqFuncName . _FunctionName %~ (methodLC :)
206219 & reqMethod .~ method
207− & reqReturnType .~ retType
220+ & reqReturnType .~ Just retType
208221 where
209− retType = typeFor lang (Proxy :: Proxy a)
222+ retType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy a)
210223 method = reflectMethod (Proxy :: Proxy method)
211224 methodLC = toLower $ decodeUtf8 method
212225
213−instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
214− => HasForeign lang (Header sym a :> sublayout) where
215− type Foreign (Header sym a :> sublayout) = Foreign sublayout
226+instance (KnownSymbol sym, HasForeignType lang ftype a, HasForeign lang ftype sublayout)
227+ => HasForeign lang ftype (Header sym a :> sublayout) where
228+ type Foreign ftype (Header sym a :> sublayout) = Foreign ftype sublayout
216229
217− foreignFor lang Proxy req =
218− foreignFor lang subP $ req & reqHeaders <>~ [HeaderArg arg]
230+ foreignFor lang Proxy Proxy req =
231+ foreignFor lang Proxy subP $ req & reqHeaders <>~ [HeaderArg arg]
219232 where
220233 hname = pack . symbolVal $ (Proxy :: Proxy sym)
221234 arg = Arg
222− { _aName = PathSegment hname
223− , _aType = typeFor lang (Proxy :: Proxy a) }
235+ { _argName = PathSegment hname
236+ , _argType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy a) }
224237 subP = Proxy :: Proxy sublayout
225238
226−instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
227− => HasForeign lang (QueryParam sym a :> sublayout) where
228− type Foreign (QueryParam sym a :> sublayout) = Foreign sublayout
239+instance (KnownSymbol sym, HasForeignType lang ftype a, HasForeign lang ftype sublayout)
240+ => HasForeign lang ftype (QueryParam sym a :> sublayout) where
241+ type Foreign ftype (QueryParam sym a :> sublayout) = Foreign ftype sublayout
229242
230− foreignFor lang Proxy req =
231− foreignFor lang (Proxy :: Proxy sublayout) $
243+ foreignFor lang Proxy Proxy req =
244+ foreignFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy sublayout) $
232245 req & reqUrl.queryStr <>~ [QueryArg arg Normal]
233246 where
234247 str = pack . symbolVal $ (Proxy :: Proxy sym)
235248 arg = Arg
236− { _aName = PathSegment str
237− , _aType = typeFor lang (Proxy :: Proxy a) }
249+ { _argName = PathSegment str
250+ , _argType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy a) }
238251
239252instance
240− (KnownSymbol sym, HasForeignType lang [a], HasForeign lang sublayout)
241− => HasForeign lang (QueryParams sym a :> sublayout) where
242− type Foreign (QueryParams sym a :> sublayout) = Foreign sublayout
243− foreignFor lang Proxy req =
244− foreignFor lang (Proxy :: Proxy sublayout) $
253+ (KnownSymbol sym, HasForeignType lang ftype [a], HasForeign lang ftype sublayout)
254+ => HasForeign lang ftype (QueryParams sym a :> sublayout) where
255+ type Foreign ftype (QueryParams sym a :> sublayout) = Foreign ftype sublayout
256+ foreignFor lang Proxy Proxy req =
257+ foreignFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy sublayout) $
245258 req & reqUrl.queryStr <>~ [QueryArg arg List]
246259 where
247260 str = pack . symbolVal $ (Proxy :: Proxy sym)
248261 arg = Arg
249− { _aName = PathSegment str
250− , _aType = typeFor lang (Proxy :: Proxy [a]) }
262+ { _argName = PathSegment str
263+ , _argType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy [a]) }
251264
252265instance
253− (KnownSymbol sym, HasForeignType lang Bool, HasForeign lang sublayout)
254− => HasForeign lang (QueryFlag sym :> sublayout) where
255− type Foreign (QueryFlag sym :> sublayout) = Foreign sublayout
266+ (KnownSymbol sym, HasForeignType lang ftype Bool, HasForeign lang ftype sublayout)
267+ => HasForeign lang ftype (QueryFlag sym :> sublayout) where
268+ type Foreign ftype (QueryFlag sym :> sublayout) = Foreign ftype sublayout
256269
257− foreignFor lang Proxy req =
258− foreignFor lang (Proxy :: Proxy sublayout) $
270+ foreignFor lang ftype Proxy req =
271+ foreignFor lang ftype (Proxy :: Proxy sublayout) $
259272 req & reqUrl.queryStr <>~ [QueryArg arg Flag]
260273 where
261274 str = pack . symbolVal $ (Proxy :: Proxy sym)
262275 arg = Arg
263− { _aName = PathSegment str
264− , _aType = typeFor lang (Proxy :: Proxy Bool) }
276+ { _argName = PathSegment str
277+ , _argType = typeFor lang ftype (Proxy :: Proxy Bool) }
265278
266−instance HasForeign lang Raw where
267− type Foreign Raw = HTTP.Method -> Req
279+instance HasForeign lang ftype Raw where
280+ type Foreign ftype Raw = HTTP.Method -> Req ftype
268281
269− foreignFor _ Proxy req method =
282+ foreignFor _ Proxy Proxy req method =
270283 req & reqFuncName . _FunctionName %~ ((toLower $ decodeUtf8 method) :)
271284 & reqMethod .~ method
272285
273−instance (Elem JSON list, HasForeignType lang a, HasForeign lang sublayout)
274− => HasForeign lang (ReqBody list a :> sublayout) where
275− type Foreign (ReqBody list a :> sublayout) = Foreign sublayout
286+instance (Elem JSON list, HasForeignType lang ftype a, HasForeign lang ftype sublayout)
287+ => HasForeign lang ftype (ReqBody list a :> sublayout) where
288+ type Foreign ftype (ReqBody list a :> sublayout) = Foreign ftype sublayout
276289
277− foreignFor lang Proxy req =
278− foreignFor lang (Proxy :: Proxy sublayout) $
279− req & reqBody .~ (Just $ typeFor lang (Proxy :: Proxy a))
290+ foreignFor lang ftype Proxy req =
291+ foreignFor lang ftype (Proxy :: Proxy sublayout) $
292+ req & reqBody .~ (Just $ typeFor lang ftype (Proxy :: Proxy a))
280293
281−instance (KnownSymbol path, HasForeign lang sublayout)
282− => HasForeign lang (path :> sublayout) where
283− type Foreign (path :> sublayout) = Foreign sublayout
294+instance (KnownSymbol path, HasForeign lang ftype sublayout)
295+ => HasForeign lang ftype (path :> sublayout) where
296+ type Foreign ftype (path :> sublayout) = Foreign ftype sublayout
284297
285− foreignFor lang Proxy req =
286− foreignFor lang (Proxy :: Proxy sublayout) $
298+ foreignFor lang ftype Proxy req =
299+ foreignFor lang ftype (Proxy :: Proxy sublayout) $
287300 req & reqUrl . path <>~ [Segment (Static (PathSegment str))]
288301 & reqFuncName . _FunctionName %~ (++ [str])
289302 where
@@ -291,58 +304,59 @@ instance (KnownSymbol path, HasForeign lang sublayout)
291304 Data.Text.map (\c -> if c == '.' then '_' else c)
292305 . pack . symbolVal $ (Proxy :: Proxy path)
293306
294−instance HasForeign lang sublayout
295− => HasForeign lang (RemoteHost :> sublayout) where
296− type Foreign (RemoteHost :> sublayout) = Foreign sublayout
307+instance HasForeign lang ftype sublayout
308+ => HasForeign lang ftype (RemoteHost :> sublayout) where
309+ type Foreign ftype (RemoteHost :> sublayout) = Foreign ftype sublayout
297310
298− foreignFor lang Proxy req =
299− foreignFor lang (Proxy :: Proxy sublayout) req
311+ foreignFor lang ftype Proxy req =
312+ foreignFor lang ftype (Proxy :: Proxy sublayout) req
300313
301−instance HasForeign lang sublayout
302− => HasForeign lang (IsSecure :> sublayout) where
303− type Foreign (IsSecure :> sublayout) = Foreign sublayout
314+instance HasForeign lang ftype sublayout
315+ => HasForeign lang ftype (IsSecure :> sublayout) where
316+ type Foreign ftype (IsSecure :> sublayout) = Foreign ftype sublayout
304317
305− foreignFor lang Proxy req =
306− foreignFor lang (Proxy :: Proxy sublayout) req
318+ foreignFor lang ftype Proxy req =
319+ foreignFor lang ftype (Proxy :: Proxy sublayout) req
307320
308−instance HasForeign lang sublayout => HasForeign lang (Vault :> sublayout) where
309− type Foreign (Vault :> sublayout) = Foreign sublayout
321+instance HasForeign lang ftype sublayout => HasForeign lang ftype (Vault :> sublayout) where
322+ type Foreign ftype (Vault :> sublayout) = Foreign ftype sublayout
310323
311− foreignFor lang Proxy req =
312− foreignFor lang (Proxy :: Proxy sublayout) req
324+ foreignFor lang ftype Proxy req =
325+ foreignFor lang ftype (Proxy :: Proxy sublayout) req
313326
314−instance HasForeign lang sublayout =>
315− HasForeign lang (WithNamedContext name context sublayout) where
327+instance HasForeign lang ftype sublayout =>
328+ HasForeign lang ftype (WithNamedContext name context sublayout) where
316329
317− type Foreign (WithNamedContext name context sublayout) = Foreign sublayout
330+ type Foreign ftype (WithNamedContext name context sublayout) = Foreign ftype sublayout
318331
319− foreignFor lang Proxy = foreignFor lang (Proxy :: Proxy sublayout)
332+ foreignFor lang ftype Proxy = foreignFor lang ftype (Proxy :: Proxy sublayout)
320333
321−instance HasForeign lang sublayout
322− => HasForeign lang (HttpVersion :> sublayout) where
323− type Foreign (HttpVersion :> sublayout) = Foreign sublayout
334+instance HasForeign lang ftype sublayout
335+ => HasForeign lang ftype (HttpVersion :> sublayout) where
336+ type Foreign ftype (HttpVersion :> sublayout) = Foreign ftype sublayout
324337
325− foreignFor lang Proxy req =
326− foreignFor lang (Proxy :: Proxy sublayout) req
338+ foreignFor lang ftype Proxy req =
339+ foreignFor lang ftype (Proxy :: Proxy sublayout) req
327340
328341-- | Utility class used by 'listFromAPI' which computes
329342-- the data needed to generate a function for each endpoint
330343-- and hands it all back in a list.
331−class GenerateList reqs where
332− generateList :: reqs -> [Req]
344+class GenerateList ftype reqs where
345+ generateList :: reqs -> [Req ftype]
333346
334−instance GenerateList Req where
347+instance GenerateList ftype (Req ftype) where
335348 generateList r = [r]
336349
337−instance (GenerateList start, GenerateList rest)
338− => GenerateList (start :<|> rest) where
350+instance (GenerateList ftype start, GenerateList ftype rest)
351+ => GenerateList ftype (start :<|> rest) where
339352 generateList (start :<|> rest) = (generateList start) ++ (generateList rest)
340353
341354-- | Generate the necessary data for codegen as a list, each 'Req'
342355-- describing one endpoint from your API type.
343356listFromAPI
344− :: (HasForeign lang api, GenerateList (Foreign api))
357+ :: (HasForeign lang ftype api, GenerateList ftype (Foreign ftype api))
345358 => Proxy lang
359+ -> Proxy ftype
346360 -> Proxy api
347− -> [Req]
348−listFromAPI lang p = generateList (foreignFor lang p defReq)
361+ -> [Req ftype]
362+listFromAPI lang ftype p = generateList (foreignFor lang ftype p defReq)