on master help sign in
Merge servant-generic
d10520dOleg Grenrus
cabal.project+1
@@ -12,6 +12,7 @@ packages: servant/
12 doc/cookbook/db-postgres-pool
12 doc/cookbook/db-postgres-pool
13 doc/cookbook/db-sqlite-simple
13 doc/cookbook/db-sqlite-simple
14 doc/cookbook/file-upload
14 doc/cookbook/file-upload
15+ doc/cookbook/generic
15 doc/cookbook/https
16 doc/cookbook/https
16 doc/cookbook/jwt-and-basic-auth
17 doc/cookbook/jwt-and-basic-auth
17 doc/cookbook/pagination
18 doc/cookbook/pagination
doc/cookbook/generic/Generic.lhsnew file+106
@@ -0,0 +1,106 @@
1+# Using generics
2+
3+```haskell
4+{-# LANGUAGE DataKinds #-}
5+{-# LANGUAGE DeriveGeneric #-}
6+{-# LANGUAGE RankNTypes #-}
7+{-# LANGUAGE TypeOperators #-}
8+module Main (main, api, getLink, routesLinks, cliGet) where
9+
10+import Control.Exception (throwIO)
11+import Data.Proxy (Proxy (..))
12+import Network.Wai.Handler.Warp (run)
13+import System.Environment (getArgs)
14+
15+import Servant
16+import Servant.Client
17+
18+import Servant.API.Generic
19+import Servant.Client.Generic
20+import Servant.Server.Generic
21+```
22+
23+The usage is simple, if you only need a collection of routes.
24+First you define a record with field types prefixed by a parameter `route`:
25+
26+```haskell
27+data Routes route = Routes
28+ { _get :: route :- Capture "id" Int :> Get '[JSON] String
29+ , _put :: route :- ReqBody '[JSON] Int :> Put '[JSON] Bool
30+ }
31+ deriving (Generic)
32+```
33+
34+Then we'll use this data type to define API, links, server and client.
35+
36+## API
37+
38+You can get a `Proxy` of the API using `genericApi`:
39+
40+```haskell
41+api :: Proxy (ToServantApi Routes)
42+api = genericApi (Proxy :: Proxy Routes)
43+```
44+
45+It's recommented to use `genericApi` function, as then you'll get
46+better error message, for example if you forget to `derive Generic`.
47+
48+## Links
49+
50+The clear advantage of record-based generics approach, is that
51+we can get safe links very conviently. We don't need to define endpoint types,
52+as field accessors work as proxies:
53+
54+```haskell
55+getLink :: Int -> Link
56+getLink = fieldLink _get
57+```
58+
59+We can also get all links at once, as a record:
60+
61+```haskell
62+routesLinks :: Routes (AsLink Link)
63+routesLinks = allFieldLinks
64+```
65+
66+## Client
67+
68+Even more power starts to show when we generate a record of client functions.
69+Here we use `genericClientHoist` function, which let us simultaneously
70+hoist the monad, in this case from `ClientM` to `IO`.
71+
72+```haskell
73+cliRoutes :: Routes (AsClientT IO)
74+cliRoutes = genericClientHoist
75+ (\x -> runClientM x env >>= either throwIO return)
76+ where
77+ env = error "undefined environment"
78+
79+cliGet :: Int -> IO String
80+cliGet = _get cliRoutes
81+```
82+
83+## Server
84+
85+Finally, probably the most handy usage: we can convert record of handlers into
86+the server implementation:
87+
88+```haskell
89+record :: Routes AsServer
90+record = Routes
91+ { _get = return . show
92+ , _put = return . odd
93+ }
94+
95+app :: Application
96+app = genericServe record
97+
98+main :: IO ()
99+main = do
100+ args <- getArgs
101+ case args of
102+ ("run":_) -> do
103+ putStrLn "Starting cookbook-generic at http://localhost:8000"
104+ run 8000 app
105+ _ -> putStrLn "To run, pass 'run' argument: cabal new-run cookbook-generic run"
106+```
doc/cookbook/generic/generic.cabalnew file+25
@@ -0,0 +1,25 @@
1+name: cookbook-generic
2+version: 0.1
3+synopsis: Using custom monad to pass a state between handlers
4+homepage: http://haskell-servant.readthedocs.org/
5+license: BSD3
6+license-file: ../../../servant/LICENSE
7+author: Servant Contributors
8+maintainer: haskell-servant-maintainers@googlegroups.com
9+build-type: Simple
10+cabal-version: >=1.10
11+tested-with: GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3
12+
13+executable cookbook-using-custom-monad
14+ main-is: Generic.lhs
15+ build-depends: base == 4.*
16+ , servant
17+ , servant-client
18+ , servant-client-core
19+ , servant-server
20+ , base-compat
21+ , warp >= 3.2
22+ , transformers >= 0.3
23+ default-language: Haskell2010
24+ ghc-options: -Wall -pgmL markdown-unlit
25+ build-tool-depends: markdown-unlit:markdown-unlit >= 0.4
doc/tutorial/tutorial.cabal+2−2
@@ -75,8 +75,8 @@ library
75 , time >= 1.4.2 && < 1.9
75 , time >= 1.4.2 && < 1.9
76
76
77 -- For legacy tools, we need to specify build-depends too
77 -- For legacy tools, we need to specify build-depends too
78− build-depends: markdown-unlit >= 0.4.1 && <0.5
78+ build-depends: markdown-unlit >= 0.5.0 && <0.6
79− build-tool-depends: markdown-unlit:markdown-unlit >= 0.4.1 && <0.5
79+ build-tool-depends: markdown-unlit:markdown-unlit >= 0.5.0 && <0.6
80
80
81test-suite spec
81test-suite spec
82 type: exitcode-stdio-1.0
82 type: exitcode-stdio-1.0
servant-client-core/CHANGELOG.md+8
@@ -1,6 +1,14 @@
1[The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client-core/CHANGELOG.md)
1[The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client-core/CHANGELOG.md)
2[Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md)
2[Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md)
3
3
4+0.14.1
5+------
6+
7+- Merge in `servant-generic` (by [Patrick Chilton](https://github.com/chpatrick))
8+ into `servant` (`Servant.API.Generic`),
9+ `servant-client-code` (`Servant.Client.Generic`)
10+ and `servant-server` (`Servant.Server.Generic`).
11+
40.14
120.14
5----
13----
6
14
servant-client-core/servant-client-core.cabal+3−2
@@ -1,5 +1,5 @@
1name: servant-client-core
1name: servant-client-core
2−version: 0.14
2+version: 0.14.1
3synopsis: Core functionality and class for client function generation for servant APIs
3synopsis: Core functionality and class for client function generation for servant APIs
4description:
4description:
5 This library provides backend-agnostic generation of client functions. For
5 This library provides backend-agnostic generation of client functions. For
@@ -33,6 +33,7 @@ library
33 exposed-modules:
33 exposed-modules:
34 Servant.Client.Core
34 Servant.Client.Core
35 Servant.Client.Free
35 Servant.Client.Free
36+ Servant.Client.Generic
36 Servant.Client.Core.Reexport
37 Servant.Client.Core.Reexport
37 Servant.Client.Core.Internal.Auth
38 Servant.Client.Core.Internal.Auth
38 Servant.Client.Core.Internal.BaseUrl
39 Servant.Client.Core.Internal.BaseUrl
@@ -59,7 +60,7 @@ library
59
60
60 -- Servant dependencies
61 -- Servant dependencies
61 build-depends:
62 build-depends:
62− servant == 0.14.*
63+ servant >= 0.14.1 && <0.15
63
64
64 -- Other dependencies: Lower bound around what is in the latest Stackage LTS.
65 -- Other dependencies: Lower bound around what is in the latest Stackage LTS.
65 -- Here can be exceptions if we really need features from the newer versions.
66 -- Here can be exceptions if we really need features from the newer versions.
servant-client-core/src/Servant/Client/Generic.hsnew file+51
@@ -0,0 +1,51 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE KindSignatures #-}
4+{-# LANGUAGE RankNTypes #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TypeFamilies #-}
7+module Servant.Client.Generic (
8+ AsClientT,
9+ genericClient,
10+ genericClientHoist,
11+ ) where
12+
13+import Data.Proxy
14+ (Proxy (..))
15+
16+import Servant.API.Generic
17+import Servant.Client.Core
18+
19+-- | A type that specifies that an API reocrd contains a client implementation.
20+data AsClientT (m :: * -> *)
21+instance GenericMode (AsClientT m) where
22+ type AsClientT m :- api = Client m api
23+
24+-- | Generate a record of client functions.
25+genericClient
26+ :: forall routes m.
27+ ( HasClient m (ToServantApi routes)
28+ , GenericServant routes (AsClientT m)
29+ , Client m (ToServantApi routes) ~ ToServant routes (AsClientT m)
30+ )
31+ => routes (AsClientT m)
32+genericClient
33+ = fromServant
34+ $ clientIn (Proxy :: Proxy (ToServantApi routes)) (Proxy :: Proxy m)
35+
36+-- | 'genericClient' but with 'hoistClientMonad' in between.
37+genericClientHoist
38+ :: forall routes m n.
39+ ( HasClient m (ToServantApi routes)
40+ , GenericServant routes (AsClientT n)
41+ , Client n (ToServantApi routes) ~ ToServant routes (AsClientT n)
42+ )
43+ => (forall x. m x -> n x) -- ^ natural transformation
44+ -> routes (AsClientT n)
45+genericClientHoist nt
46+ = fromServant
47+ $ hoistClientMonad m api nt
48+ $ clientIn api m
49+ where
50+ m = Proxy :: Proxy m
51+ api = Proxy :: Proxy (ToServantApi routes)
servant-server/CHANGELOG.md+10
@@ -1,6 +1,16 @@
1[The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-server/CHANGELOG.md)
1[The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-server/CHANGELOG.md)
2[Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md)
2[Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md)
3
3
4+0.14.1
5+------
6+
7+- Merge in `servant-generic` (by [Patrick Chilton](https://github.com/chpatrick))
8+ into `servant` (`Servant.API.Generic`),
9+ `servant-client-code` (`Servant.Client.Generic`)
10+ and `servant-server` (`Servant.Server.Generic`).
11+
12+- *servant-server* Deprecate `Servant.Utils.StaticUtils`, use `Servant.Server.StaticUtils`.
13+
40.14
140.14
5----
15----
6
16
servant-server/servant-server.cabal+3−2
@@ -1,5 +1,5 @@
1name: servant-server
1name: servant-server
2−version: 0.14
2+version: 0.14.1
3synopsis: A family of combinators for defining webservices APIs and serving them
3synopsis: A family of combinators for defining webservices APIs and serving them
4description:
4description:
5 A family of combinators for defining webservices APIs and serving them
5 A family of combinators for defining webservices APIs and serving them
@@ -47,6 +47,7 @@ library
47 Servant
47 Servant
48 Servant.Server
48 Servant.Server
49 Servant.Server.Experimental.Auth
49 Servant.Server.Experimental.Auth
50+ Servant.Server.Generic
50 Servant.Server.Internal
51 Servant.Server.Internal
51 Servant.Server.Internal.BasicAuth
52 Servant.Server.Internal.BasicAuth
52 Servant.Server.Internal.Context
53 Servant.Server.Internal.Context
@@ -79,7 +80,7 @@ library
79
80
80 -- Servant dependencies
81 -- Servant dependencies
81 build-depends:
82 build-depends:
82− servant == 0.14.*
83+ servant >= 0.14.1 && <0.15
83
84
84 -- Other dependencies: Lower bound around what is in the latest Stackage LTS.
85 -- Other dependencies: Lower bound around what is in the latest Stackage LTS.
85 -- Here can be exceptions if we really need features from the newer versions.
86 -- Here can be exceptions if we really need features from the newer versions.
servant-server/src/Servant/Server/Generic.hsnew file+52
@@ -0,0 +1,52 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE KindSignatures #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TypeFamilies #-}
7+{-# LANGUAGE TypeOperators #-}
8+{-# LANGUAGE UndecidableInstances #-}
9+-- | @since 0.14.1
10+module Servant.Server.Generic (
11+ AsServerT,
12+ AsServer,
13+ genericServe,
14+ genericServer,
15+ genericServerT,
16+ ) where
17+
18+import Data.Proxy
19+ (Proxy (..))
20+
21+import Servant.API.Generic
22+import Servant.Server
23+
24+-- | A type that specifies that an API record contains a server implementation.
25+data AsServerT (m :: * -> *)
26+instance GenericMode (AsServerT m) where
27+ type AsServerT m :- api = ServerT api m
28+
29+type AsServer = AsServerT Handler
30+
31+-- | Transform record of routes into a WAI 'Application'.
32+genericServe
33+ :: forall routes.
34+ ( HasServer (ToServantApi routes) '[]
35+ , GenericServant routes AsServer
36+ , Server (ToServantApi routes) ~ ToServant routes AsServer
37+ )
38+ => routes AsServer -> Application
39+genericServe = serve (Proxy :: Proxy (ToServantApi routes)) . genericServer
40+
41+-- | Transform record of endpoints into a 'Server'.
42+genericServer
43+ :: GenericServant routes AsServer
44+ => routes AsServer
45+ -> ToServant routes AsServer
46+genericServer = toServant
47+
48+genericServerT
49+ :: GenericServant routes (AsServerT m)
50+ => routes (AsServerT m)
51+ -> ToServant routes (AsServerT m)
52+genericServerT = toServant
servant/CHANGELOG.md+13
@@ -1,5 +1,18 @@
1[The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md)
1[The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md)
2
2
3+0.14.1
4+------
5+
6+- Merge in (and slightly refactor) `servant-generic`
7+ (by [Patrick Chilton](https://github.com/chpatrick))
8+ into `servant` (`Servant.API.Generic`),
9+ `servant-client-code` (`Servant.Client.Generic`)
10+ and `servant-server` (`Servant.Server.Generic`).
11+
12+- Deprecate `Servant.Utils.Links`, use `Servant.Links`.
13+
14+- *servant-server* Deprecate `Servant.Utils.StaticUtils`, use `Servant.Server.StaticUtils`.
15+
30.14
160.14
4----
17----
5
18
servant/servant.cabal+2−1
@@ -1,5 +1,5 @@
1name: servant
1name: servant
2−version: 0.14
2+version: 0.14.1
3synopsis: A family of combinators for defining webservices APIs
3synopsis: A family of combinators for defining webservices APIs
4description:
4description:
5 A family of combinators for defining webservices APIs and serving them
5 A family of combinators for defining webservices APIs and serving them
@@ -46,6 +46,7 @@ library
46 Servant.API.Description
46 Servant.API.Description
47 Servant.API.Empty
47 Servant.API.Empty
48 Servant.API.Experimental.Auth
48 Servant.API.Experimental.Auth
49+ Servant.API.Generic
49 Servant.API.Header
50 Servant.API.Header
50 Servant.API.HttpVersion
51 Servant.API.HttpVersion
51 Servant.API.Internal.Test.ComprehensiveAPI
52 Servant.API.Internal.Test.ComprehensiveAPI
servant/src/Servant/API/Generic.hsnew file+146
@@ -0,0 +1,146 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE TypeFamilies #-}
4+{-# LANGUAGE TypeOperators #-}
5+-- | Define servant servers from record types. Generics for the win.
6+--
7+-- The usage is simple, if you only need a collection of routes. First you
8+-- define a record with field types prefixed by a parameter `route`:
9+--
10+-- @
11+-- data Routes route = Routes
12+-- { _get :: route :- Capture "id" Int :> Get '[JSON] String
13+-- , _put :: route :- ReqBody '[JSON] Int :> Put '[JSON] Bool
14+-- }
15+-- deriving ('Generic')
16+-- @
17+--
18+-- You can get a 'Proxy' of the server using
19+--
20+-- @
21+-- api :: Proxy (ToServantApi Routes)
22+-- api = genericApi (Proxy :: Proxy Routes)
23+-- @
24+--
25+-- Using 'genericApi' is better as it checks that instances exists,
26+-- i.e. you get better error messages than simply using 'Proxy' value.
27+--
28+-- __Note:__ in 0.14 series this module isn't re-exported from 'Servant.API'.
29+--
30+-- "Servant.API.Generic" is based on @servant-generic@ package by
31+-- [Patrick Chilton](https://github.com/chpatrick)
32+--
33+-- @since 0.14.1
34+module Servant.API.Generic (
35+ GenericMode (..),
36+ GenericServant,
37+ ToServant,
38+ toServant,
39+ fromServant,
40+ -- * AsApi
41+ AsApi,
42+ ToServantApi,
43+ genericApi,
44+ -- * Utility
45+ GServantProduct,
46+ -- * re-exports
47+ Generic (Rep),
48+ ) where
49+
50+-- Based on servant-generic licensed under MIT License
51+--
52+-- Copyright (c) 2017 Patrick Chilton
53+--
54+-- Permission is hereby granted, free of charge, to any person obtaining a copy
55+-- of this software and associated documentation files (the "Software"), to deal
56+-- in the Software without restriction, including without limitation the rights
57+-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
58+-- copies of the Software, and to permit persons to whom the Software is
59+-- furnished to do so, subject to the following conditions:
60+--
61+-- The above copyright notice and this permission notice shall be included in all
62+-- copies or substantial portions of the Software.
63+--
64+-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
65+-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
66+-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
67+-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
68+-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
69+-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
70+-- SOFTWARE.
71+
72+import Data.Proxy
73+ (Proxy (..))
74+import GHC.Generics
75+ ((:*:) (..), Generic (..), K1 (..), M1 (..))
76+
77+import Servant.API.Alternative
78+
79+-- | A constraint alias, for work with 'mode' and 'routes'.
80+type GenericServant routes mode = (GenericMode mode, Generic (routes mode), GServantProduct (Rep (routes mode)))
81+
82+-- | A class with a type family that applies an appropriate type family to the @api@
83+-- parameter. For example, 'AsApi' will leave @api@ untouched, while
84+-- @'AsServerT' m@ will produce @'ServerT' api m@.
85+class GenericMode mode where
86+ type mode :- api :: *
87+
88+infixl 0 :-
89+
90+-- | Turns a generic product type into a tree of `:<|>` combinators.
91+type ToServant routes mode = GToServant (Rep (routes mode))
92+
93+type ToServantApi routes = ToServant routes AsApi
94+
95+-- | See `ToServant`, but at value-level.
96+toServant
97+ :: GenericServant routes mode
98+ => routes mode -> ToServant routes mode
99+toServant = gtoServant . from
100+
101+-- | Inverse of `toServant`.
102+--
103+-- This can be used to turn 'generated' values such as client functions into records.
104+--
105+-- You may need to provide a type signature for the /output/ type (your record type).
106+fromServant
107+ :: GenericServant routes mode
108+ => ToServant routes mode -> routes mode
109+fromServant = to . gfromServant
110+
111+-- | A type that specifies that an API record contains an API definition. Only useful at type-level.
112+data AsApi
113+instance GenericMode AsApi where
114+ type AsApi :- api = api
115+
116+-- | Get a 'Proxy' of an API type.
117+genericApi
118+ :: GenericServant routes AsApi
119+ => Proxy routes
120+ -> Proxy (ToServantApi routes)
121+genericApi _ = Proxy
122+
123+-------------------------------------------------------------------------------
124+-- Class
125+-------------------------------------------------------------------------------
126+
127+
128+class GServantProduct f where
129+ type GToServant f
130+ gtoServant :: f p -> GToServant f
131+ gfromServant :: GToServant f -> f p
132+
133+instance GServantProduct f => GServantProduct (M1 i c f) where
134+ type GToServant (M1 i c f) = GToServant f
135+ gtoServant = gtoServant . unM1
136+ gfromServant = M1 . gfromServant
137+
138+instance (GServantProduct l, GServantProduct r) => GServantProduct (l :*: r) where
139+ type GToServant (l :*: r) = GToServant l :<|> GToServant r
140+ gtoServant (l :*: r) = gtoServant l :<|> gtoServant r
141+ gfromServant (l :<|> r) = gfromServant l :*: gfromServant r
142+
143+instance GServantProduct (K1 i c) where
144+ type GToServant (K1 i c) = c
145+ gtoServant = unK1
146+ gfromServant = K1
servant/src/Servant/Links.hs+86
@@ -91,6 +91,8 @@
91-- This error is essentially saying that the type family couldn't find
91-- This error is essentially saying that the type family couldn't find
92-- bad_link under api after trying the open (but empty) type family
92-- bad_link under api after trying the open (but empty) type family
93-- `IsElem'` as a last resort.
93-- `IsElem'` as a last resort.
94+--
95+-- @since 0.14.1
94module Servant.Links (
96module Servant.Links (
95 module Servant.API.TypeLevel,
97 module Servant.API.TypeLevel,
96
98
@@ -102,6 +104,12 @@ module Servant.Links (
102 , allLinks
104 , allLinks
103 , allLinks'
105 , allLinks'
104 , URI(..)
106 , URI(..)
107+ -- * Generics
108+ , AsLink
109+ , fieldLink
110+ , fieldLink'
111+ , allFieldLinks
112+ , allFieldLinks'
105 -- * Adding custom types
113 -- * Adding custom types
106 , HasLink(..)
114 , HasLink(..)
107 , Link
115 , Link
@@ -144,6 +152,7 @@ import Servant.API.Empty
144 (EmptyAPI (..))
152 (EmptyAPI (..))
145import Servant.API.Experimental.Auth
153import Servant.API.Experimental.Auth
146 (AuthProtect)
154 (AuthProtect)
155+import Servant.API.Generic
147import Servant.API.Header
156import Servant.API.Header
148 (Header')
157 (Header')
149import Servant.API.HttpVersion
158import Servant.API.HttpVersion
@@ -334,6 +343,83 @@ allLinks'
334 -> MkLink api a
343 -> MkLink api a
335allLinks' toA api = toLink toA api (Link mempty mempty)
344allLinks' toA api = toLink toA api (Link mempty mempty)
336
345
346+-------------------------------------------------------------------------------
347+-- Generics
348+-------------------------------------------------------------------------------
349+
350+-- | Given an API record field, create a link for that route. Only the field's
351+-- type is used.
352+--
353+-- @
354+-- data Record route = Record
355+-- { _get :: route :- Capture "id" Int :> Get '[JSON] String
356+-- , _put :: route :- ReqBody '[JSON] Int :> Put '[JSON] Bool
357+-- }
358+-- deriving ('Generic')
359+--
360+-- getLink :: Int -> Link
361+-- getLink = 'fieldLink' _get
362+-- @
363+--
364+-- @since 0.14.1
365+fieldLink
366+ :: ( IsElem endpoint (ToServantApi routes), HasLink endpoint
367+ , GenericServant routes AsApi
368+ )
369+ => (routes AsApi -> endpoint)
370+ -> MkLink endpoint Link
371+fieldLink = fieldLink' id
372+
373+-- | More general version of 'fieldLink'
374+--
375+-- @since 0.14.1
376+fieldLink'
377+ :: forall routes endpoint a.
378+ ( IsElem endpoint (ToServantApi routes), HasLink endpoint
379+ , GenericServant routes AsApi
380+ )
381+ => (Link -> a)
382+ -> (routes AsApi -> endpoint)
383+ -> MkLink endpoint a
384+fieldLink' toA _ = safeLink' toA (genericApi (Proxy :: Proxy routes)) (Proxy :: Proxy endpoint)
385+
386+-- | A type that specifies that an API record contains a set of links.
387+--
388+-- @since 0.14.1
389+data AsLink (a :: *)
390+instance GenericMode (AsLink a) where
391+ type (AsLink a) :- api = MkLink api a
392+
393+-- | Get all links as a record.
394+--
395+-- @since 0.14.1
396+allFieldLinks
397+ :: ( HasLink (ToServantApi routes)
398+ , GenericServant routes (AsLink Link)
399+ , ToServant routes (AsLink Link) ~ MkLink (ToServantApi routes) Link
400+ )
401+ => routes (AsLink Link)
402+allFieldLinks = allFieldLinks' id
403+
404+-- | More general version of 'allFieldLinks'.
405+--
406+-- @since 0.14.1
407+allFieldLinks'
408+ :: forall routes a.
409+ ( HasLink (ToServantApi routes)
410+ , GenericServant routes (AsLink a)
411+ , ToServant routes (AsLink a) ~ MkLink (ToServantApi routes) a
412+ )
413+ => (Link -> a)
414+ -> routes (AsLink a)
415+allFieldLinks' toA
416+ = fromServant
417+ $ allLinks' toA (Proxy :: Proxy (ToServantApi routes))
418+
419+-------------------------------------------------------------------------------
420+-- HasLink
421+-------------------------------------------------------------------------------
422+
337-- | Construct a toLink for an endpoint.
423-- | Construct a toLink for an endpoint.
338class HasLink endpoint where
424class HasLink endpoint where
339 type MkLink endpoint (a :: *)
425 type MkLink endpoint (a :: *)