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
15 changes: 12 additions & 3 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
version: "2"

linters:
enable:
- err113
- goconst
- gocritic
- intrange
- modernize
- perfsprint
- thelper
- unconvert
- unparam
- wastedassign
- whitespace
settings:
staticcheck:
checks:
- "all"
# ST1012: public API exports FooError variables (NoBaseURLError,
# InvalidPortError, ...). Renaming to ErrFoo would break consumers.
- "-ST1012"
# ST1020/ST1021: exported symbols are documented with their
# corresponding WHATWG spec URL, not a "Foo does..." sentence.
# The URL is the most useful pointer for maintainers of a
Expand Down
6 changes: 3 additions & 3 deletions benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func BenchmarkNew(b *testing.B) {
b.ReportAllocs()
var p *urlpattern.URLPattern
var err error
for i := 0; i < b.N; i++ {
for range b.N {
p, err = urlpattern.New(bc.pattern, bc.baseURL, nil)
if err != nil {
b.Fatal(err)
Expand All @@ -60,7 +60,7 @@ func BenchmarkTest(b *testing.B) {
b.Run(bc.name, func(b *testing.B) {
b.ReportAllocs()
var ok bool
for i := 0; i < b.N; i++ {
for range b.N {
ok = p.Test(bc.input, "")
}
benchBoolSink = ok
Expand All @@ -77,7 +77,7 @@ func BenchmarkExec(b *testing.B) {
b.Run(bc.name, func(b *testing.B) {
b.ReportAllocs()
var r *urlpattern.URLPatternResult
for i := 0; i < b.N; i++ {
for range b.N {
r = p.Exec(bc.input, "")
}
benchResultSink = r
Expand Down
34 changes: 17 additions & 17 deletions constructor_type_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,13 @@ func parseConstructorString(input string) (*URLPatternInit, error) {
if p.state == stateInit {
p.rewind()

if p.isHashPrefix() {
switch {
case p.isHashPrefix():
p.changeState(stateHash, 1)
} else if p.isSearchPrefix() {
case p.isSearchPrefix():
p.changeState(stateSearch, 1)
//p.result.Hash = ""
} else {
default:
p.changeState(statePathname, 0)
//p.result.Hash = ""
//p.result.Search = ""
}

p.tokenIndex += p.tokenIncrement
Expand Down Expand Up @@ -160,26 +158,28 @@ func parseConstructorString(input string) (*URLPatternInit, error) {
}

case stateHostname:
if p.isIPV6Open() {
switch {
case p.isIPV6Open():
p.hostnameIPv6BracketDepth++
} else if p.isIPV6Close() {
case p.isIPV6Close():
p.hostnameIPv6BracketDepth--
} else if p.isPortPrefix() && p.hostnameIPv6BracketDepth == 0 {
case p.isPortPrefix() && p.hostnameIPv6BracketDepth == 0:
p.changeState(statePort, 1)
} else if p.isPathnameStart() {
case p.isPathnameStart():
p.changeState(statePathname, 0)
} else if p.isSearchPrefix() {
case p.isSearchPrefix():
p.changeState(stateSearch, 1)
} else if p.isHashPrefix() {
case p.isHashPrefix():
p.changeState(stateHash, 1)
}

case statePort:
if p.isPathnameStart() {
switch {
case p.isPathnameStart():
p.changeState(statePathname, 0)
} else if p.isSearchPrefix() {
case p.isSearchPrefix():
p.changeState(stateSearch, 1)
} else if p.isHashPrefix() {
case p.isHashPrefix():
p.changeState(stateHash, 1)
}

Expand Down Expand Up @@ -351,15 +351,15 @@ func (p *constructorTypeParser) changeState(newState state, skip int) {
}

p.state = newState
p.tokenIndex = p.tokenIndex + skip
p.tokenIndex += skip
p.componentStart = p.tokenIndex
p.tokenIncrement = 0
}

// https://urlpattern.spec.whatwg.org/#make-a-component-string
func (p *constructorTypeParser) makeComponentString() string {
token := p.tokenList[p.tokenIndex]
componentStartToken := p.getSafeToken(int(p.componentStart))
componentStartToken := p.getSafeToken(p.componentStart)
componentStartInputIndex := componentStartToken.index
endIndex := token.index

Expand Down
4 changes: 2 additions & 2 deletions escape.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func init() {
func escapeRegexpString(s string) string {
// A byte loop is correct because all metacharacters are ASCII.
var i int
for i = 0; i < len(s); i++ {
for i = 0; i < len(s); i++ { //nolint:intrange // i is reused below
if specialRegexp(s[i]) {
break
}
Expand Down Expand Up @@ -63,7 +63,7 @@ func escapeRegexpString(s string) string {
func escapePatternString(s string) string {
// A byte loop is correct because all metacharacters are ASCII.
var i int
for i = 0; i < len(s); i++ {
for i = 0; i < len(s); i++ { //nolint:intrange // i is reused below
if specialPattern(s[i]) {
break
}
Expand Down
57 changes: 30 additions & 27 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,16 @@ var urlParser = url.NewParser()
var hostnameParser = canonicalizer.New(canonicalizer.WithDefaultScheme("http"))

var (
NonEmptySuffixError = errors.New("suffix must be the empty string")
BadParserIndexError = errors.New("parser's index must be less than parser's token list size")
DuplicatePartNameError = errors.New("duplicate name")
RequiredTokenError = errors.New("missing required token")
InvalidIPv6HostnameError = errors.New("invalid IPv6 hostname")
InvalidPortError = errors.New("invalid port")
ErrNonEmptySuffix = errors.New("suffix must be the empty string")
ErrBadParserIndex = errors.New("parser's index must be less than parser's token list size")
ErrDuplicatePartName = errors.New("duplicate name")
ErrRequiredToken = errors.New("missing required token")
ErrInvalidIPv6Hostname = errors.New("invalid IPv6 hostname")
ErrInvalidPort = errors.New("invalid port")
)

var errInvalidHostname = errors.New("invalid hostname")

// https://urlpattern.spec.whatwg.org/#encoding-callback
type encodingCallback func(string) (string, error)

Expand Down Expand Up @@ -88,7 +90,7 @@ func parsePatternString(input string, options options, encodingCallback encoding
}

if prefix != "" && prefix != string(options.prefixCodePoint) {
p.pendingFixedValue = p.pendingFixedValue + prefix
p.pendingFixedValue += prefix
prefix = ""
}

Expand All @@ -115,7 +117,7 @@ func parsePatternString(input string, options options, encodingCallback encoding
}
}
if fixedToken != nil {
p.pendingFixedValue = p.pendingFixedValue + fixedToken.value
p.pendingFixedValue += fixedToken.value

continue
}
Expand Down Expand Up @@ -146,7 +148,7 @@ func parsePatternString(input string, options options, encodingCallback encoding
return nil, err
}

if _, err := p.consumeRequiredToken(tokenClose); err != nil {
if err := p.consumeRequiredToken(tokenClose); err != nil {
return nil, fmt.Errorf("missing close token: %w", err)
}

Expand All @@ -166,7 +168,7 @@ func parsePatternString(input string, options options, encodingCallback encoding
return nil, err
}

if _, err := p.consumeRequiredToken(tokenEnd); err != nil {
if err := p.consumeRequiredToken(tokenEnd); err != nil {
return nil, fmt.Errorf("missing end token: %w", err)
}
}
Expand All @@ -189,7 +191,7 @@ type patternParser struct {
func (p *patternParser) tryConsumeToken(tokenType tokenType) (*token, error) {
// Assert: parser’s index is less than parser’s token list size.
if p.index >= len(p.tokenList) {
return nil, BadParserIndexError
return nil, ErrBadParserIndex
}

nextToken := p.tokenList[p.index]
Expand Down Expand Up @@ -265,7 +267,7 @@ func (p *patternParser) addPart(prefix string, nameToken *token, regexpOrWildcar
}

if nameToken == nil && regexpOrWildcardToken == nil && modifier == partModifierNone {
p.pendingFixedValue = p.pendingFixedValue + prefix
p.pendingFixedValue += prefix

return nil
}
Expand All @@ -277,7 +279,7 @@ func (p *patternParser) addPart(prefix string, nameToken *token, regexpOrWildcar
if nameToken == nil && regexpOrWildcardToken == nil {
// Assert: suffix is the empty string.
if suffix != "" {
return NonEmptySuffixError
return ErrNonEmptySuffix
}

if prefix == "" {
Expand All @@ -295,12 +297,13 @@ func (p *patternParser) addPart(prefix string, nameToken *token, regexpOrWildcar
return nil
}

regexpValue := ""
if regexpOrWildcardToken == nil {
var regexpValue string
switch {
case regexpOrWildcardToken == nil:
regexpValue = p.segmentWildcardRegexp
} else if regexpOrWildcardToken.tType == tokenAsterisk {
case regexpOrWildcardToken.tType == tokenAsterisk:
regexpValue = fullWildcardRegexpValue
} else {
default:
regexpValue = regexpOrWildcardToken.value
}

Expand All @@ -312,7 +315,6 @@ func (p *patternParser) addPart(prefix string, nameToken *token, regexpOrWildcar
case fullWildcardRegexpValue:
pType = partFullWildcard
regexpValue = ""

}

name := ""
Expand All @@ -325,7 +327,7 @@ func (p *patternParser) addPart(prefix string, nameToken *token, regexpOrWildcar

// https://urlpattern.spec.whatwg.org/#is-a-duplicate-name
if _, seen := p.seenNames[name]; seen {
return DuplicatePartNameError
return ErrDuplicatePartName
}

encodedPrefix, err := p.encodingCallback(prefix)
Expand Down Expand Up @@ -373,16 +375,16 @@ func (p *patternParser) consumeText() (string, error) {
}

// https://urlpattern.spec.whatwg.org/#consume-a-required-token
func (p *patternParser) consumeRequiredToken(tokenType tokenType) (*token, error) {
func (p *patternParser) consumeRequiredToken(tokenType tokenType) error {
result, err := p.tryConsumeToken(tokenType)
if err != nil {
return nil, err
return err
}
if result == nil {
return nil, RequiredTokenError
return ErrRequiredToken
}

return result, nil
return nil
}

// https://urlpattern.spec.whatwg.org/#generate-a-segment-wildcard-regexp
Expand Down Expand Up @@ -435,7 +437,7 @@ func canonicalizeHostname(hostnameValue, protocolValue string) (string, error) {
if hostnameValue[0] != '[' {
for _, c := range hostnameValue {
if c == ':' {
return "", errors.New("invalid hostname")
return "", errInvalidHostname
}
}
}
Expand Down Expand Up @@ -478,16 +480,17 @@ func canonicalizePort(portValue, protocolValue string) (string, error) {
// an ASCII digit (e.g. "invalid80"). Without this the URL library returns
// an empty port instead of failing.
firstDigit := false
for i := 0; i < len(portValue); i++ {
for i := range len(portValue) {
c := portValue[i]
if c == '\t' || c == '\n' || c == '\r' {
continue
}
firstDigit = c >= '0' && c <= '9'

break
}
if !firstDigit {
return "", InvalidPortError
return "", ErrInvalidPort
}

scheme := protocolValue
Expand Down Expand Up @@ -595,7 +598,7 @@ func canonicalizeIPv6Hostname(value string) (string, error) {

for _, c := range value {
if c != '[' && c != ']' && c != ':' && !unicode.Is(unicode.ASCII_Hex_Digit, c) {
return "", InvalidIPv6HostnameError
return "", ErrInvalidIPv6Hostname
}

result.WriteRune(unicode.ToLower(c))
Expand Down
16 changes: 8 additions & 8 deletions parts.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ const (
)

var (
EmptyPartNameError = errors.New("part's name must not be empty string")
InvalidModifierError = errors.New(`part's modifier must be "zero-or-more" or "one-or-more"`)
InvalidPrefixOrSuffix = errors.New("part's prefix is not the empty string or part's suffix is not the empty string")
InvalidPartNameError = errors.New("part's name is not the empty string or null")
ErrEmptyPartName = errors.New("part's name must not be empty string")
ErrInvalidModifier = errors.New(`part's modifier must be "zero-or-more" or "one-or-more"`)
ErrInvalidPrefixOrSuffix = errors.New("part's prefix is not the empty string or part's suffix is not the empty string")
ErrInvalidPartName = errors.New("part's name is not the empty string or null")
)

type partModifier uint8
Expand Down Expand Up @@ -81,7 +81,7 @@ func (pl partList) generateRegularExpressionAndNameList(options options) (string

// Assert: part's name is not the empty string.
if p.name == "" {
return "", nil, EmptyPartNameError
return "", nil, ErrEmptyPartName
}

nameList = append(nameList, p.name)
Expand Down Expand Up @@ -140,12 +140,12 @@ func (pl partList) generateRegularExpressionAndNameList(options options) (string

// Assert: part’s modifier is "zero-or-more" or "one-or-more".
if p.modifier != partModifierZeroOrMore && p.modifier != partModifierOneOrMore {
return "", nil, InvalidModifierError
return "", nil, ErrInvalidModifier
}

// Assert: part’s prefix is not the empty string or part’s suffix is not the empty string.
if p.prefix == "" && p.suffix == "" {
return "", nil, InvalidPrefixOrSuffix
return "", nil, ErrInvalidPrefixOrSuffix
}

result.WriteString("(?:")
Expand Down Expand Up @@ -233,7 +233,7 @@ func (pl partList) generatePatternString(options options) (string, error) {

// Assert: part’s name is not the empty string or null.
if part.name == "" {
return "", InvalidPartNameError
return "", ErrInvalidPartName
}

if needGrouping {
Expand Down
4 changes: 2 additions & 2 deletions tokenizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"golang.org/x/exp/utf8string"
)

var TypeError = errors.New("type error")
var ErrType = errors.New("type error")

// https://wicg.github.io/urlpattern/#tokenizing
type tokenizePolicy bool
Expand Down Expand Up @@ -241,7 +241,7 @@ func (t *tokenizer) addTokenWithDefaultPositionAndLength(tType tokenType) {

func (t *tokenizer) processTokenizingError(nextPosition, valuePosition int) error {
if t.policy == tokenizePolicyStrict {
return fmt.Errorf("%w: %#v", TypeError, t)
return fmt.Errorf("%w: %#v", ErrType, t)
}

t.addTokenWithDefaultLength(tokenInvalidChar, nextPosition, valuePosition)
Expand Down
Loading