implement ToRFC1459(); implement Glob()

This commit is contained in:
Liam Stanley 2017-02-21 22:53:52 -05:00
parent fcef03e022
commit 4ec34eb268
2 changed files with 203 additions and 0 deletions

View File

@ -232,3 +232,64 @@ func IsValidUser(name string) bool {
return true
}
// ToRFC1459 converts a string to the stripped down conversion within RFC
// 1459. This will do things like replace an "A" with an "a", "[]" with "{}",
// and so forth. Useful to compare two nicknames.
func ToRFC1459(input string) (out string) {
for i := 0; i < len(input); i++ {
if input[i] >= 65 && input[i] <= 94 {
out += string(rune(input[i]) + 32)
} else {
out += string(input[i])
}
}
return out
}
const globChar = "*"
// Glob will test a string pattern, potentially containing globs, against a
// string. The glob character is *.
func Glob(input, match string) bool {
// Empty pattern.
if match == "" {
return input == match
}
// If a glob, match all.
if match == globChar {
return true
}
parts := strings.Split(match, globChar)
if len(parts) == 1 {
// No globs, test for equality.
return input == match
}
leadingGlob, trailingGlob := strings.HasPrefix(match, globChar), strings.HasSuffix(match, globChar)
last := len(parts) - 1
// Check prefix first.
if !leadingGlob && !strings.HasPrefix(input, parts[0]) {
return false
}
// Check middle section.
for i := 1; i < last; i++ {
if !strings.Contains(input, parts[i]) {
return false
}
// Trim already-evaluated text from input during loop over match
// text.
idx := strings.Index(input, parts[i]) + len(parts[i])
input = input[idx:]
}
// Check suffix last.
return trailingGlob || strings.HasSuffix(input, parts[last])
}

View File

@ -5,6 +5,7 @@
package girc
import (
"strings"
"testing"
)
@ -215,3 +216,144 @@ func TestIsValidUser(t *testing.T) {
}
}
}
func TestToRFC1459(t *testing.T) {
cases := []struct {
in string
want string
}{
{"", ""},
{"a", "a"},
{"abcd", "abcd"},
{"AbcD", "abcd"},
{"!@#$%^&*()_+-=", "!@#$%~&*()_+-="},
{"Abcd[]", "abcd{}"},
}
for _, tt := range cases {
if got := ToRFC1459(tt.in); got != tt.want {
t.Errorf("ToRFC1459() = %q, want %q", got, tt.want)
}
}
return
}
func BenchmarkGlob(b *testing.B) {
for i := 0; i < b.N; i++ {
if !Glob("*quick*fox*dog", "The quick brown fox jumped over the lazy dog") {
b.Fatalf("should match")
}
}
return
}
func testGlobMatch(t *testing.T, subj, pattern string) {
if !Glob(subj, pattern) {
t.Fatalf("'%s' should match '%s'", pattern, subj)
}
return
}
func testGlobNoMatch(t *testing.T, subj, pattern string) {
if Glob(subj, pattern) {
t.Fatalf("'%s' should not match '%s'", pattern, subj)
}
return
}
func TestEmptyPattern(t *testing.T) {
testGlobMatch(t, "", "")
testGlobNoMatch(t, "test", "")
return
}
func TestEmptySubject(t *testing.T) {
cases := []string{
"",
"*",
"**",
"***",
"****************",
strings.Repeat("*", 1000000),
}
for _, pattern := range cases {
testGlobMatch(t, "", pattern)
}
cases = []string{
// No globs/non-glob characters.
"test",
"*test*",
// Trailing characters.
"*x",
"*****************x",
strings.Repeat("*", 1000000) + "x",
// Leading characters.
"x*",
"x*****************",
"x" + strings.Repeat("*", 1000000),
// Mixed leading/trailing characters.
"x*x",
"x****************x",
"x" + strings.Repeat("*", 1000000) + "x",
}
for _, pattern := range cases {
testGlobNoMatch(t, pattern, "")
}
return
}
func TestPatternWithoutGlobs(t *testing.T) {
testGlobMatch(t, "test", "test")
return
}
func TestGlob(t *testing.T) {
cases := []string{
"*test", // Leading.
"this*", // Trailing.
"this*test", // Middle.
"*is *", // String in between two.
"*is*a*", // Lots.
"**test**", // Double glob characters.
"**is**a***test*", // Varying number.
"* *", // White space between.
"*", // Lone.
"**********", // Nothing but globs.
"*Ѿ*", // Unicode.
"*is a ϗѾ *", // Mixed ASCII/unicode.
}
for _, pattern := range cases {
testGlobMatch(t, "this is a ϗѾ test", pattern)
}
cases = []string{
"test*", // Implicit substring match.
"*is", // Partial match.
"*no*", // Globs without a match between them.
" ", // Plain white space.
"* ", // Trailing white space.
" *", // Leading white space.
"*ʤ*", // Non-matching unicode.
}
// Non-matches
for _, pattern := range cases {
testGlobNoMatch(t, "this is a test", pattern)
}
return
}