func matchesRuleWithPlaceholderOrSplats()

in internal/redirects/matching.go [72:117]


func matchesRuleWithPlaceholderOrSplats(requestPath string, fromPath string, toPath string, status int) (bool, string) {
	// Any logic beyond this point handles placeholders and splats.
	// If the FF_ENABLE_PLACEHOLDERS feature flag isn't enabled, exit now.
	if !feature.RedirectsPlaceholders.Enabled() {
		return false, ""
	}

	regexSegments := convertToRegexSegments(fromPath)
	if len(regexSegments) == 0 {
		return false, ""
	}

	fromRegexString := `(?i)^` + strings.Join(regexSegments, "") + `/*$`
	fromRegex, err := regexp.Compile(fromRegexString)
	if err != nil {
		log.WithFields(log.Fields{
			"fromRegexString": fromRegexString,
			"rule.From":       fromPath,
			"rule.To":         toPath,
			"rule.Status":     status,
			"path":            requestPath,
		}).WithError(err).Warnf("matchesRule generated an invalid regex: %q", fromRegexString)

		return false, ""
	}

	template := regexPlaceholderReplacement.ReplaceAllString(toPath, `${$placeholder}`)
	subMatchIndex := fromRegex.FindStringSubmatchIndex(requestPath)

	if subMatchIndex == nil {
		return false, ""
	}

	var templatedToPath []byte
	templatedToPath = fromRegex.ExpandString(templatedToPath, template, requestPath, subMatchIndex)

	// Some replacements result in subsequent slashes. For example, a rule with a "to"
	// like `foo/:splat/bar` will result in a path like `foo//bar` if the splat
	// character matches nothing. To avoid this, replace all instances
	// of multiple subsequent forward slashes with a single forward slash.
	// The regex captures any character except a colon ([^:]) before the double slashes
	// and includes it in the replacement.
	templatedToPath = regexMultipleSlashes.ReplaceAll(templatedToPath, []byte("$1/"))

	return true, string(templatedToPath)
}