From c90f36e3dd179d2de96f4f5fe38d8dc9a9de6dfe Mon Sep 17 00:00:00 2001 From: Emile Date: Fri, 25 Oct 2024 15:55:50 +0200 Subject: vendor --- vendor/go.mau.fi/util/glob/glob.go | 100 +++++++++++++++++++++++++++++++++++ vendor/go.mau.fi/util/glob/regex.go | 43 +++++++++++++++ vendor/go.mau.fi/util/glob/simple.go | 62 ++++++++++++++++++++++ vendor/go.mau.fi/util/glob/util.go | 42 +++++++++++++++ 4 files changed, 247 insertions(+) create mode 100644 vendor/go.mau.fi/util/glob/glob.go create mode 100644 vendor/go.mau.fi/util/glob/regex.go create mode 100644 vendor/go.mau.fi/util/glob/simple.go create mode 100644 vendor/go.mau.fi/util/glob/util.go (limited to 'vendor/go.mau.fi/util/glob') diff --git a/vendor/go.mau.fi/util/glob/glob.go b/vendor/go.mau.fi/util/glob/glob.go new file mode 100644 index 0000000..67fce3e --- /dev/null +++ b/vendor/go.mau.fi/util/glob/glob.go @@ -0,0 +1,100 @@ +// Package glob implements very simple glob pattern matching used in various parts of the Matrix spec, +// such as push rules and moderation policy lists. +// +// See https://spec.matrix.org/v1.11/appendices/#glob-style-matching for more info. +package glob + +import ( + "strings" +) + +type Glob interface { + Match(string) bool +} + +var ( + _ Glob = ExactGlob("") + _ Glob = PrefixGlob("") + _ Glob = SuffixGlob("") + _ Glob = ContainsGlob("") + _ Glob = (*PrefixAndSuffixGlob)(nil) + _ Glob = (*PrefixSuffixAndContainsGlob)(nil) + _ Glob = (*RegexGlob)(nil) +) + +// Compile compiles a glob pattern into an object that can be used to efficiently match strings against the pattern. +// +// Simple globs will be converted into prefix/suffix/contains checks, while complicated ones will be compiled as regex. +func Compile(pattern string) Glob { + pattern = Simplify(pattern) + g := compileSimple(pattern) + if g != nil { + return g + } + g, _ = CompileRegex(pattern) + return g +} + +// CompileWithImplicitContains is a wrapper for Compile which will replace exact matches with contains matches. +// i.e. if the pattern has no wildcards, it will be treated as if it was surrounded in asterisks (`foo` -> `*foo*`). +func CompileWithImplicitContains(pattern string) Glob { + g := Compile(pattern) + if _, isExact := g.(ExactGlob); isExact { + return ContainsGlob(pattern) + } + return g +} + +// CompileSimple compiles a glob pattern into one of the non-regex forms. +// +// If the pattern can't be compiled into a simple form, it returns nil. +func CompileSimple(pattern string) Glob { + return compileSimple(Simplify(pattern)) +} + +func compileSimple(pattern string) Glob { + if strings.ContainsRune(pattern, '?') { + return nil + } + switch strings.Count(pattern, "*") { + case 0: + return ExactGlob(pattern) + case 1: + if strings.HasPrefix(pattern, "*") { + return SuffixGlob(pattern[1:]) + } else if strings.HasSuffix(pattern, "*") { + return PrefixGlob(pattern[:len(pattern)-1]) + } else { + parts := strings.Split(pattern, "*") + return PrefixAndSuffixGlob{ + Prefix: parts[0], + Suffix: parts[1], + } + } + case 2: + if strings.HasPrefix(pattern, "*") && strings.HasSuffix(pattern, "*") { + return ContainsGlob(pattern[1 : len(pattern)-1]) + } + parts := strings.Split(pattern, "*") + return PrefixSuffixAndContainsGlob{ + Prefix: parts[0], + Contains: parts[1], + Suffix: parts[2], + } + default: + return nil + } +} + +var sqlCompiler = strings.NewReplacer( + `\`, `\\`, + `%`, `\%`, + `_`, `\_`, + `*`, `%`, + `?`, `_`, +) + +// ToSQL converts a Matrix glob pattern to a SQL LIKE pattern. +func ToSQL(pattern string) string { + return sqlCompiler.Replace(Simplify(pattern)) +} diff --git a/vendor/go.mau.fi/util/glob/regex.go b/vendor/go.mau.fi/util/glob/regex.go new file mode 100644 index 0000000..f224533 --- /dev/null +++ b/vendor/go.mau.fi/util/glob/regex.go @@ -0,0 +1,43 @@ +package glob + +import ( + "fmt" + "regexp" + "strings" +) + +type RegexGlob struct { + regex *regexp.Regexp +} + +func (rg *RegexGlob) Match(s string) bool { + return rg.regex.MatchString(s) +} + +func CompileRegex(pattern string) (*RegexGlob, error) { + var buf strings.Builder + buf.WriteRune('^') + for _, part := range SplitPattern(pattern) { + if strings.ContainsRune(part, '*') || strings.ContainsRune(part, '?') { + questions := strings.Count(part, "?") + star := strings.ContainsRune(part, '*') + if star { + if questions > 0 { + _, _ = fmt.Fprintf(&buf, ".{%d,}", questions) + } else { + buf.WriteString(".*") + } + } else if questions > 0 { + _, _ = fmt.Fprintf(&buf, ".{%d}", questions) + } + } else { + buf.WriteString(regexp.QuoteMeta(part)) + } + } + buf.WriteRune('$') + regex, err := regexp.Compile(buf.String()) + if err != nil { + return nil, err + } + return &RegexGlob{regex}, nil +} diff --git a/vendor/go.mau.fi/util/glob/simple.go b/vendor/go.mau.fi/util/glob/simple.go new file mode 100644 index 0000000..bb9bf6d --- /dev/null +++ b/vendor/go.mau.fi/util/glob/simple.go @@ -0,0 +1,62 @@ +package glob + +import ( + "strings" +) + +// ExactGlob is the result of [Compile] when the pattern contains no glob characters. +// It uses a simple string comparison to match. +type ExactGlob string + +func (eg ExactGlob) Match(s string) bool { + return string(eg) == s +} + +// SuffixGlob is the result of [Compile] when the pattern only has one `*` at the beginning. +// It uses [strings.HasSuffix] to match. +type SuffixGlob string + +func (sg SuffixGlob) Match(s string) bool { + return strings.HasSuffix(s, string(sg)) +} + +// PrefixGlob is the result of [Compile] when the pattern only has one `*` at the end. +// It uses [strings.HasPrefix] to match. +type PrefixGlob string + +func (pg PrefixGlob) Match(s string) bool { + return strings.HasPrefix(s, string(pg)) +} + +// ContainsGlob is the result of [Compile] when the pattern has two `*`s, one at the beginning and one at the end. +// It uses [strings.Contains] to match. +// +// When there are exactly two `*`s, but they're not surrounding the string, the pattern is compiled as a [PrefixSuffixAndContainsGlob] instead. +type ContainsGlob string + +func (cg ContainsGlob) Match(s string) bool { + return strings.Contains(s, string(cg)) +} + +// PrefixAndSuffixGlob is the result of [Compile] when the pattern only has one `*` in the middle. +type PrefixAndSuffixGlob struct { + Prefix string + Suffix string +} + +func (psg PrefixAndSuffixGlob) Match(s string) bool { + return strings.HasPrefix(s, psg.Prefix) && strings.HasSuffix(s[len(psg.Prefix):], psg.Suffix) +} + +// PrefixSuffixAndContainsGlob is the result of [Compile] when the pattern has two `*`s which are not surrounding the rest of the pattern. +type PrefixSuffixAndContainsGlob struct { + Prefix string + Suffix string + Contains string +} + +func (psacg PrefixSuffixAndContainsGlob) Match(s string) bool { + return strings.HasPrefix(s, psacg.Prefix) && + strings.HasSuffix(s[len(psacg.Prefix):], psacg.Suffix) && + strings.Contains(s[len(psacg.Prefix):len(s)-len(psacg.Suffix)], psacg.Contains) +} diff --git a/vendor/go.mau.fi/util/glob/util.go b/vendor/go.mau.fi/util/glob/util.go new file mode 100644 index 0000000..37d9b2d --- /dev/null +++ b/vendor/go.mau.fi/util/glob/util.go @@ -0,0 +1,42 @@ +package glob + +import ( + "regexp" + "strings" +) + +var redundantStarRegex = regexp.MustCompile(`\*{2,}`) +var maybeRedundantQuestionRegex = regexp.MustCompile(`[*?]{2,}`) +var wildcardRegex = regexp.MustCompile(`[*?]+`) + +func SplitPattern(pattern string) []string { + indexes := wildcardRegex.FindAllStringIndex(pattern, -1) + if len(indexes) == 0 { + return []string{pattern} + } + parts := make([]string, 0, len(indexes)+1) + start := 0 + for _, part := range indexes { + end := part[0] + if end > start { + parts = append(parts, pattern[start:end]) + } + parts = append(parts, pattern[part[0]:part[1]]) + start = part[1] + } + if start < len(pattern) { + parts = append(parts, pattern[start:]) + } + return parts +} + +func Simplify(pattern string) string { + pattern = redundantStarRegex.ReplaceAllString(pattern, "*") + pattern = maybeRedundantQuestionRegex.ReplaceAllStringFunc(pattern, func(s string) string { + if !strings.ContainsRune(s, '*') { + return s + } + return strings.Repeat("?", strings.Count(s, "?")) + "*" + }) + return pattern +} -- cgit 1.4.1