on master help sign in
server/config: added more documentation
09b2245Sönke Hahn
servant-examples/wai-middleware/wai-middleware.hs+1−1
@@ -41,7 +41,7 @@ server = return products
41
41
42-- logStdout :: Middleware
42-- logStdout :: Middleware
43-- i.e, logStdout :: Application -> Application
43-- i.e, logStdout :: Application -> Application
44−-- serve :: Proxy api -> Config a -> Server api -> Application
44+-- serve :: Proxy api -> Config config -> Server api -> Application
45-- so applying a middleware is really as simple as
45-- so applying a middleware is really as simple as
46-- applying a function to the result of 'serve'
46-- applying a function to the result of 'serve'
47app :: Application
47app :: Application
servant-server/src/Servant/Server.hs+3
@@ -38,7 +38,10 @@ module Servant.Server
38
38
39 -- * Config
39 -- * Config
40 , Config(..)
40 , Config(..)
41+ , HasConfigEntry(getConfigEntry)
42+ -- ** NamedConfig
41 , NamedConfig(..)
43 , NamedConfig(..)
44+ , descendIntoNamedConfig
42
45
43 -- * Default error type
46 -- * Default error type
44 , ServantErr(..)
47 , ServantErr(..)
servant-server/src/Servant/Server/Internal.hs−1
@@ -33,7 +33,6 @@ import Data.String (fromString)
33import Data.String.Conversions (cs, (<>))
33import Data.String.Conversions (cs, (<>))
34import Data.Text (Text)
34import Data.Text (Text)
35import Data.Typeable
35import Data.Typeable
36−import GHC.Exts (Constraint)
37import GHC.TypeLits (KnownNat, KnownSymbol, natVal,
36import GHC.TypeLits (KnownNat, KnownSymbol, natVal,
38 symbolVal)
37 symbolVal)
39import Network.HTTP.Types hiding (Header, ResponseHeaders)
38import Network.HTTP.Types hiding (Header, ResponseHeaders)
servant-server/src/Servant/Server/Internal/Config.hs+44−2
@@ -15,8 +15,18 @@ module Servant.Server.Internal.Config where
15import Data.Proxy
15import Data.Proxy
16import GHC.TypeLits
16import GHC.TypeLits
17
17
18−-- | The entire configuration.
18+-- | When calling 'Servant.Server.serve' you have to supply a configuration
19+-- value of type @'Config' configTypes@. This parameter is used to pass values
20+-- to combinators. (It shouldn't be confused with general configuration
21+-- parameters for your web app, like the port, etc.). If you don't use
22+-- combinators that require any config entries, you can just pass 'EmptyConfig'.
23+-- To create a config with entries, use the operator @(':.')@. The parameter of
24+-- the type 'Config' is a type-level list reflecting the types of the contained
25+-- config entries:
26+--
27+-- >>> :type True :. () :. EmptyConfig
28+-- True :. () :. EmptyConfig :: Config '[Bool, ()]
19−data Config a where
29+data Config configTypes where
20 EmptyConfig :: Config '[]
30 EmptyConfig :: Config '[]
21 (:.) :: x -> Config xs -> Config (x ': xs)
31 (:.) :: x -> Config xs -> Config (x ': xs)
22infixr 5 :.
32infixr 5 :.
@@ -33,6 +43,19 @@ instance Eq (Config '[]) where
33instance (Eq a, Eq (Config as)) => Eq (Config (a ': as)) where
43instance (Eq a, Eq (Config as)) => Eq (Config (a ': as)) where
34 x1 :. y1 == x2 :. y2 = x1 == x2 && y1 == y2
44 x1 :. y1 == x2 :. y2 = x1 == x2 && y1 == y2
35
45
46+-- | This class is used to access config entries in 'Config's. 'getConfigEntry'
47+-- returns the first value where the type matches:
48+--
49+-- >>> getConfigEntry (True :. False :. EmptyConfig) :: Bool
50+-- True
51+--
52+-- If the 'Config' does not contain an entry of the requested type, you'll get
53+-- an error:
54+--
55+-- >>> getConfigEntry (True :. False :. EmptyConfig) :: String
56+-- ...
57+-- No instance for (HasConfigEntry '[] [Char])
58+-- ...
36class HasConfigEntry (config :: [*]) (val :: *) where
59class HasConfigEntry (config :: [*]) (val :: *) where
37 getConfigEntry :: Config config -> val
60 getConfigEntry :: Config config -> val
38
61
@@ -46,9 +69,28 @@ instance OVERLAPPING_
46
69
47-- * support for named subconfigs
70-- * support for named subconfigs
48
71
72+-- | Normally config entries are accessed by their types. In case you need
73+-- to have multiple values of the same type in your 'Config' and need to access
74+-- them, we provide 'NamedConfig'. You can think of it as sub-namespaces for
75+-- 'Config's.
49data NamedConfig (name :: Symbol) (subConfig :: [*])
76data NamedConfig (name :: Symbol) (subConfig :: [*])
50 = NamedConfig (Config subConfig)
77 = NamedConfig (Config subConfig)
51
78
79+-- | 'descendIntoNamedConfig' allows you to access `NamedConfig's. Usually you
80+-- won't have to use it yourself but instead use a combinator like
81+-- 'Servant.API.WithNamedConfig.WithNamedConfig'.
82+--
83+-- This is how 'descendIntoNamedConfig' works:
84+--
85+-- >>> :set -XFlexibleContexts
86+-- >>> let subConfig = True :. EmptyConfig
87+-- >>> :type subConfig
88+-- subConfig :: Config '[Bool]
89+-- >>> let parentConfig = False :. (NamedConfig subConfig :: NamedConfig "subConfig" '[Bool]) :. EmptyConfig
90+-- >>> :type parentConfig
91+-- parentConfig :: Config '[Bool, NamedConfig "subConfig" '[Bool]]
92+-- >>> descendIntoNamedConfig (Proxy :: Proxy "subConfig") parentConfig :: Config '[Bool]
93+-- True :. EmptyConfig
52descendIntoNamedConfig :: forall config name subConfig .
94descendIntoNamedConfig :: forall config name subConfig .
53 HasConfigEntry config (NamedConfig name subConfig) =>
95 HasConfigEntry config (NamedConfig name subConfig) =>
54 Proxy (name :: Symbol) -> Config config -> Config subConfig
96 Proxy (name :: Symbol) -> Config config -> Config subConfig
servant/src/Servant/API/WithNamedConfig.hs+5−9
@@ -6,20 +6,16 @@ module Servant.API.WithNamedConfig where
6import GHC.TypeLits
6import GHC.TypeLits
7
7
8-- | 'WithNamedConfig' names a specific tagged configuration to use for the
8-- | 'WithNamedConfig' names a specific tagged configuration to use for the
9−-- combinators in the API. For example:
9+-- combinators in the API. (See also in @servant-server@,
10+-- @Servant.Server.Config@.) For example:
10--
11--
11−-- > type UseNamedConfigAPI1 = WithNamedConfig "myConfig" '[String] (
12+-- > type UseNamedConfigAPI = WithNamedConfig "myConfig" '[String] (
12-- > ReqBody '[JSON] Int :> Get '[JSON] Int)
13-- > ReqBody '[JSON] Int :> Get '[JSON] Int)
13--
14--
14-- Both the 'ReqBody' and 'Get' combinators will use the 'NamedConfig' with
15-- Both the 'ReqBody' and 'Get' combinators will use the 'NamedConfig' with
15−-- type tag "myConfig" as their configuration. In constrast, in (notice
16+-- type tag "myConfig" as their configuration.
16−-- parentesizing):
17--
17--
18−-- > type UseNamedConfigAPI2 = WithNamedConfig "myConfig" '[String] (
18+-- 'Config's are only relevant for @servant-server@.
19−-- > ReqBody '[JSON] Int) :> Get '[JSON] Int
20−--
21−-- Only the 'ReqBody' combinator will use this configuration, and 'Get' will
22−-- maintain the default configuration.
23--
19--
24-- For more information, see the tutorial.
20-- For more information, see the tutorial.
25data WithNamedConfig (name :: Symbol) (subConfig :: [*]) subApi
21data WithNamedConfig (name :: Symbol) (subConfig :: [*]) subApi