@@ -39,11 +39,12 @@ type RetryFunc func(ctx context.Context, httpRes *http.Response, err error) erro
39
39
40
40
// Request represents a JSON/REST HTTP request.
41
41
type Request [Req any , Res any ] struct {
42
- uri string
43
- method string
44
- req Req
45
- marshalRequest MarshalJSONFunc [Req ]
46
- unmarshalResponse UnmarshalJSONFunc [Res ]
42
+ uri string
43
+ method string
44
+ req Req
45
+ ignoreResponseBody bool
46
+ marshalRequest MarshalJSONFunc [Req ]
47
+ unmarshalResponse UnmarshalJSONFunc [Res ]
47
48
}
48
49
49
50
// RequestOpt is a function that configures a Request.
@@ -58,7 +59,7 @@ type UnmarshalJSONFunc[T any] func(httpRes *http.Response, val *T) error
58
59
// Response represents a JSON/REST HTTP response.
59
60
type Response [T any ] struct {
60
61
// Res is the value decoded from the response body.
61
- // Res will be the default value of T if StatusCode==http.StatusNoContent, or if T is Void or *Void .
62
+ // Res will be the default value of T if StatusCode==http.StatusNoContent, or if the response body is ignored .
62
63
Res T
63
64
64
65
// StatusCode is the HTTP response status code.
@@ -68,15 +69,12 @@ type Response[T any] struct {
68
69
Status string
69
70
}
70
71
71
- // Void can be used as a request type to indicate that the request has no body,
72
- // or as a response type to indicate that the response has no body.
73
- type Void struct {}
74
-
75
72
type httpError string
76
73
77
74
var _ error = httpError ("" )
78
75
79
76
// New creates a new Client with the given options.
77
+ //
80
78
// The default options are: slog.Default() as the logger, http.DefaultClient as the HTTP client,
81
79
// request timeout of 30s, maximum number of attempts of 5, gobackoff.New() as the backoff,
82
80
// and a retry function that returns an error if the HTTP response status code is http.StatusBadRequest.
@@ -214,18 +212,25 @@ func WithUnmarshalResponseFunc[Req any, Res any](fun UnmarshalJSONFunc[Res]) Req
214
212
}
215
213
}
216
214
217
- // Do executes req and returns the response.
215
+ // WithIgnoreResponseBody configures a Request to ignore the response body, regardless of status code.
216
+ // The response body will always be ignored if the status code is http.StatusNoContent.
217
+ func WithIgnoreResponseBody [Req any , Res any ]() RequestOpt [Req , Res ] {
218
+ return func (req * Request [Req , Res ]) {
219
+ req .ignoreResponseBody = true
220
+ }
221
+ }
222
+
223
+ // Do executes req with client and returns the response.
224
+ //
225
+ // If the request data is nil, the request will be made without a body.
226
+ // If the response status code is http.StatusNoContent or the response body should be ignored,
227
+ // Response.Res will be the default value of Res.
218
228
//
219
229
// If an HTTP request fails, it is retried using backoff according to the retry function, up to the
220
230
// maximum number of attempts.
221
231
// If the context is canceled, or if the retry function returns a non-nil error, Do stops and returns
222
232
// a gobackoff.AbortError.
223
233
//
224
- // If Req is Void or *Void, the request will be made without a body.
225
- // If the response status code is http.StatusNoContent, Response.Res will be the default value of Res.
226
- // If Res is Void or *Void, the response body will be ignored, and Response.Res will be the default
227
- // value of Res.
228
- //
229
234
// Do is safe to call concurrently with the same Request.
230
235
func Do [Req any , Res any ](ctx context.Context , client * Client , req * Request [Req , Res ]) (* Response [Res ], error ) {
231
236
var res * Response [Res ]
@@ -296,11 +301,7 @@ func do[Req any, Res any](ctx context.Context, client *Client, req *Request[Req,
296
301
func newHTTPRequest [Req any , Res any ](ctx context.Context , client * Client , req * Request [Req , Res ]) (* http.Request , error ) {
297
302
var jsonReqData io.Reader = http .NoBody
298
303
299
- switch any (req .req ).(type ) {
300
- case Void :
301
- case * Void :
302
-
303
- default :
304
+ if any (req .req ) != nil {
304
305
buf := bytes.Buffer {}
305
306
306
307
if err := req .marshalRequest (& buf , req .req ); err != nil {
@@ -328,24 +329,14 @@ func newHTTPRequest[Req any, Res any](ctx context.Context, client *Client, req *
328
329
}
329
330
330
331
func response [Req any , Res any ](httpRes * http.Response , req * Request [Req , Res ]) (* Response [Res ], error ) {
331
- if httpRes .StatusCode == http .StatusNoContent {
332
+ if httpRes .StatusCode == http .StatusNoContent || req . ignoreResponseBody {
332
333
return & Response [Res ]{
333
334
StatusCode : httpRes .StatusCode ,
334
335
Status : httpRes .Status ,
335
336
}, nil
336
337
}
337
338
338
339
var jsonRes Res
339
-
340
- switch any (jsonRes ).(type ) {
341
- case Void :
342
- case * Void :
343
- return & Response [Res ]{
344
- StatusCode : httpRes .StatusCode ,
345
- Status : httpRes .Status ,
346
- }, nil
347
- }
348
-
349
340
if err := req .unmarshalResponse (httpRes , & jsonRes ); err != nil {
350
341
return nil , fmt .Errorf ("decode response: %w" , err )
351
342
}
0 commit comments