Skip to content

Commit b6982df

Browse files
committed
refactor: consolidate matcher handling and update dispatch API
1 parent 4e0db9b commit b6982df

6 files changed

Lines changed: 99 additions & 78 deletions

File tree

docs/USAGE.md

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -52,25 +52,26 @@ For paths with many query-parameter variants, `Dispatch` groups them into a
5252
single block — clearer than scattering individual calls across many lines:
5353

5454
```go
55-
import "github.com/mallardduck/teapot-router/pkg/dispatch"
56-
57-
r.Dispatch("GET", "/{bucket}", func(d *teapot.DispatchBuilder) {
55+
r.Dispatch("GET", "/{bucket}", func(d *teapot.DispatchBuilder, m teapot.Matchers) {
5856
d.Default(listObjects).Name("bucket.list").Action("s3:ListBucket")
59-
d.When(dispatch.QueryEquals("list-type", "2")).Do(listObjectsV2).Name("bucket.list-v2").Action("s3:ListObjectsV2")
60-
d.When(dispatch.QueryExists("acl")).Do(getBucketAcl).Name("bucket.acl").Action("s3:GetBucketAcl")
61-
d.When(dispatch.QueryExists("versioning")).Do(getVersioning).Name("bucket.versioning").Action("s3:GetBucketVersioning")
57+
d.When(m.QueryEquals("list-type", "2")).Do(listObjectsV2).Name("bucket.list-v2").Action("s3:ListObjectsV2")
58+
d.When(m.QueryExists("acl")).Do(getBucketAcl).Name("bucket.acl").Action("s3:GetBucketAcl")
59+
d.When(m.QueryExists("versioning")).Do(getVersioning).Name("bucket.versioning").Action("s3:GetBucketVersioning")
6260
})
6361
```
6462

6563
- `Default(handler)` — the fallback, matches when no other route's conditions match
6664
- `When(matchers...).Do(handler)` — a conditional route; all matchers must match (AND)
6765
- `.Name()`, `.Action()`, `.With()` — same fluent chain as the scattered API, available on both `Default` and `When` routes
6866

69-
Matcher constructors live in `pkg/dispatch`:
67+
The `m` parameter (type `teapot.Matchers`) exposes all built-in matcher constructors
68+
so that the `dispatch` package does not need to be imported separately:
7069

71-
- `dispatch.QueryExists("key")` — matches if the query param is present (any value)
72-
- `dispatch.QueryEquals("key", "value")` — matches if the query param equals a specific value
73-
- Multiple matchers in one `When` are ANDed: `When(dispatch.QueryExists("partNumber"), dispatch.QueryExists("uploadId"))`
70+
- `m.QueryExists("key")` — matches if the query param is present (any value)
71+
- `m.QueryEquals("key", "value")` — matches if the query param equals a specific value
72+
- `m.HeaderExists("key")` — matches if the header is present with a non-empty value
73+
- `m.HeaderEquals("key", "value")` — matches if the header equals a specific value
74+
- Multiple matchers in one `When` are ANDed: `When(m.QueryExists("partNumber"), m.QueryExists("uploadId"))`
7475

7576
Both styles coexist in the same router — use `Dispatch` where you have a dense
7677
cluster of variants on one path, and the fluent style elsewhere.
@@ -251,13 +252,11 @@ The GET bucket operations above could equivalently use `Dispatch` to group all
251252
the query variants explicitly:
252253

253254
```go
254-
import "github.com/mallardduck/teapot-router/pkg/dispatch"
255-
256255
// Inside the NamedGroup("/{bucket}", ...) callback:
257-
r.Dispatch("GET", "", func(d *teapot.DispatchBuilder) {
256+
r.Dispatch("GET", "", func(d *teapot.DispatchBuilder, m teapot.Matchers) {
258257
d.Default(listObjects).Name("list").Action("s3:ListBucket")
259-
d.When(dispatch.QueryExists("acl")).Do(getBucketAcl).Name("acl.get").Action("s3:GetBucketAcl")
260-
d.When(dispatch.QueryExists("versions")).Do(listObjectVersions).Name("versions").Action("s3:ListBucketVersions")
258+
d.When(m.QueryExists("acl")).Do(getBucketAcl).Name("acl.get").Action("s3:GetBucketAcl")
259+
d.When(m.QueryExists("versions")).Do(listObjectVersions).Name("versions").Action("s3:ListBucketVersions")
261260
})
262261
```
263262

