101 lines
2.6 KiB
Go
101 lines
2.6 KiB
Go
// 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))
|
|
}
|