on master help sign in
Merge branch 'jkarni/qq-docs' into new-impl
52c24c8Alp Mestanogullari
servant.cabal+1−1
@@ -31,8 +31,8 @@ library
31 Servant.Common.Req
31 Servant.Common.Req
32 Servant.Common.Text
32 Servant.Common.Text
33 Servant.Docs
33 Servant.Docs
34+ Servant.QQ
34 Servant.Server
35 Servant.Server
35− Servant.Utils.ApiQuasiQuoting
36 Servant.Utils.Links
36 Servant.Utils.Links
37 Servant.Utils.StaticFiles
37 Servant.Utils.StaticFiles
38 build-depends:
38 build-depends:
src/Servant.hs+2−2
@@ -12,7 +12,7 @@ module Servant (
12 -- | Using your types in request paths and query string parameters
12 -- | Using your types in request paths and query string parameters
13 module Servant.Common.Text,
13 module Servant.Common.Text,
14 -- | Utilities on top of the servant core
14 -- | Utilities on top of the servant core
15− module Servant.Utils.ApiQuasiQuoting,
15+ module Servant.QQ,
16 module Servant.Utils.Links,
16 module Servant.Utils.Links,
17 module Servant.Utils.StaticFiles,
17 module Servant.Utils.StaticFiles,
18 -- | Useful re-exports
18 -- | Useful re-exports
@@ -25,6 +25,6 @@ import Servant.Client
25import Servant.Common.Text
25import Servant.Common.Text
26import Servant.Docs
26import Servant.Docs
27import Servant.Server
27import Servant.Server
28−import Servant.Utils.ApiQuasiQuoting
28+import Servant.QQ
29import Servant.Utils.Links
29import Servant.Utils.Links
30import Servant.Utils.StaticFiles
30import Servant.Utils.StaticFiles
src/Servant/API.hs+2−2
@@ -31,7 +31,7 @@ module Servant.API (
31
31
32 -- * Utilities
32 -- * Utilities
33 -- | QuasiQuotes for endpoints
33 -- | QuasiQuotes for endpoints
34− module Servant.Utils.ApiQuasiQuoting,
34+ module Servant.QQ,
35 -- | Type-safe internal URLs
35 -- | Type-safe internal URLs
36 module Servant.Utils.Links,
36 module Servant.Utils.Links,
37 ) where
37 ) where
@@ -46,6 +46,6 @@ import Servant.API.QueryParam
46import Servant.API.Raw
46import Servant.API.Raw
47import Servant.API.ReqBody
47import Servant.API.ReqBody
48import Servant.API.Sub
48import Servant.API.Sub
49−import Servant.Utils.ApiQuasiQuoting (sitemap)
49+import Servant.QQ (sitemap)
50import Servant.Utils.Links (mkLink)
50import Servant.Utils.Links (mkLink)
51import Servant.Utils.StaticFiles
51import Servant.Utils.StaticFiles
src/Servant/Utils/ApiQuasiQuoting.hs → src/Servant/QQ.hsrenamed+40−1
@@ -5,7 +5,28 @@
5{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE TemplateHaskell #-}
6{-# LANGUAGE TemplateHaskell #-}
7{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
7{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
8+-- | QuasiQuoting utilities for API types.
9+--
10+-- 'sitemap' allows you to write your type in a very natural way:
11+--
12+-- @
13+-- [sitemap|
14+-- PUT hello String -> ()
15+-- POST hello/p:Int String -> ()
16+-- GET hello/?name:String Int
17+-- |]
18+-- @
19+--
20+-- Will generate:
21+--
22+-- @
23+-- "hello" :> ReqBody String :> Put ()
24+-- :\<|> "hello" :> Capture "p" Int :> ReqBody String :> Post ()
25+-- :\<|> "hello" :> QueryParam "name" String :> Get Int
26+-- @
27+--
28+-- Note the @/@ before a @QueryParam@!
8−module Servant.Utils.ApiQuasiQuoting where
29+module Servant.QQ where
9
30
10import Control.Monad (void)
31import Control.Monad (void)
11import Control.Applicative hiding (many, (<|>), optional)
32import Control.Applicative hiding (many, (<|>), optional)
@@ -23,6 +44,11 @@ import Servant.API.ReqBody
23import Servant.API.Sub
44import Servant.API.Sub
24import Servant.API.Alternative
45import Servant.API.Alternative
25
46
47+-- | Finally-tagless encoding for our DSL.
48+-- Keeping 'repr'' and 'repr' distinct when writing functions with an
49+-- @ExpSYM@ context ensures certain invariants (for instance, that there is
50+-- only one of 'get', 'post', 'put', and 'delete' in a value), but
51+-- sometimes requires a little more work.
26class ExpSYM repr' repr | repr -> repr', repr' -> repr where
52class ExpSYM repr' repr | repr -> repr', repr' -> repr where
27 lit :: String -> repr' -> repr
53 lit :: String -> repr' -> repr
28 capture :: String -> String -> repr -> repr
54 capture :: String -> String -> repr -> repr
@@ -148,6 +174,19 @@ parseAll = do
148 where union :: Type -> Type -> Type
174 where union :: Type -> Type -> Type
149 union a = AppT (AppT (ConT ''(:<|>)) a)
175 union a = AppT (AppT (ConT ''(:<|>)) a)
150
176
177+-- | The sitemap QuasiQuoter.
178+--
179+-- * @.../<var>:<type>/...@ becomes a capture
180+-- * @.../?<var>:<type>@ becomes a query parameter
181+-- * @<method> ... <typ>@ becomes a method returning @<typ>@
182+-- * @<method> ... <typ1> -> <typ2>@ becomes a method with request
183+-- body of @<typ1>@ and returning @<typ2>@
184+--
185+-- Comments are allowed, and have the standard Haskell format
186+--
187+-- * @--@ for inline
188+-- * @{- ... -}@ for block
189+--
151sitemap :: QuasiQuoter
190sitemap :: QuasiQuoter
152sitemap = QuasiQuoter { quoteExp = undefined
191sitemap = QuasiQuoter { quoteExp = undefined
153 , quotePat = undefined
192 , quotePat = undefined
src/Servant/Utils/Links.hs+27
@@ -6,6 +6,33 @@
6{-# LANGUAGE FunctionalDependencies #-}
6{-# LANGUAGE FunctionalDependencies #-}
7{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE UndecidableInstances #-}
8{-# LANGUAGE UndecidableInstances #-}
9+-- | Type safe internal links.
10+--
11+-- Provides the function 'mkLink':
12+--
13+-- @
14+-- type API = Proxy ("hello" :> Get Int
15+-- :<|> "bye" :> QueryParam "name" String :> Post Bool)
16+--
17+-- api :: API
18+-- api = proxy
19+--
20+-- link1 :: Proxy ("hello" :> Get Int)
21+-- link1 = proxy
22+--
23+-- link2 :: Proxy ("hello" :> Delete)
24+-- link2 = proxy
25+--
26+-- mkLink link1 API -- typechecks, returns 'Link "/hello"'
27+--
28+-- mkLink link2 API -- doesn't typecheck
29+-- @
30+--
31+-- That is, 'mkLink' takes two arguments, a link proxy and a sitemap, and
32+-- returns a 'Link', but only typechecks if the link proxy is a valid link,
33+-- and part of the sitemap.
34+--
35+-- __N.B.:__ 'mkLink' assumes a capture matches any string (without slashes).
9module Servant.Utils.Links where
36module Servant.Utils.Links where
10
37
11import Data.Proxy
38import Data.Proxy
test/Servant/Utils/ApiQuasiQuotingSpec.hs → test/Servant/QQSpec.hsrenamed+1−1
@@ -7,7 +7,7 @@
7{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeOperators #-}
8{-# LANGUAGE TypeOperators #-}
9{-# LANGUAGE UndecidableInstances #-}
9{-# LANGUAGE UndecidableInstances #-}
10−module Servant.Utils.ApiQuasiQuotingSpec where
10+module Servant.QQSpec where
11
11
12import Test.Hspec
12import Test.Hspec
13
13
test/Servant/Utils/LinksSpec.hs+1−1
@@ -6,7 +6,7 @@ module Servant.Utils.LinksSpec where
6import Test.Hspec
6import Test.Hspec
7
7
8import Servant.API
8import Servant.API
9−import Servant.Utils.ApiQuasiQuotingSpec ( (~>) )
9+import Servant.QQSpec ( (~>) )
10import Servant.Utils.Links (IsElem, IsLink)
10import Servant.Utils.Links (IsElem, IsLink)
11
11
12type TestApi =
12type TestApi =