// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Patterns for ServeMux routing.

package http

import (
	
	
	
	
	
)

// A pattern is something that can be matched against an HTTP request.
// It has an optional method, an optional host, and a path.
type pattern struct {
	str    string // original string
	method string
	host   string
	// The representation of a path differs from the surface syntax, which
	// simplifies most algorithms.
	//
	// Paths ending in '/' are represented with an anonymous "..." wildcard.
	// For example, the path "a/" is represented as a literal segment "a" followed
	// by a segment with multi==true.
	//
	// Paths ending in "{$}" are represented with the literal segment "/".
	// For example, the path "a/{$}" is represented as a literal segment "a" followed
	// by a literal segment "/".
	segments []segment
	loc      string // source location of registering call, for helpful messages
}

func ( *pattern) () string { return .str }

func ( *pattern) () segment {
	return .segments[len(.segments)-1]
}

// A segment is a pattern piece that matches one or more path segments, or
// a trailing slash.
//
// If wild is false, it matches a literal segment, or, if s == "/", a trailing slash.
// Examples:
//
//	"a" => segment{s: "a"}
//	"/{$}" => segment{s: "/"}
//
// If wild is true and multi is false, it matches a single path segment.
// Example:
//
//	"{x}" => segment{s: "x", wild: true}
//
// If both wild and multi are true, it matches all remaining path segments.
// Example:
//
//	"{rest...}" => segment{s: "rest", wild: true, multi: true}
type segment struct {
	s     string // literal or wildcard name or "/" for "/{$}".
	wild  bool
	multi bool // "..." wildcard
}

// parsePattern parses a string into a Pattern.
// The string's syntax is
//
//	[METHOD] [HOST]/[PATH]
//
// where:
//   - METHOD is an HTTP method
//   - HOST is a hostname
//   - PATH consists of slash-separated segments, where each segment is either
//     a literal or a wildcard of the form "{name}", "{name...}", or "{$}".
//
// METHOD, HOST and PATH are all optional; that is, the string can be "/".
// If METHOD is present, it must be followed by a single space.
// Wildcard names must be valid Go identifiers.
// The "{$}" and "{name...}" wildcard must occur at the end of PATH.
// PATH may end with a '/'.
// Wildcard names in a path must be distinct.
func parsePattern( string) ( *pattern,  error) {
	if len() == 0 {
		return nil, errors.New("empty pattern")
	}
	 := 0 // offset into string
	defer func() {
		if  != nil {
			 = fmt.Errorf("at offset %d: %w", , )
		}
	}()

	, ,  := strings.Cut(, " ")
	if ! {
		 = 
		 = ""
	}
	if  != "" && !validMethod() {
		return nil, fmt.Errorf("invalid method %q", )
	}
	 := &pattern{str: , method: }

	if  {
		 = len() + 1
	}
	 := strings.IndexByte(, '/')
	if  < 0 {
		return nil, errors.New("host/path missing /")
	}
	.host = [:]
	 = [:]
	if  := strings.IndexByte(.host, '{');  >= 0 {
		 += 
		return nil, errors.New("host contains '{' (missing initial '/'?)")
	}
	// At this point, rest is the path.
	 += 

	// An unclean path with a method that is not CONNECT can never match,
	// because paths are cleaned before matching.
	if  != "" &&  != "CONNECT" &&  != cleanPath() {
		return nil, errors.New("non-CONNECT pattern with unclean path can never match")
	}

	 := map[string]bool{} // remember wildcard names to catch dups
	for len() > 0 {
		// Invariant: rest[0] == '/'.
		 = [1:]
		 = len() - len()
		if len() == 0 {
			// Trailing slash.
			.segments = append(.segments, segment{wild: true, multi: true})
			break
		}
		 := strings.IndexByte(, '/')
		if  < 0 {
			 = len()
		}
		var  string
		,  = [:], [:]
		if  := strings.IndexByte(, '{');  < 0 {
			// Literal.
			 = pathUnescape()
			.segments = append(.segments, segment{s: })
		} else {
			// Wildcard.
			if  != 0 {
				return nil, errors.New("bad wildcard segment (must start with '{')")
			}
			if [len()-1] != '}' {
				return nil, errors.New("bad wildcard segment (must end with '}')")
			}
			 := [1 : len()-1]
			if  == "$" {
				if len() != 0 {
					return nil, errors.New("{$} not at end")
				}
				.segments = append(.segments, segment{s: "/"})
				break
			}
			,  := strings.CutSuffix(, "...")
			if  && len() != 0 {
				return nil, errors.New("{...} wildcard not at end")
			}
			if  == "" {
				return nil, errors.New("empty wildcard")
			}
			if !isValidWildcardName() {
				return nil, fmt.Errorf("bad wildcard name %q", )
			}
			if [] {
				return nil, fmt.Errorf("duplicate wildcard name %q", )
			}
			[] = true
			.segments = append(.segments, segment{s: , wild: true, multi: })
		}
	}
	return , nil
}

