37public record
Route(String pattern, Set<String> allowedMethods) {
40 allowedMethods = normalizeMethods(allowedMethods);
43 public static Route of(
final String pattern,
final Set<String> methods) {
44 return new Route(pattern, methods);
47 public boolean allows(
final String method) {
48 return allowedMethods.contains(method.toUpperCase());
51 public Optional<Map<String, String>> match(
final String relPath) {
52 if (
"/**".equals(pattern)) {
53 return Optional.of(Map.of());
56 final var patternSegments = split(pattern);
57 final var pathSegments = split(relPath);
58 if (patternSegments.size() != pathSegments.size()) {
59 return Optional.empty();
62 final var params =
new LinkedHashMap<String, String>();
63 for (
int i = 0; i < patternSegments.size(); i++) {
64 final var expected = patternSegments.get(i);
65 final var actual = pathSegments.get(i);
67 if (expected.startsWith(
"{") && expected.endsWith(
"}")) {
68 final var key = expected.substring(1, expected.length() - 1);
69 params.put(key, actual);
72 if (!expected.equals(actual)) {
73 return Optional.empty();
77 return Optional.of(params);
80 private static List<String> split(
final String path) {
81 final var cleaned = path.startsWith(
"/") ? path.substring(1) : path;
82 if (cleaned.isEmpty()) {
85 return Arrays.asList(cleaned.split(
"/"));
88 private static Set<String> normalizeMethods(
final Set<String> methods) {
89 final var out =
new LinkedHashSet<String>();
90 if (methods !=
null) {
91 for (
final var method : methods) {
92 if (method !=
null && !method.isBlank()) {
93 out.add(method.trim().toUpperCase());
97 return Set.copyOf(out);