Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ on:
push:
workflow_dispatch:

permissions:
contents: read

jobs:
test:
name: Test Fox
runs-on: ubuntu-latest
strategy:
matrix:
go: [ '>=1.24' ]
go: [ '>=1.26' ]
steps:
- name: Set up Go
uses: actions/setup-go@v6
Expand All @@ -19,6 +22,8 @@ jobs:

- name: Check out code
uses: actions/checkout@v6
with:
persist-credentials: false

- name: Run tests
run: go test -v -coverprofile=coverage.txt -covermode=atomic ./...
Expand All @@ -37,7 +42,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go: [ '>=1.24' ]
go: [ '>=1.26' ]
steps:
- name: Set up Go
uses: actions/setup-go@v6
Expand All @@ -47,6 +52,8 @@ jobs:

- name: Check out code
uses: actions/checkout@v6
with:
persist-credentials: false

- name: Run linter
uses: golangci/golangci-lint-action@v9
60 changes: 56 additions & 4 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ var (
ErrNoClientIPResolver = errors.New("no client ip resolver")
ErrReadOnlyTxn = errors.New("write on read-only transaction")
ErrSettledTxn = errors.New("transaction settled")
ErrParamKeyTooLarge = errors.New("parameter key too large")
ErrTooManyParams = errors.New("too many params")
ErrTooManyMatchers = errors.New("too many matchers")
ErrRegexpNotAllowed = errors.New("regexp not allowed")
ErrInvalidConfig = errors.New("invalid config")
ErrInvalidMatcher = errors.New("invalid matcher")
)
Expand All @@ -44,7 +41,7 @@ func (e *RouteConflictError) Error() string {
routef(sb, e.New, 4, true)

if e.isShadowed {
if e.New.catchEmpty {
if e.New.pattern.optionalCatchAll {
sb.WriteString("\nis shadowed by")
} else {
sb.WriteString("\nwould shadow")
Expand Down Expand Up @@ -96,3 +93,58 @@ func newRouteNotFoundError(route *Route) error {
sb.WriteString("\nis not registered")
return fmt.Errorf("%w: %s", ErrRouteNotFound, sb.String())
}

type PatternError struct {
err error // wrapped error
Pattern string // provided pattern
Type string // hostname | path
Reason string // syntax | parameter | regexp | constraint
Hint string // hint
Start int // start offset of the offending segment
End int // end offset of the offending segment
}

// Unwrap returns the underlying error, if any.
func (e *PatternError) Unwrap() error {
return e.err
}

// Error returns a human-readable error message with a visual pointer to the offending segment.
func (e *PatternError) Error() string {
var sb strings.Builder
sb.WriteString("pattern: ")
if e.Type != "" {
sb.WriteString(e.Type)
sb.WriteString(": ")
}
sb.WriteString(e.Reason)
sb.WriteString(": ")
sb.WriteString(e.Hint)
if e.Pattern != "" {
sb.WriteByte('\n')
sb.WriteString(" ")
sb.WriteString(e.Pattern)
sb.WriteByte('\n')
sb.WriteString(" ")
for i := 0; i < e.Start; i++ {
sb.WriteByte(' ')
}
n := e.End - e.Start
if n <= 0 {
n = 1
}
for i := 0; i < n; i++ {
sb.WriteByte('^')
}
}
return sb.String()
}

func newPatternError(reason string, start, end int, msg string) *PatternError {
return &PatternError{
Reason: reason,
Start: start,
End: end,
Hint: msg,
}
}
Loading
Loading