@@ -1,27 +1,15 @@
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)
23−import qualified Data.Char as C
24import Data.Proxy
11import Data.Proxy
25import Data.Text
13import Data.Text
26import Data.Text.Encoding (decodeUtf8)
14import Data.Text.Encoding (decodeUtf8)
27import GHC.Exts (Constraint)
15import GHC.Exts (Constraint)
@@ -30,36 +18,38 @@ import qualified Network.HTTP.Types as HTTP
30import Prelude hiding (concat)
18import Prelude hiding (concat)
31import Servant.API
19import Servant.API
33−type FunctionName = [Text]
35−-- | Function name builder that simply concat each part together
22+newtype FunctionName = FunctionName { unFunctionName :: [Text] }
36−concatCase :: FunctionName -> Text
23+ deriving (Show, Eq, Monoid)
39−-- | Function name builder using the snake_case convention.
40−-- each part is separated by a single underscore character.
41−snakeCase :: FunctionName -> Text
25+makePrisms ''FunctionName
42−snakeCase = intercalate "_"
44−-- | Function name builder using the CamelCase convention.
27+newtype ForeignType = ForeignType { unForeignType :: Text }
45−-- each part begins with an upper case character.
28+ 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
30+makePrisms ''ForeignType
32+newtype PathSegment = PathSegment { unPathSegment :: Text }
33+ deriving (Show, Eq, IsString, Monoid)
35+makePrisms ''PathSegment
38+ { _aName :: PathSegment
55−type Arg = (Text, ForeignType)
39+ , _aType :: ForeignType }
44+aPath :: Getter Arg Text
45+aPath = aName . _PathSegment
57data SegmentType
47data SegmentType
58− = Static Text
48+ = Static PathSegment
59 -- ^ a static path segment. like "/foo"
49 -- ^ a static path segment. like "/foo"
61 -- ^ a capture. like "/:userid"
51 -- ^ a capture. like "/:userid"
62− deriving (Eq, Show)
52+ deriving (Show, Eq)
64makePrisms ''SegmentType
54makePrisms ''SegmentType
@@ -68,6 +58,14 @@ newtype Segment = Segment { unSegment :: SegmentType }
69makePrisms ''Segment
59makePrisms ''Segment
61+isCapture :: Segment -> Bool
62+isCapture (Segment (Cap _)) = True
65+captureArg :: Segment -> Arg
66+captureArg (Segment (Cap s)) = s
67+captureArg _ = error "captureArg called on non capture"
71type Path = [Segment]
69type Path = [Segment]
73data ArgType
71data ArgType
@@ -86,10 +84,10 @@ data QueryArg = QueryArg
86makeLenses ''QueryArg
84makeLenses ''QueryArg
88data HeaderArg = HeaderArg
86data HeaderArg = HeaderArg
89− { headerArg :: Arg }
87+ { _headerArg :: Arg }
90 | ReplaceHeaderArg
88 | ReplaceHeaderArg
91− { headerArg :: Arg
89+ { _headerArg :: Arg
92− , headerPattern :: Text
90+ , _headerPattern :: Text
93 } deriving (Eq, Show)
91 } deriving (Eq, Show)
95makeLenses ''HeaderArg
93makeLenses ''HeaderArg
@@ -117,16 +115,8 @@ data Req = Req
118makeLenses ''Req
116makeLenses ''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
118defReq :: Req
129−defReq = Req defUrl "GET" [] Nothing "" []
119+defReq = Req defUrl "GET" [] Nothing (ForeignType "") (FunctionName [])
131-- | To be used exclusively as a "negative" return type/constraint
121-- | To be used exclusively as a "negative" return type/constraint
132-- by @'Elem`@ type family.
122-- by @'Elem`@ type family.
@@ -173,7 +163,7 @@ class HasForeignType lang a where
173data NoTypes
163data NoTypes
175instance HasForeignType NoTypes ftype where
165instance HasForeignType NoTypes ftype where
176− typeFor _ _ = empty
166+ typeFor _ _ = ForeignType empty
178type HasNoForeignType = HasForeignType NoTypes
168type HasNoForeignType = HasForeignType NoTypes
@@ -195,18 +185,21 @@ instance (KnownSymbol sym, HasForeignType lang ftype, HasForeign lang sublayout)
196 foreignFor lang Proxy req =
186 foreignFor lang Proxy req =
197 foreignFor lang (Proxy :: Proxy sublayout) $
187 foreignFor lang (Proxy :: Proxy sublayout) $
198− req & reqUrl.path <>~ [Segment (Cap arg)]
188+ req & reqUrl . path <>~ [Segment (Cap arg)]
199− & reqFuncName %~ (++ ["by", str])
189+ & reqFuncName . _FunctionName %~ (++ ["by", str])
201− str = pack . symbolVal $ (Proxy :: Proxy sym)
191+ str = pack . symbolVal $ (Proxy :: Proxy sym)
202− arg = (str, typeFor lang (Proxy :: Proxy ftype))
192+ ftype = typeFor lang (Proxy :: Proxy ftype)
194+ { _aName = PathSegment str
204instance (Elem JSON list, HasForeignType lang a, ReflectMethod method)
197instance (Elem JSON list, HasForeignType lang a, ReflectMethod method)
205 => HasForeign lang (Verb method status list a) where
198 => HasForeign lang (Verb method status list a) where
206 type Foreign (Verb method status list a) = Req
199 type Foreign (Verb method status list a) = Req
208 foreignFor lang Proxy req =
201 foreignFor lang Proxy req =
209− req & reqFuncName %~ (methodLC :)
202+ req & reqFuncName . _FunctionName %~ (methodLC :)
210 & reqMethod .~ method
203 & reqMethod .~ method
211 & reqReturnType .~ retType
204 & reqReturnType .~ retType
@@ -219,12 +212,13 @@ instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
219 type Foreign (Header sym a :> sublayout) = Foreign sublayout
212 type Foreign (Header sym a :> sublayout) = Foreign sublayout
221 foreignFor lang Proxy req =
214 foreignFor lang Proxy req =
222− foreignFor lang subP $ req
223− & reqHeaders <>~ [HeaderArg arg]
215+ foreignFor lang subP $ req & reqHeaders <>~ [HeaderArg arg]
225 hname = pack . symbolVal $ (Proxy :: Proxy sym)
217 hname = pack . symbolVal $ (Proxy :: Proxy sym)
219+ { _aName = PathSegment hname
226− arg = (hname, typeFor lang (Proxy :: Proxy a))
220+ , _aType = typeFor lang (Proxy :: Proxy a) }
227− subP = Proxy :: Proxy sublayout
221+ subP = Proxy :: Proxy sublayout
229instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
223instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
230 => HasForeign lang (QueryParam sym a :> sublayout) where
224 => HasForeign lang (QueryParam sym a :> sublayout) where
@@ -233,10 +227,11 @@ instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
233 foreignFor lang Proxy req =
227 foreignFor lang Proxy req =
234 foreignFor lang (Proxy :: Proxy sublayout) $
228 foreignFor lang (Proxy :: Proxy sublayout) $
235 req & reqUrl.queryStr <>~ [QueryArg arg Normal]
229 req & reqUrl.queryStr <>~ [QueryArg arg Normal]
238 str = pack . symbolVal $ (Proxy :: Proxy sym)
231 str = pack . symbolVal $ (Proxy :: Proxy sym)
233+ { _aName = PathSegment str
239− arg = (str, typeFor lang (Proxy :: Proxy a))
234+ , _aType = typeFor lang (Proxy :: Proxy a) }
242 (KnownSymbol sym, HasForeignType lang [a], HasForeign lang sublayout)
237 (KnownSymbol sym, HasForeignType lang [a], HasForeign lang sublayout)
@@ -247,7 +242,9 @@ instance
247 req & reqUrl.queryStr <>~ [QueryArg arg List]
242 req & reqUrl.queryStr <>~ [QueryArg arg List]
249 str = pack . symbolVal $ (Proxy :: Proxy sym)
244 str = pack . symbolVal $ (Proxy :: Proxy sym)
246+ { _aName = PathSegment str
250− arg = (str, typeFor lang (Proxy :: Proxy [a]))
247+ , _aType = typeFor lang (Proxy :: Proxy [a]) }
253 (KnownSymbol sym, HasForeignType lang Bool, HasForeign lang sublayout)
250 (KnownSymbol sym, HasForeignType lang Bool, HasForeign lang sublayout)
@@ -259,13 +256,15 @@ instance
259 req & reqUrl.queryStr <>~ [QueryArg arg Flag]
256 req & reqUrl.queryStr <>~ [QueryArg arg Flag]
261 str = pack . symbolVal $ (Proxy :: Proxy sym)
258 str = pack . symbolVal $ (Proxy :: Proxy sym)
260+ { _aName = PathSegment str
262− arg = (str, typeFor lang (Proxy :: Proxy Bool))
261+ , _aType = typeFor lang (Proxy :: Proxy Bool) }
264instance HasForeign lang Raw where
263instance HasForeign lang Raw where
265 type Foreign Raw = HTTP.Method -> Req
264 type Foreign Raw = HTTP.Method -> Req
267 foreignFor _ Proxy req method =
266 foreignFor _ Proxy req method =
268− req & reqFuncName %~ ((toLower $ decodeUtf8 method) :)
267+ req & reqFuncName . _FunctionName %~ ((toLower $ decodeUtf8 method) :)
269 & reqMethod .~ method
268 & reqMethod .~ method
271instance (Elem JSON list, HasForeignType lang a, HasForeign lang sublayout)
270instance (Elem JSON list, HasForeignType lang a, HasForeign lang sublayout)
@@ -282,8 +281,8 @@ instance (KnownSymbol path, HasForeign lang sublayout)
283 foreignFor lang Proxy req =
282 foreignFor lang Proxy req =
284 foreignFor lang (Proxy :: Proxy sublayout) $
283 foreignFor lang (Proxy :: Proxy sublayout) $
285− req & reqUrl.path <>~ [Segment (Static str)]
284+ req & reqUrl . path <>~ [Segment (Static (PathSegment str))]
286− & reqFuncName %~ (++ [str])
285+ & reqFuncName . _FunctionName %~ (++ [str])
289 Data.Text.map (\c -> if c == '.' then '_' else c)
288 Data.Text.map (\c -> if c == '.' then '_' else c)