aboutsummaryrefslogtreecommitdiff
path: root/pathrouter/choice.go
blob: 09026b850d1fbe358199fcd77976dae98214e184 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// SPDX-FileCopyrightText: 2026 Stefan Majewsky <majewsky@gmx.net>
// SPDX-License-Identifier: Apache-2.0

package pathrouter

import (
	"slices"

	. "go.xyrillian.de/gg/option"
	"go.xyrillian.de/gg/options"
)

// Choice is a [Matcher] that accepts all subpaths that are accepted by any of its contained matchers.
// If multiple matchers accept the requested subpath, the first match wins.
//
// Choice is used to specify different matchers for different subpaths,
// as illustrated in the example in the package docstring.
func Choice(matchers ...Matcher) Matcher {
	downcasted := make([]realMatcher, len(matchers))
	for idx, matcher := range matchers {
		downcasted[idx] = matcher.downcast()
	}
	return choice(downcasted)
}

func choice(matchers []realMatcher) Matcher {
	if len(matchers) == 0 {
		panic("Choice() called without any matchers")
	}

	var (
		minLengths     = make([]int, len(matchers))
		maxLengths     = make([]Option[int], len(matchers))
		maxLengthIsInf = false
	)
	for idx, m := range matchers {
		minLengths[idx] = m.minLength
		maxLengths[idx] = m.maxLength
		if m.maxLength.IsNone() {
			maxLengthIsInf = true
		}
	}
	maxLength := None[int]()
	if !maxLengthIsInf {
		maxLength = options.Max(maxLengths...)
	}

	return realMatcher{
		minLength: slices.Min(minLengths),
		maxLength: maxLength,
		accept: func(path []string, vars map[string]string) HandlerFunc {
			for _, m := range matchers {
				hf := m.accept(path, vars)
				if hf != nil {
					return hf
				}
			}
			return nil
		},
	}
}