feat(smalltrip,multiplexer): pattern options and rules multiplexer

This commit is contained in:
Guz
2025-08-08 19:17:57 -03:00
parent dfebde8c4e
commit cecf19693f

168
multiplexer/patterns.go Normal file
View File

@@ -0,0 +1,168 @@
// Copyright 2025-present Gustavo "Guz" L. de Mello
// Copyright 2025-present The Lored.dev Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package multiplexer
import (
"fmt"
"net/http"
"slices"
"strings"
)
func WithPatternsOptions(mux Multiplexer, opts ...PatternOption) Multiplexer {
return &patternsMux{opts: opts, Multiplexer: mux}
}
func AddTrailingSlash() PatternOption {
return func(s string) string {
if strings.HasSuffix(s, "...}") {
return s
}
// If the pattern is /image.html{$}, we modify it to be /image.html/{$}
if strings.HasSuffix(s, "{$}") && !strings.HasSuffix(s, "/{$}") {
return strings.TrimSuffix(s, "{$}") + "/{$}"
}
if !strings.HasSuffix(s, "/") {
return s + "/"
}
return s
}
}
func RemoveTrailingSlash() PatternOption {
return func(s string) string {
s = strings.TrimSuffix(s, "/")
if strings.HasSuffix(s, "/{$}") {
return strings.TrimSuffix(s, "/{$}") + "{$}"
}
return s
}
}
func AddStrictEnd() PatternOption {
return func(s string) string {
if strings.HasSuffix(s, "{$}") {
return s
}
return s + "{$}"
}
}
func RemoteStrictEnd() PatternOption {
return func(s string) string {
return strings.TrimSuffix(s, "{$}")
}
}
type PatternOption func(string) string
func WithPatternRules(mux Multiplexer, rules ...PatternRule) Multiplexer {
opts := make([]PatternOption, len(rules))
for i, r := range rules {
opts[i] = func(s string) string { r(s); return s }
}
return &patternsMux{Multiplexer: mux, opts: opts}
}
func NoTrailingSlash() PatternRule {
return func(s string) {
if strings.HasSuffix(s, "/") || strings.HasSuffix(s, "/{$}") {
panic(fmt.Sprintf("no-trailing-slash: pattern %q has trailing slash", s))
}
}
}
func EnsureTrailingSlash() PatternRule {
return func(s string) {
if !strings.HasSuffix(s, "/{$}") && !strings.HasSuffix(s, "/") && !strings.HasSuffix(s, "...}") {
panic(fmt.Sprintf("trailing-slash: pattern %q doesn't has a trailing slash", s))
}
}
}
func NoMethod() PatternRule {
return func(s string) {
if len(strings.Split(s, " ")) > 1 {
panic(fmt.Sprintf("no-method: pattern %q has a method", s))
}
}
}
func EnsureMethod(methods ...string) PatternRule {
if len(methods) == 0 && methods != nil {
methods = DefaultMethods
}
return func(s string) {
sp := strings.Split(s, " ")
if len(sp) <= 0 {
panic(fmt.Sprintf("method: pattern %q doesn't has a method", s))
}
if methods != nil {
if slices.Contains(methods, sp[0]) {
panic(fmt.Sprintf("method: pattern %q doesn't has a valid method, valid methods are: %s", s, strings.Join(methods, ", ")))
}
}
}
}
var DefaultMethods = []string{
http.MethodConnect,
http.MethodDelete,
http.MethodGet,
http.MethodHead,
http.MethodOptions,
http.MethodPatch,
http.MethodPost,
http.MethodPut,
http.MethodTrace,
}
func EnsureStrictEnd() PatternRule {
return func(s string) {
if !strings.HasSuffix(s, "{$}") && !strings.HasSuffix(s, "...}") {
panic(fmt.Sprintf(`strict-end: pattern %q doesn't end with "{$}"`, s))
}
}
}
func NoStrictEnd() PatternRule {
return func(s string) {
if strings.HasSuffix(s, "{$}") {
panic(fmt.Sprintf(`no-strict-end: pattern %q ends with "{$}"`, s))
}
}
}
type PatternRule func(string)
type patternsMux struct {
opts []PatternOption
Multiplexer
}
func (pm *patternsMux) HandleFunc(p string, h func(http.ResponseWriter, *http.Request)) {
for _, po := range pm.opts {
p = po(p)
}
pm.Multiplexer.HandleFunc(p, h)
}
func (pm *patternsMux) Handle(p string, h http.Handler) {
for _, po := range pm.opts {
p = po(p)
}
pm.Multiplexer.Handle(p, h)
}