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