examples/routes-cli/main.go

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,21 @@ func setupRoutes() *teapot.Router {
128128
router.QueryPOST("/{bucket}", deleteObjects).Query("delete").Name("s3.bucket.delete-objects").Action("api:s3:DeleteObjects")
129129

130130
// ==================== OBJECT OPERATIONS ====================
131-
// Direct routes for operations without query params
132131
router.GET("/{bucket}/{key:.*}", getObject).Name("s3.object.get").Action("api:s3:GetObject")
133-
router.PUT("/{bucket}/{key:.*}", putObject).Name("s3.object.put").Action("api:s3:PutObject")
134132
router.DELETE("/{bucket}/{key:.*}", deleteObject).Name("s3.object.delete").Action("api:s3:DeleteObject")
135133
router.HEAD("/{bucket}/{key:.*}", headObject).Name("s3.object.head").Action("api:s3:HeadObject")
136-
// Note: CopyObject uses PUT /{bucket}/{key} with x-amz-copy-source header.
137-
// The putObject handler detects this header and can adjust action context
138-
// for logging/metrics (e.g., override to "api:s3:CopyObject")
134+
135+
// PUT /{bucket}/{key} dispatches on X-Amz-Copy-Source header.
136+
// UploadPart / UploadPartCopy also live here: same method+path, and header
137+
// presence distinguishes the copy variant. The remaining QueryPUT routes
138+
// below (acl, tagging, …) are added to this same dispatcher automatically.
139+
router.Dispatch("PUT", "/{bucket}/{key:.*}", func(d *teapot.DispatchBuilder, m teapot.Matchers) {
140+
d.Default(putObject).Name("s3.object.put").Action("api:s3:PutObject")
141+
d.When(m.HeaderExists("X-Amz-Copy-Source")).Do(copyObject).Name("s3.object.copy").Action("api:s3:CopyObject")
142+
143+
d.When(m.QueryExists("partNumber"), m.QueryExists("uploadId")).Do(uploadPart).Name("s3.multipart.upload-part").Action("api:s3:UploadPart")
144+
d.When(m.QueryExists("partNumber"), m.QueryExists("uploadId"), m.HeaderExists("X-Amz-Copy-Source")).Do(uploadPartCopy).Name("s3.multipart.upload-part-copy").Action("api:s3:UploadPartCopy")
145+
})
139146

140147
// Query-based object operations
141148
router.QueryGET("/{bucket}/{key:.*}", getObjectAcl).Query("acl").Name("s3.object.get-acl").Action("api:s3:GetObjectAcl")
@@ -161,10 +168,7 @@ func setupRoutes() *teapot.Router {
161168
// Initiate multipart upload
162169
router.QueryPOST("/{bucket}/{key:.*}", createMultipartUpload).Query("uploads").Name("s3.multipart.create").Action("api:s3:CreateMultipartUpload")
163170

164-
// Upload part (requires both partNumber and uploadId query params)
165-
// Note: UploadPartCopy uses the same route with x-amz-copy-source header.
166-
// The uploadPart handler detects this and can adjust action context accordingly.
167-
router.QueryPUT("/{bucket}/{key:.*}", uploadPart).Query("partNumber").Query("uploadId").Name("s3.multipart.upload-part").Action("api:s3:UploadPart")
171+
// UploadPart / UploadPartCopy are in the PUT dispatch block above.
168172

169173
// Complete multipart upload
170174
router.QueryPOST("/{bucket}/{key:.*}", completeMultipartUpload).Query("uploadId").Name("s3.multipart.complete").Action("api:s3:CompleteMultipartUpload")
@@ -224,6 +228,7 @@ func getBucketAnalyticsConfiguration(_ http.ResponseWriter, _ *http.Request) {}
224228
// Object operations
225229
func getObject(_ http.ResponseWriter, _ *http.Request) {}
226230
func putObject(_ http.ResponseWriter, _ *http.Request) {}
231+
func copyObject(_ http.ResponseWriter, _ *http.Request) {}
227232
func deleteObject(_ http.ResponseWriter, _ *http.Request) {}
228233
func headObject(_ http.ResponseWriter, _ *http.Request) {}
229234
func getObjectAcl(_ http.ResponseWriter, _ *http.Request) {}
@@ -242,6 +247,7 @@ func getObjectTorrent(_ http.ResponseWriter, _ *http.Request) {}
242247
// Multipart upload operations
243248
func createMultipartUpload(_ http.ResponseWriter, _ *http.Request) {}
244249
func uploadPart(_ http.ResponseWriter, _ *http.Request) {}
250+
func uploadPartCopy(_ http.ResponseWriter, _ *http.Request) {}
245251
func completeMultipartUpload(_ http.ResponseWriter, _ *http.Request) {}
246252
func abortMultipartUpload(_ http.ResponseWriter, _ *http.Request) {}
247253
func listParts(_ http.ResponseWriter, _ *http.Request) {}

pkg/teapot/dispatch.go

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,27 @@ import (
88
"github.com/mallardduck/teapot-router/pkg/dispatch"
99
)
1010

11+
// Matchers provides convenient access to all built-in matcher constructors.
12+
// A zero-value instance is passed as the second argument to the Dispatch
13+
// callback so that the dispatch package does not need to be imported.
14+
type Matchers struct{}
15+
16+
// QueryExists returns a Matcher that matches when the given query parameter is present.
17+
func (Matchers) QueryExists(key string) dispatch.Matcher { return dispatch.QueryExists(key) }
18+
19+
// QueryEquals returns a Matcher that matches when the given query parameter equals value.
20+
func (Matchers) QueryEquals(key, value string) dispatch.Matcher {
21+
return dispatch.QueryEquals(key, value)
22+
}
23+
24+
// HeaderExists returns a Matcher that matches when the given header is present with a non-empty value.
25+
func (Matchers) HeaderExists(key string) dispatch.Matcher { return dispatch.HeaderExists(key) }
26+
27+
// HeaderEquals returns a Matcher that matches when the given header equals value.
28+
func (Matchers) HeaderEquals(key, value string) dispatch.Matcher {
29+
return dispatch.HeaderEquals(key, value)
30+
}
31+
1132
// DispatchBuilder configures routes for a Dispatch group.
1233
// Each route shares the same method and pattern but is distinguished by
1334
// query parameter conditions (or other Matchers).
@@ -34,18 +55,18 @@ type DispatchRoute struct {
3455
}
3556

3657
// Dispatch registers a group of routes on the same method+pattern, distinguished
37-
// by query parameters (or other Matchers). The callback fn configures routes via
38-
// a DispatchBuilder. This is the explicit, grouped alternative to scattered
39-
// QueryGET/QueryPOST calls.
58+
// by query parameters (or other Matchers). The callback receives a DispatchBuilder
59+
// for route configuration and a Matchers value for creating matchers without
60+
// importing the dispatch package directly.
4061
//
4162
// Example:
4263
//
43-
// r.Dispatch("GET", "/{bucket}", func(d *teapot.DispatchBuilder) {
64+
// r.Dispatch("GET", "/{bucket}", func(d *teapot.DispatchBuilder, m teapot.Matchers) {
4465
// d.Default(listV1).Name("s3.bucket.list-v1").Action("api:s3:ListObjects")
45-
// d.When(dispatch.QueryEquals("list-type", "2")).Do(listV2).Name("s3.bucket.list-v2").Action("api:s3:ListObjectsV2")
46-
// d.When(dispatch.QueryExists("location")).Do(getLocation).Name("s3.bucket.get-location")
66+
// d.When(m.QueryEquals("list-type", "2")).Do(listV2).Name("s3.bucket.list-v2").Action("api:s3:ListObjectsV2")
67+
// d.When(m.QueryExists("location")).Do(getLocation).Name("s3.bucket.get-location")
4768
// })
48-
func (r *Router) Dispatch(method, pattern string, fn func(d *DispatchBuilder)) {
69+
func (r *Router) Dispatch(method, pattern string, fn func(d *DispatchBuilder, m Matchers)) {
4970
fullPattern := r.pathPrefix + pattern
5071
chiPattern, wildcardParams := core.TranslatePattern(fullPattern)
5172
dispatcherKey := method + ":" + chiPattern
@@ -65,7 +86,7 @@ func (r *Router) Dispatch(method, pattern string, fn func(d *DispatchBuilder)) {
6586
chiPattern: chiPattern,
6687
}
6788

68-
fn(db)
89+
fn(db, Matchers{})
6990

7091
// Validate that every route has a handler set
7192
for _, cfg := range db.routes {

pkg/teapot/dispatch_test.go

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,19 @@ import (
77

88
"github.com/stretchr/testify/assert"
99
"github.com/stretchr/testify/require"
10-
11-
"github.com/mallardduck/teapot-router/pkg/dispatch"
1210
)
1311

1412
func TestDispatchBasicRouting(t *testing.T) {
1513
r := New()
1614

17-
r.Dispatch("GET", "/items", func(d *DispatchBuilder) {
15+
r.Dispatch("GET", "/items", func(d *DispatchBuilder, m Matchers) {
1816
d.Default(func(w http.ResponseWriter, _ *http.Request) {
1917
_, _ = w.Write([]byte("list-all"))
2018
})
21-
d.When(dispatch.QueryEquals("type", "active")).Do(func(w http.ResponseWriter, _ *http.Request) {
19+
d.When(m.QueryEquals("type", "active")).Do(func(w http.ResponseWriter, _ *http.Request) {
2220
_, _ = w.Write([]byte("list-active"))
2321
})
24-
d.When(dispatch.QueryExists("search")).Do(func(w http.ResponseWriter, _ *http.Request) {
22+
d.When(m.QueryExists("search")).Do(func(w http.ResponseWriter, _ *http.Request) {
2523
_, _ = w.Write([]byte("search"))
2624
})
2725
})
@@ -52,13 +50,13 @@ func TestDispatchNameAndAction(t *testing.T) {
5250

5351
var capturedName, capturedAction string
5452

55-
r.Dispatch("GET", "/things", func(d *DispatchBuilder) {
53+
r.Dispatch("GET", "/things", func(d *DispatchBuilder, m Matchers) {
5654
d.Default(func(_ http.ResponseWriter, req *http.Request) {
5755
capturedName = GetRouteName(req)
5856
capturedAction = GetAction(req)
5957
}).Name("things.list").Action("api:things:List")
6058

61-
d.When(dispatch.QueryEquals("view", "detail")).Do(func(_ http.ResponseWriter, req *http.Request) {
59+
d.When(m.QueryEquals("view", "detail")).Do(func(_ http.ResponseWriter, req *http.Request) {
6260
capturedName = GetRouteName(req)
6361
capturedAction = GetAction(req)
6462
}).Name("things.detail").Action("api:things:Detail")
@@ -86,9 +84,9 @@ func TestDispatchNameAndAction(t *testing.T) {
8684
func TestDispatchURLGeneration(t *testing.T) {
8785
r := New()
8886

89-
r.Dispatch("GET", "/users/{id}", func(d *DispatchBuilder) {
87+
r.Dispatch("GET", "/users/{id}", func(d *DispatchBuilder, m Matchers) {
9088
d.Default(func(_ http.ResponseWriter, _ *http.Request) {}).Name("user.show")
91-
d.When(dispatch.QueryExists("edit")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("user.edit")
89+
d.When(m.QueryExists("edit")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("user.edit")
9290
})
9391

9492
url, err := r.URL("user.show", "id", "42")
@@ -103,10 +101,10 @@ func TestDispatchURLGeneration(t *testing.T) {
103101
func TestDispatchRouteListing(t *testing.T) {
104102
r := New()
105103

106-
r.Dispatch("GET", "/api/items", func(d *DispatchBuilder) {
104+
r.Dispatch("GET", "/api/items", func(d *DispatchBuilder, m Matchers) {
107105
d.Default(func(_ http.ResponseWriter, _ *http.Request) {}).Name("api.items.list").Action("api:Items:List")
108-
d.When(dispatch.QueryEquals("status", "active")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("api.items.active").Action("api:Items:ListActive")
109-
d.When(dispatch.QueryExists("search")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("api.items.search").Action("api:Items:Search")
106+
d.When(m.QueryEquals("status", "active")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("api.items.active").Action("api:Items:ListActive")
107+
d.When(m.QueryExists("search")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("api.items.search").Action("api:Items:Search")
110108
})
111109

112110
routes := r.Routes()
@@ -142,13 +140,13 @@ func TestDispatchWildcardParams(t *testing.T) {
142140

143141
var capturedBucket, capturedKey string
144142

145-
r.Dispatch("GET", "/{bucket}/{key:.*}", func(d *DispatchBuilder) {
143+
r.Dispatch("GET", "/{bucket}/{key:.*}", func(d *DispatchBuilder, m Matchers) {
146144
d.Default(func(w http.ResponseWriter, req *http.Request) {
147145
capturedBucket = URLParam(req, "bucket")
148146
capturedKey = URLParam(req, "key")
149147
_, _ = w.Write([]byte("object"))
150148
})
151-
d.When(dispatch.QueryExists("acl")).Do(func(w http.ResponseWriter, req *http.Request) {
149+
d.When(m.QueryExists("acl")).Do(func(w http.ResponseWriter, req *http.Request) {
152150
capturedBucket = URLParam(req, "bucket")
153151
capturedKey = URLParam(req, "key")
154152
_, _ = w.Write([]byte("acl"))
@@ -187,7 +185,7 @@ func TestDispatchMiddleware(t *testing.T) {
187185
})
188186
}
189187

190-
r.Dispatch("GET", "/mw-test", func(d *DispatchBuilder) {
188+
r.Dispatch("GET", "/mw-test", func(d *DispatchBuilder, m Matchers) {
191189
d.Default(func(w http.ResponseWriter, _ *http.Request) {
192190
_, _ = w.Write([]byte("ok"))
193191
}).With(mw)
@@ -206,7 +204,7 @@ func TestDispatchConflictWithDirect(t *testing.T) {
206204
r.GET("/conflict", func(_ http.ResponseWriter, _ *http.Request) {})
207205

208206
assert.Panics(t, func() {
209-
r.Dispatch("GET", "/conflict", func(d *DispatchBuilder) {
207+
r.Dispatch("GET", "/conflict", func(d *DispatchBuilder, m Matchers) {
210208
d.Default(func(_ http.ResponseWriter, _ *http.Request) {})
211209
})
212210
})
@@ -217,7 +215,7 @@ func TestDispatchConflictWithExistingDispatcher(t *testing.T) {
217215
r.QueryGET("/conflict", func(_ http.ResponseWriter, _ *http.Request) {}).Query("foo")
218216

219217
assert.Panics(t, func() {
220-
r.Dispatch("GET", "/conflict", func(d *DispatchBuilder) {
218+
r.Dispatch("GET", "/conflict", func(d *DispatchBuilder, m Matchers) {
221219
d.Default(func(_ http.ResponseWriter, _ *http.Request) {})
222220
})
223221
})
@@ -227,8 +225,8 @@ func TestDispatchMissingHandler(t *testing.T) {
227225
r := New()
228226

229227
assert.Panics(t, func() {
230-
r.Dispatch("GET", "/missing", func(d *DispatchBuilder) {
231-
d.When(dispatch.QueryExists("foo")) // No .Do() call — handler is nil
228+
r.Dispatch("GET", "/missing", func(d *DispatchBuilder, m Matchers) {
229+
d.When(m.QueryExists("foo")) // No .Do() call — handler is nil
232230
})
233231
})
234232
}
@@ -237,9 +235,9 @@ func TestDispatchDuplicateName(t *testing.T) {
237235
r := New()
238236

239237
assert.Panics(t, func() {
240-
r.Dispatch("GET", "/dup-name", func(d *DispatchBuilder) {
238+
r.Dispatch("GET", "/dup-name", func(d *DispatchBuilder, m Matchers) {
241239
d.Default(func(_ http.ResponseWriter, _ *http.Request) {}).Name("same-name")
242-
d.When(dispatch.QueryExists("x")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("same-name")
240+
d.When(m.QueryExists("x")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("same-name")
243241
})
244242
})
245243
}
@@ -249,7 +247,7 @@ func TestDispatchNameConflictWithFluentAPI(t *testing.T) {
249247
r.GET("/other", func(_ http.ResponseWriter, _ *http.Request) {}).Name("taken")
250248

251249
assert.Panics(t, func() {
252-
r.Dispatch("GET", "/different-path", func(d *DispatchBuilder) {
250+
r.Dispatch("GET", "/different-path", func(d *DispatchBuilder, m Matchers) {
253251
d.Default(func(_ http.ResponseWriter, _ *http.Request) {}).Name("taken")
254252
})
255253
})
@@ -264,11 +262,11 @@ func TestDispatchCoexistsWithFluentAPI(t *testing.T) {
264262
}).Name("fluent.route")
265263

266264
// Dispatch-style route on a different path
267-
r.Dispatch("GET", "/grouped", func(d *DispatchBuilder) {
265+
r.Dispatch("GET", "/grouped", func(d *DispatchBuilder, m Matchers) {
268266
d.Default(func(w http.ResponseWriter, _ *http.Request) {
269267
_, _ = w.Write([]byte("grouped-default"))
270268
}).Name("grouped.default")
271-
d.When(dispatch.QueryExists("special")).Do(func(w http.ResponseWriter, _ *http.Request) {
269+
d.When(m.QueryExists("special")).Do(func(w http.ResponseWriter, _ *http.Request) {
272270
_, _ = w.Write([]byte("grouped-special"))
273271
}).Name("grouped.special")
274272
})
@@ -303,7 +301,7 @@ func TestDispatchWithNamedGroup(t *testing.T) {
303301
r := New()
304302

305303
r.NamedGroup("/api", "api", func(sub *Router) {
306-
sub.Dispatch("GET", "/things", func(d *DispatchBuilder) {
304+
sub.Dispatch("GET", "/things", func(d *DispatchBuilder, m Matchers) {
307305
d.Default(func(w http.ResponseWriter, _ *http.Request) {
308306
_, _ = w.Write([]byte("things"))
309307
}).Name("list").Action("api:Things:List")

pkg/teapot/internal_coverage_test.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import (
1010

1111
"github.com/stretchr/testify/assert"
1212
"github.com/stretchr/testify/require"
13-
14-
"github.com/mallardduck/teapot-router/pkg/dispatch"
1513
)
1614

1715
// --- SetDebugLog / debugLogf (0% / 50%) ---
@@ -219,10 +217,10 @@ func TestURLParamsNilRouteContext(t *testing.T) {
219217
func TestRoutesPopulatesHeaderParams(t *testing.T) {
220218
r := New()
221219

222-
r.Dispatch("GET", "/header-info", func(d *DispatchBuilder) {
220+
r.Dispatch("GET", "/header-info", func(d *DispatchBuilder, m Matchers) {
223221
d.Default(func(_ http.ResponseWriter, _ *http.Request) {}).Name("hi.default")
224-
d.When(dispatch.HeaderExists("X-Custom")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("hi.exists")
225-
d.When(dispatch.HeaderEquals("Content-Type", "application/json")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("hi.value")
222+
d.When(m.HeaderExists("X-Custom")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("hi.exists")
223+
d.When(m.HeaderEquals("Content-Type", "application/json")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("hi.value")
226224
})
227225

228226
routes := r.Routes()
@@ -315,7 +313,7 @@ func TestFormatRoutesCompactWithQueryAndHeaders(t *testing.T) {
315313
func TestDispatchRouteUnnamed(t *testing.T) {
316314
r := New()
317315

318-
r.Dispatch("GET", "/unnamed", func(d *DispatchBuilder) {
316+
r.Dispatch("GET", "/unnamed", func(d *DispatchBuilder, m Matchers) {
319317
d.Default(func(w http.ResponseWriter, _ *http.Request) {
320318
_, _ = w.Write([]byte("unnamed-ok"))
321319
})
@@ -334,9 +332,9 @@ func TestDispatchRouteUnnamed(t *testing.T) {
334332
func TestNewListRoutesHandlerHTMLWithHeaders(t *testing.T) {
335333
r := New()
336334

337-
r.Dispatch("GET", "/hht", func(d *DispatchBuilder) {
335+
r.Dispatch("GET", "/hht", func(d *DispatchBuilder, m Matchers) {
338336
d.Default(func(_ http.ResponseWriter, _ *http.Request) {}).Name("hht.default")
339-
d.When(dispatch.HeaderEquals("X-Copy", "source")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("hht.copy")
337+
d.When(m.HeaderEquals("X-Copy", "source")).Do(func(_ http.ResponseWriter, _ *http.Request) {}).Name("hht.copy")
340338
})
341339

342340
handler := NewListRoutesHandler(r, nil)

0 commit comments

Comments
 (0)