@@ -24,7 +24,9 @@ module Servant.API.ResponseHeaders
24
24
, ResponseHeader (.. )
25
25
, AddHeader
26
26
, addHeader
27
+ , addHeader'
27
28
, noHeader
29
+ , noHeader'
28
30
, HasResponseHeader
29
31
, lookupResponseHeader
30
32
, BuildHeadersTo (buildHeadersTo )
@@ -37,7 +39,7 @@ module Servant.API.ResponseHeaders
37
39
import Control.DeepSeq
38
40
(NFData (.. ))
39
41
import Data.ByteString.Char8 as BS
40
- (ByteString , init , pack , unlines )
42
+ (ByteString , pack )
41
43
import qualified Data.CaseInsensitive as CI
42
44
import qualified Data.List as L
43
45
import Data.Proxy
@@ -52,7 +54,9 @@ import Web.HttpApiData
52
54
import Prelude ()
53
55
import Prelude.Compat
54
56
import Servant.API.Header
55
- (Header )
57
+ (Header' )
58
+ import Servant.API.Modifiers
59
+ (Optional , Strict )
56
60
import Servant.API.UVerb.Union
57
61
import qualified Data.SOP.BasicFunctors as SOP
58
62
import qualified Data.SOP.NS as SOP
@@ -81,19 +85,19 @@ instance NFData a => NFData (ResponseHeader sym a) where
81
85
82
86
data HList a where
83
87
HNil :: HList '[]
84
- HCons :: ResponseHeader h x -> HList xs -> HList (Header h x ': xs )
88
+ HCons :: ResponseHeader h x -> HList xs -> HList (Header' mods h x ': xs )
85
89
86
90
class NFDataHList xs where rnfHList :: HList xs -> ()
87
91
instance NFDataHList '[] where rnfHList HNil = ()
88
- instance (y ~ Header h x , NFData x , NFDataHList xs ) => NFDataHList (y ': xs ) where
92
+ instance (y ~ Header' mods h x , NFData x , NFDataHList xs ) => NFDataHList (y ': xs ) where
89
93
rnfHList (HCons h xs) = rnf h `seq` rnfHList xs
90
94
91
95
instance NFDataHList xs => NFData (HList xs ) where
92
96
rnf = rnfHList
93
97
94
98
type family HeaderValMap (f :: * -> * ) (xs :: [* ]) where
95
99
HeaderValMap f '[] = '[]
96
- HeaderValMap f (Header h x ': xs ) = Header h (f x ) ': HeaderValMap f xs
100
+ HeaderValMap f (Header' mods h x ': xs ) = Header' mods h (f x ) ': HeaderValMap f xs
97
101
98
102
99
103
class BuildHeadersTo hs where
@@ -105,7 +109,7 @@ instance {-# OVERLAPPING #-} BuildHeadersTo '[] where
105
109
-- The current implementation does not manipulate HTTP header field lines in any way,
106
110
-- like merging field lines with the same field name in a single line.
107
111
instance {-# OVERLAPPABLE #-} ( FromHttpApiData v , BuildHeadersTo xs , KnownSymbol h )
108
- => BuildHeadersTo (Header h v ': xs ) where
112
+ => BuildHeadersTo (Header' mods h v ': xs ) where
109
113
buildHeadersTo headers = case L. find wantedHeader headers of
110
114
Nothing -> MissingHeader `HCons ` buildHeadersTo headers
111
115
Just header@ (_, val) -> case parseHeader val of
@@ -130,7 +134,7 @@ instance GetHeadersFromHList '[] where
130
134
getHeadersFromHList _ = []
131
135
132
136
instance (KnownSymbol h , ToHttpApiData x , GetHeadersFromHList xs )
133
- => GetHeadersFromHList (Header h x ': xs )
137
+ => GetHeadersFromHList (Header' mods h x ': xs )
134
138
where
135
139
getHeadersFromHList hdrs = case hdrs of
136
140
Header val `HCons ` rest -> (headerName , toHeader val) : getHeadersFromHList rest
@@ -151,42 +155,42 @@ instance GetHeaders' '[] where
151
155
getHeaders' _ = []
152
156
153
157
instance (KnownSymbol h , GetHeadersFromHList rest , ToHttpApiData v )
154
- => GetHeaders' (Header h v ': rest )
158
+ => GetHeaders' (Header' mods h v ': rest )
155
159
where
156
160
getHeaders' hs = getHeadersFromHList $ getHeadersHList hs
157
161
158
162
-- * Adding headers
159
163
160
164
-- We need all these fundeps to save type inference
161
- class AddHeader h v orig new
162
- | h v orig -> new , new -> h , new -> v , new -> orig where
165
+ class AddHeader ( mods :: [ * ]) h v orig new
166
+ | mods h v orig -> new , new -> mods , new -> h , new -> v , new -> orig where
163
167
addOptionalHeader :: ResponseHeader h v -> orig -> new -- ^ N.B.: The same header can't be added multiple times
164
168
165
169
-- In this instance, we add a Header on top of something that is already decorated with some headers
166
170
instance {-# OVERLAPPING #-} ( KnownSymbol h , ToHttpApiData v )
167
- => AddHeader h v (Headers (fst ': rest ) a ) (Headers (Header h v ': fst ': rest ) a ) where
171
+ => AddHeader mods h v (Headers (fst ': rest ) a ) (Headers (Header' mods h v ': fst ': rest ) a ) where
168
172
addOptionalHeader hdr (Headers resp heads) = Headers resp (HCons hdr heads)
169
173
170
174
-- In this instance, 'a' parameter is decorated with a Header.
171
- instance {-# OVERLAPPABLE #-} ( KnownSymbol h , ToHttpApiData v , new ~ Headers '[Header h v ] a )
172
- => AddHeader h v a new where
175
+ instance {-# OVERLAPPABLE #-} ( KnownSymbol h , ToHttpApiData v , new ~ Headers '[Header' mods h v ] a )
176
+ => AddHeader mods h v a new where
173
177
addOptionalHeader hdr resp = Headers resp (HCons hdr HNil )
174
178
175
179
-- Instances to decorate all responses in a 'Union' with headers. The functional
176
180
-- dependencies force us to consider singleton lists as the base case in the
177
181
-- recursion (it is impossible to determine h and v otherwise from old / new
178
182
-- responses if the list is empty).
179
- instance (AddHeader h v old new ) => AddHeader h v (Union '[old ]) (Union '[new ]) where
183
+ instance (AddHeader mods h v old new ) => AddHeader mods h v (Union '[old ]) (Union '[new ]) where
180
184
addOptionalHeader hdr resp =
181
185
SOP. Z $ SOP. I $ addOptionalHeader hdr $ SOP. unI $ SOP. unZ $ resp
182
186
183
187
instance
184
- ( AddHeader h v old new , AddHeader h v (Union oldrest ) (Union newrest )
188
+ ( AddHeader mods h v old new , AddHeader mods h v (Union oldrest ) (Union newrest )
185
189
-- This ensures that the remainder of the response list is _not_ empty
186
190
-- It is necessary to prevent the two instances for union types from
187
191
-- overlapping.
188
192
, oldrest ~ (a ': as ), newrest ~ (b ': bs ))
189
- => AddHeader h v (Union (old ': (a ': as ))) (Union (new ': (b ': bs ))) where
193
+ => AddHeader mods h v (Union (old ': (a ': as ))) (Union (new ': (b ': bs ))) where
190
194
addOptionalHeader hdr resp = case resp of
191
195
SOP. Z (SOP. I rHead) -> SOP. Z $ SOP. I $ addOptionalHeader hdr rHead
192
196
SOP. S rOthers -> SOP. S $ addOptionalHeader hdr rOthers
@@ -211,21 +215,29 @@ instance
211
215
-- Note that while in your handlers type annotations are not required, since
212
216
-- the type can be inferred from the API type, in other cases you may find
213
217
-- yourself needing to add annotations.
214
- addHeader :: AddHeader h v orig new => v -> orig -> new
218
+ addHeader :: AddHeader '[ Optional , Strict ] h v orig new => v -> orig -> new
215
219
addHeader = addOptionalHeader . Header
216
220
221
+ -- | Same as 'addHeader' but works with `Header'`, so it's possible to use any @mods@.
222
+ addHeader' :: AddHeader mods h v orig new => v -> orig -> new
223
+ addHeader' = addOptionalHeader . Header
224
+
217
225
-- | Deliberately do not add a header to a value.
218
226
--
219
227
-- >>> let example1 = noHeader "hi" :: Headers '[Header "someheader" Int] String
220
228
-- >>> getHeaders example1
221
229
-- []
222
- noHeader :: AddHeader h v orig new => orig -> new
230
+ noHeader :: AddHeader '[ Optional , Strict ] h v orig new => orig -> new
223
231
noHeader = addOptionalHeader MissingHeader
224
232
233
+ -- | Same as 'noHeader' but works with `Header'`, so it's possible to use any @mods@.
234
+ noHeader' :: AddHeader mods h v orig new => orig -> new
235
+ noHeader' = addOptionalHeader MissingHeader
236
+
225
237
class HasResponseHeader h a headers where
226
238
hlistLookupHeader :: HList headers -> ResponseHeader h a
227
239
228
- instance {-# OVERLAPPING #-} HasResponseHeader h a (Header h a ': rest ) where
240
+ instance {-# OVERLAPPING #-} HasResponseHeader h a (Header' mods h a ': rest ) where
229
241
hlistLookupHeader (HCons ha _) = ha
230
242
231
243
instance {-# OVERLAPPABLE #-} (HasResponseHeader h a rest ) => HasResponseHeader h a (first ': rest ) where
0 commit comments