func isValidWildcardName( string) bool {
	if  == "" {
		return false
	}
	// Valid Go identifier.
	for ,  := range  {
		if !unicode.IsLetter() &&  != '_' && ( == 0 || !unicode.IsDigit()) {
			return false
		}
	}
	return true
}

func pathUnescape( string) string {
	,  := url.PathUnescape()
	if  != nil {
		// Invalidly escaped path; use the original
		return 
	}
	return 
}

// relationship is a relationship between two patterns, p1 and p2.
type relationship string

const (
	equivalent   relationship = "equivalent"   // both match the same requests
	moreGeneral  relationship = "moreGeneral"  // p1 matches everything p2 does & more
	moreSpecific relationship = "moreSpecific" // p2 matches everything p1 does & more
	disjoint     relationship = "disjoint"     // there is no request that both match
	overlaps     relationship = "overlaps"     // there is a request that both match, but neither is more specific
)

// conflictsWith reports whether p1 conflicts with p2, that is, whether
// there is a request that both match but where neither is higher precedence
// than the other.
//
//	Precedence is defined by two rules:
//	1. Patterns with a host win over patterns without a host.
//	2. Patterns whose method and path is more specific win. One pattern is more
//	   specific than another if the second matches all the (method, path) pairs
//	   of the first and more.
//
// If rule 1 doesn't apply, then two patterns conflict if their relationship
// is either equivalence (they match the same set of requests) or overlap
// (they both match some requests, but neither is more specific than the other).
func ( *pattern) ( *pattern) bool {
	if .host != .host {
		// Either one host is empty and the other isn't, in which case the
		// one with the host wins by rule 1, or neither host is empty
		// and they differ, so they won't match the same paths.
		return false
	}
	 := .comparePathsAndMethods()
	return  == equivalent ||  == overlaps
}

func ( *pattern) ( *pattern) relationship {
	 := .compareMethods()
	// Optimization: avoid a call to comparePaths.
	if  == disjoint {
		return disjoint
	}
	 := .comparePaths()
	return combineRelationships(, )
}

// compareMethods determines the relationship between the method
// part of patterns p1 and p2.
//
// A method can either be empty, "GET", or something else.
// The empty string matches any method, so it is the most general.
// "GET" matches both GET and HEAD.
// Anything else matches only itself.
func ( *pattern) ( *pattern) relationship {
	if .method == .method {
		return equivalent
	}
	if .method == "" {
		// p1 matches any method, but p2 does not, so p1 is more general.
		return moreGeneral
	}
	if .method == "" {
		return moreSpecific
	}
	if .method == "GET" && .method == "HEAD" {
		// p1 matches GET and HEAD; p2 matches only HEAD.
		return moreGeneral
	}
	if .method == "GET" && .method == "HEAD" {
		return moreSpecific
	}
	return disjoint
}

// comparePaths determines the relationship between the path
// part of two patterns.
func ( *pattern) ( *pattern) relationship {
	// Optimization: if a path pattern doesn't end in a multi ("...") wildcard, then it
	// can only match paths with the same number of segments.
	if len(.segments) != len(.segments) && !.lastSegment().multi && !.lastSegment().multi {
		return disjoint
	}

	// Consider corresponding segments in the two path patterns.
	var ,  []segment
	 := equivalent
	for ,  = .segments, .segments; len() > 0 && len() > 0; ,  = [1:], [1:] {
		 = combineRelationships(, compareSegments([0], [0]))
		if  == disjoint {
			return 
		}
	}
	// We've reached the end of the corresponding segments of the patterns.
	// If they have the same number of segments, then we've already determined
	// their relationship.
	if len() == 0 && len() == 0 {
		return 
	}
	// Otherwise, the only way they could fail to be disjoint is if the shorter
	// pattern ends in a multi. In that case, that multi is more general
	// than the remainder of the longer pattern, so combine those two relationships.
	if len() < len() && .lastSegment().multi {
		return combineRelationships(, moreGeneral)
	}
	if len() < len() && .lastSegment().multi {
		return combineRelationships(, moreSpecific)
	}
	return disjoint
}

// compareSegments determines the relationship between two segments.
func compareSegments(,  segment) relationship {
	if .multi && .multi {
		return equivalent
	}
	if .multi {
		return moreGeneral
	}
	if .multi {
		return moreSpecific
	}
	if .wild && .wild {
		return equivalent
	}
	if .wild {
		if .s == "/" {
			// A single wildcard doesn't match a trailing slash.
			return disjoint
		}
		return moreGeneral
	}
	if .wild {
		if .s == "/" {
			return disjoint
		}
		return moreSpecific
	}
	// Both literals.
	if .s == .s {
		return equivalent
	}
	return disjoint
}

