@@ -1,27 +1,18 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ConstraintKinds #-}
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE FlexibleContexts #-}
5−{-# LANGUAGE FlexibleInstances #-}
6#if !MIN_VERSION_base(4,8,0)
2#if !MIN_VERSION_base(4,8,0)
7−{-# LANGUAGE NullaryTypeClasses #-}
3+{-# LANGUAGE NullaryTypeClasses #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TemplateHaskell #-}
12−{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE TypeOperators #-}
14−{-# LANGUAGE UndecidableInstances #-}
15−{-# LANGUAGE OverloadedStrings #-}
16−{-# LANGUAGE PolyKinds #-}
18-- | Generalizes all the data needed to make code generation work with
6-- | Generalizes all the data needed to make code generation work with
19-- arbitrary programming languages.
7-- arbitrary programming languages.
20module Servant.Foreign.Internal where
8module Servant.Foreign.Internal where
22−import Control.Lens (makeLenses, makePrisms, (%~), (&), (.~), (<>~))
10+import Control.Lens hiding (cons, List)
11+#if !MIN_VERSION_base(4,8,0)
23−import qualified Data.Char as C
12+import Data.Monoid
24import Data.Proxy
14import Data.Proxy
25import Data.Text
16import Data.Text
26import Data.Text.Encoding (decodeUtf8)
17import Data.Text.Encoding (decodeUtf8)
27import GHC.Exts (Constraint)
18import GHC.Exts (Constraint)
@@ -30,36 +21,38 @@ import qualified Network.HTTP.Types as HTTP
30import Prelude hiding (concat)
21import Prelude hiding (concat)
31import Servant.API
22import Servant.API
33−type FunctionName = [Text]
35−-- | Function name builder that simply concat each part together
25+newtype FunctionName = FunctionName { unFunctionName :: [Text] }
26+ deriving (Show, Eq, Monoid)
36−concatCase :: FunctionName -> Text
28+makePrisms ''FunctionName
37−concatCase = concat
29+
30+newtype ForeignType = ForeignType { unForeignType :: Text }
31+ deriving (Show, Eq, IsString, Monoid)
39−-- | Function name builder using the snake_case convention.
33+makePrisms ''ForeignType
40−-- each part is separated by a single underscore character.
41−snakeCase :: FunctionName -> Text
42−snakeCase = intercalate "_"
44−-- | Function name builder using the CamelCase convention.
35+newtype PathSegment = PathSegment { unPathSegment :: Text }
45−-- each part begins with an upper case character.
36+ deriving (Show, Eq, IsString, Monoid)
46−camelCase :: FunctionName -> Text
47−camelCase = camelCase' . Prelude.map (replace "-" "")
48− where camelCase' [] = ""
49− camelCase' (p:ps) = concat $ p : Prelude.map capitalize ps
51− capitalize name = C.toUpper (Data.Text.head name) `cons` Data.Text.tail name
53−type ForeignType = Text
38+makePrisms ''PathSegment
55−type Arg = (Text, ForeignType)
40+data Arg = Arg
41+ { _aName :: PathSegment
42+ , _aType :: ForeignType }
47+aPath :: Getter Arg Text
48+aPath = aName . _PathSegment
57data SegmentType
50data SegmentType
58− = Static Text
51+ = Static PathSegment
59 -- ^ a static path segment. like "/foo"
52 -- ^ a static path segment. like "/foo"
61 -- ^ a capture. like "/:userid"
54 -- ^ a capture. like "/:userid"
62− deriving (Eq, Show)
55+ deriving (Show, Eq)
64makePrisms ''SegmentType
57makePrisms ''SegmentType
@@ -68,6 +61,14 @@ newtype Segment = Segment { unSegment :: SegmentType }
69makePrisms ''Segment
62makePrisms ''Segment
64+isCapture :: Segment -> Bool
65+isCapture (Segment (Cap _)) = True
68+captureArg :: Segment -> Arg
69+captureArg (Segment (Cap s)) = s
70+captureArg _ = error "captureArg called on non capture"
71type Path = [Segment]
72type Path = [Segment]
73data ArgType
74data ArgType
@@ -86,10 +87,10 @@ data QueryArg = QueryArg
86makeLenses ''QueryArg
87makeLenses ''QueryArg
88data HeaderArg = HeaderArg
89data HeaderArg = HeaderArg
89− { headerArg :: Arg }
90+ { _headerArg :: Arg }
90 | ReplaceHeaderArg
91 | ReplaceHeaderArg
91− { headerArg :: Arg
92+ { _headerArg :: Arg
92− , headerPattern :: Text
93+ , _headerPattern :: Text
93 } deriving (Eq, Show)
94 } deriving (Eq, Show)
95makeLenses ''HeaderArg
96makeLenses ''HeaderArg
@@ -117,16 +118,8 @@ data Req = Req
118makeLenses ''Req
119makeLenses ''Req
120−isCapture :: Segment -> Bool
121−isCapture (Segment (Cap _)) = True
124−captureArg :: Segment -> Arg
125−captureArg (Segment (Cap s)) = s
126−captureArg _ = error "captureArg called on non capture"
128defReq :: Req
121defReq :: Req
129−defReq = Req defUrl "GET" [] Nothing "" []
122+defReq = Req defUrl "GET" [] Nothing (ForeignType "") (FunctionName [])
131-- | To be used exclusively as a "negative" return type/constraint
124-- | To be used exclusively as a "negative" return type/constraint
132-- by @'Elem`@ type family.
125-- by @'Elem`@ type family.
@@ -173,7 +166,7 @@ class HasForeignType lang a where
173data NoTypes
166data NoTypes
175instance HasForeignType NoTypes ftype where
168instance HasForeignType NoTypes ftype where
176− typeFor _ _ = empty
169+ typeFor _ _ = ForeignType empty
178type HasNoForeignType = HasForeignType NoTypes
171type HasNoForeignType = HasForeignType NoTypes
@@ -195,18 +188,21 @@ instance (KnownSymbol sym, HasForeignType lang ftype, HasForeign lang sublayout)
196 foreignFor lang Proxy req =
189 foreignFor lang Proxy req =
197 foreignFor lang (Proxy :: Proxy sublayout) $
190 foreignFor lang (Proxy :: Proxy sublayout) $
198− req & reqUrl.path <>~ [Segment (Cap arg)]
191+ req & reqUrl . path <>~ [Segment (Cap arg)]
199− & reqFuncName %~ (++ ["by", str])
192+ & reqFuncName . _FunctionName %~ (++ ["by", str])
201− str = pack . symbolVal $ (Proxy :: Proxy sym)
194+ str = pack . symbolVal $ (Proxy :: Proxy sym)
202− arg = (str, typeFor lang (Proxy :: Proxy ftype))
195+ ftype = typeFor lang (Proxy :: Proxy ftype)
197+ { _aName = PathSegment str
204instance (Elem JSON list, HasForeignType lang a, ReflectMethod method)
200instance (Elem JSON list, HasForeignType lang a, ReflectMethod method)
205 => HasForeign lang (Verb method status list a) where
201 => HasForeign lang (Verb method status list a) where
206 type Foreign (Verb method status list a) = Req
202 type Foreign (Verb method status list a) = Req
208 foreignFor lang Proxy req =
204 foreignFor lang Proxy req =
209− req & reqFuncName %~ (methodLC :)
205+ req & reqFuncName . _FunctionName %~ (methodLC :)
210 & reqMethod .~ method
206 & reqMethod .~ method
211 & reqReturnType .~ retType
207 & reqReturnType .~ retType
@@ -219,12 +215,13 @@ instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
219 type Foreign (Header sym a :> sublayout) = Foreign sublayout
215 type Foreign (Header sym a :> sublayout) = Foreign sublayout
221 foreignFor lang Proxy req =
217 foreignFor lang Proxy req =
222− foreignFor lang subP $ req
223− & reqHeaders <>~ [HeaderArg arg]
218+ foreignFor lang subP $ req & reqHeaders <>~ [HeaderArg arg]
225 hname = pack . symbolVal $ (Proxy :: Proxy sym)
220 hname = pack . symbolVal $ (Proxy :: Proxy sym)
222+ { _aName = PathSegment hname
226− arg = (hname, typeFor lang (Proxy :: Proxy a))
223+ , _aType = typeFor lang (Proxy :: Proxy a) }
227− subP = Proxy :: Proxy sublayout
224+ subP = Proxy :: Proxy sublayout
229instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
226instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
230 => HasForeign lang (QueryParam sym a :> sublayout) where
227 => HasForeign lang (QueryParam sym a :> sublayout) where
@@ -233,10 +230,11 @@ instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
233 foreignFor lang Proxy req =
230 foreignFor lang Proxy req =
234 foreignFor lang (Proxy :: Proxy sublayout) $
231 foreignFor lang (Proxy :: Proxy sublayout) $
235 req & reqUrl.queryStr <>~ [QueryArg arg Normal]
232 req & reqUrl.queryStr <>~ [QueryArg arg Normal]
238 str = pack . symbolVal $ (Proxy :: Proxy sym)
234 str = pack . symbolVal $ (Proxy :: Proxy sym)
236+ { _aName = PathSegment str
239− arg = (str, typeFor lang (Proxy :: Proxy a))
237+ , _aType = typeFor lang (Proxy :: Proxy a) }
242 (KnownSymbol sym, HasForeignType lang [a], HasForeign lang sublayout)
240 (KnownSymbol sym, HasForeignType lang [a], HasForeign lang sublayout)
@@ -247,7 +245,9 @@ instance
247 req & reqUrl.queryStr <>~ [QueryArg arg List]
245 req & reqUrl.queryStr <>~ [QueryArg arg List]
249 str = pack . symbolVal $ (Proxy :: Proxy sym)
247 str = pack . symbolVal $ (Proxy :: Proxy sym)
249+ { _aName = PathSegment str
250− arg = (str, typeFor lang (Proxy :: Proxy [a]))
250+ , _aType = typeFor lang (Proxy :: Proxy [a]) }
253 (KnownSymbol sym, HasForeignType lang Bool, HasForeign lang sublayout)
253 (KnownSymbol sym, HasForeignType lang Bool, HasForeign lang sublayout)
@@ -259,13 +259,15 @@ instance
259 req & reqUrl.queryStr <>~ [QueryArg arg Flag]
259 req & reqUrl.queryStr <>~ [QueryArg arg Flag]
261 str = pack . symbolVal $ (Proxy :: Proxy sym)
261 str = pack . symbolVal $ (Proxy :: Proxy sym)
263+ { _aName = PathSegment str
262− arg = (str, typeFor lang (Proxy :: Proxy Bool))
264+ , _aType = typeFor lang (Proxy :: Proxy Bool) }
264instance HasForeign lang Raw where
266instance HasForeign lang Raw where
265 type Foreign Raw = HTTP.Method -> Req
267 type Foreign Raw = HTTP.Method -> Req
267 foreignFor _ Proxy req method =
269 foreignFor _ Proxy req method =
268− req & reqFuncName %~ ((toLower $ decodeUtf8 method) :)
270+ req & reqFuncName . _FunctionName %~ ((toLower $ decodeUtf8 method) :)
269 & reqMethod .~ method
271 & reqMethod .~ method
271instance (Elem JSON list, HasForeignType lang a, HasForeign lang sublayout)
273instance (Elem JSON list, HasForeignType lang a, HasForeign lang sublayout)
@@ -282,8 +284,8 @@ instance (KnownSymbol path, HasForeign lang sublayout)
283 foreignFor lang Proxy req =
285 foreignFor lang Proxy req =
284 foreignFor lang (Proxy :: Proxy sublayout) $
286 foreignFor lang (Proxy :: Proxy sublayout) $
285− req & reqUrl.path <>~ [Segment (Static str)]
287+ req & reqUrl . path <>~ [Segment (Static (PathSegment str))]
286− & reqFuncName %~ (++ [str])
288+ & reqFuncName . _FunctionName %~ (++ [str])
289 Data.Text.map (\c -> if c == '.' then '_' else c)
291 Data.Text.map (\c -> if c == '.' then '_' else c)