// combineRelationships determines the overall relationship of two patterns
// given the relationships of a partition of the patterns into two parts.
//
// For example, if p1 is more general than p2 in one way but equivalent
// in the other, then it is more general overall.
//
// Or if p1 is more general in one way and more specific in the other, then
// they overlap.
func combineRelationships(,  relationship) relationship {
	switch  {
	case equivalent:
		return 
	case disjoint:
		return disjoint
	case overlaps:
		if  == disjoint {
			return disjoint
		}
		return overlaps
	case moreGeneral, moreSpecific:
		switch  {
		case equivalent:
			return 
		case inverseRelationship():
			return overlaps
		default:
			return 
		}
	default:
		panic(fmt.Sprintf("unknown relationship %q", ))
	}
}

// If p1 has relationship `r` to p2, then
// p2 has inverseRelationship(r) to p1.
func inverseRelationship( relationship) relationship {
	switch  {
	case moreSpecific:
		return moreGeneral
	case moreGeneral:
		return moreSpecific
	default:
		return 
	}
}

// isLitOrSingle reports whether the segment is a non-dollar literal or a single wildcard.
func isLitOrSingle( segment) bool {
	if .wild {
		return !.multi
	}
	return .s != "/"
}

// describeConflict returns an explanation of why two patterns conflict.
func describeConflict(,  *pattern) string {
	 := .compareMethods()
	 := .comparePaths()
	 := combineRelationships(, )
	if  == equivalent {
		return fmt.Sprintf("%s matches the same requests as %s", , )
	}
	if  != overlaps {
		panic("describeConflict called with non-conflicting patterns")
	}
	if  == overlaps {
		return fmt.Sprintf(`%[1]s and %[2]s both match some paths, like %[3]q.
But neither is more specific than the other.
%[1]s matches %[4]q, but %[2]s doesn't.
%[2]s matches %[5]q, but %[1]s doesn't.`,
			, , commonPath(, ), differencePath(, ), differencePath(, ))
	}
	if  == moreGeneral &&  == moreSpecific {
		return fmt.Sprintf("%s matches more methods than %s, but has a more specific path pattern", , )
	}
	if  == moreSpecific &&  == moreGeneral {
		return fmt.Sprintf("%s matches fewer methods than %s, but has a more general path pattern", , )
	}
	return fmt.Sprintf("bug: unexpected way for two patterns %s and %s to conflict: methods %s, paths %s", , , , )
}

// writeMatchingPath writes to b a path that matches the segments.
func writeMatchingPath( *strings.Builder,  []segment) {
	for ,  := range  {
		writeSegment(, )
	}
}

func writeSegment( *strings.Builder,  segment) {
	.WriteByte('/')
	if !.multi && .s != "/" {
		.WriteString(.s)
	}
}

// commonPath returns a path that both p1 and p2 match.
// It assumes there is such a path.
func commonPath(,  *pattern) string {
	var  strings.Builder
	var ,  []segment
	for ,  = .segments, .segments; len() > 0 && len() > 0; ,  = [1:], [1:] {
		if  := [0]; .wild {
			writeSegment(&, [0])
		} else {
			writeSegment(&, )
		}
	}
	if len() > 0 {
		writeMatchingPath(&, )
	} else if len() > 0 {
		writeMatchingPath(&, )
	}
	return .String()
}

// differencePath returns a path that p1 matches and p2 doesn't.
// It assumes there is such a path.
func differencePath(,  *pattern) string {
	var  strings.Builder

	var ,  []segment
	for ,  = .segments, .segments; len() > 0 && len() > 0; ,  = [1:], [1:] {
		 := [0]
		 := [0]
		if .multi && .multi {
			// From here the patterns match the same paths, so we must have found a difference earlier.
			.WriteByte('/')
			return .String()

		}
		if .multi && !.multi {
			// s1 ends in a "..." wildcard but s2 does not.
			// A trailing slash will distinguish them, unless s2 ends in "{$}",
			// in which case any segment will do; prefer the wildcard name if
			// it has one.
			.WriteByte('/')
			if .s == "/" {
				if .s != "" {
					.WriteString(.s)
				} else {
					.WriteString("x")
				}
			}
			return .String()
		}
		if !.multi && .multi {
			writeSegment(&, )
		} else if .wild && .wild {
			// Both patterns will match whatever we put here; use
			// the first wildcard name.
			writeSegment(&, )
		} else if .wild && !.wild {
			// s1 is a wildcard, s2 is a literal.
			// Any segment other than s2.s will work.
			// Prefer the wildcard name, but if it's the same as the literal,
			// tweak the literal.
			if .s != .s {
				writeSegment(&, )
			} else {
				.WriteByte('/')
				.WriteString(.s + "x")
			}
		} else if !.wild && .wild {
			writeSegment(&, )
		} else {
			// Both are literals. A precondition of this function is that the
			// patterns overlap, so they must be the same literal. Use it.
			if .s != .s {
				panic(fmt.Sprintf("literals differ: %q and %q", .s, .s))
			}
			writeSegment(&, )
		}
	}
	if len() > 0 {
		// p1 is longer than p2, and p2 does not end in a multi.
		// Anything that matches the rest of p1 will do.
		writeMatchingPath(&, )
	} else if len() > 0 {
		writeMatchingPath(&, )
	}
	return .String()
}