mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Search: Add escaping capability for the characters "|" and "&" #5188
* Backend: Add query escape capability * Tests: escape capability verification * Backend: Clean up comments and remove commented out code.
This commit is contained in:
@@ -33,7 +33,7 @@ func LikeAny(col, s string, keywords, exact bool) (wheres []string) {
|
||||
wildcardThreshold = 2
|
||||
}
|
||||
|
||||
for _, k := range strings.Split(s, txt.And) {
|
||||
for _, k := range txt.UnTrimmedSplitWithEscape(s, txt.AndRune, txt.EscapeRune) {
|
||||
var orWheres []string
|
||||
var words []string
|
||||
|
||||
@@ -133,10 +133,10 @@ func LikeAllNames(cols Cols, s string) (wheres []string) {
|
||||
return wheres
|
||||
}
|
||||
|
||||
for _, k := range strings.Split(s, txt.And) {
|
||||
for _, k := range txt.UnTrimmedSplitWithEscape(s, txt.AndRune, txt.EscapeRune) {
|
||||
var orWheres []string
|
||||
|
||||
for _, w := range strings.Split(k, txt.Or) {
|
||||
for _, w := range txt.UnTrimmedSplitWithEscape(k, txt.OrRune, txt.EscapeRune) {
|
||||
w = strings.TrimSpace(w)
|
||||
|
||||
if w == txt.EmptyString {
|
||||
@@ -243,7 +243,7 @@ func OrLike(col, s string) (where string, values []interface{}) {
|
||||
s = strings.ReplaceAll(s, "*", "%")
|
||||
s = strings.ReplaceAll(s, "%%", "%")
|
||||
|
||||
terms := strings.Split(s, txt.Or)
|
||||
terms := txt.UnTrimmedSplitWithEscape(s, txt.OrRune, txt.EscapeRune)
|
||||
values = make([]interface{}, len(terms))
|
||||
|
||||
if l := len(terms); l == 0 {
|
||||
@@ -271,7 +271,7 @@ func OrLikeCols(cols []string, s string) (where string, values []interface{}) {
|
||||
s = strings.ReplaceAll(s, "*", "%")
|
||||
s = strings.ReplaceAll(s, "%%", "%")
|
||||
|
||||
terms := strings.Split(s, txt.Or)
|
||||
terms := txt.UnTrimmedSplitWithEscape(s, txt.OrRune, txt.EscapeRune)
|
||||
|
||||
if len(terms) == 0 {
|
||||
return "", []interface{}{}
|
||||
@@ -299,37 +299,12 @@ func OrLikeCols(cols []string, s string) (where string, values []interface{}) {
|
||||
return strings.Join(wheres, " OR "), values
|
||||
}
|
||||
|
||||
// Split splits a search string into separate values and trims whitespace.
|
||||
func Split(s string, sep string) (result []string) {
|
||||
if s == "" {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Trim separator and split.
|
||||
s = strings.Trim(s, sep)
|
||||
v := strings.Split(s, sep)
|
||||
|
||||
if len(v) <= 1 {
|
||||
return v
|
||||
}
|
||||
|
||||
result = make([]string, 0, len(v))
|
||||
|
||||
for i := range v {
|
||||
if t := strings.TrimSpace(v[i]); t != "" {
|
||||
result = append(result, t)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// SplitOr splits a search string into separate OR values for an IN condition.
|
||||
func SplitOr(s string) (values []string) {
|
||||
return Split(s, txt.Or)
|
||||
return txt.TrimmedSplitWithEscape(s, txt.OrRune, txt.EscapeRune)
|
||||
}
|
||||
|
||||
// SplitAnd splits a search string into separate AND values.
|
||||
func SplitAnd(s string) (values []string) {
|
||||
return Split(s, txt.And)
|
||||
return txt.TrimmedSplitWithEscape(s, txt.AndRune, txt.EscapeRune)
|
||||
}
|
||||
|
||||
@@ -387,6 +387,18 @@ func TestOrLikeCols(t *testing.T) {
|
||||
assert.Equal(t, "k.keyword LIKE ? OR k.keyword LIKE ? OR p.photo_caption LIKE ? OR p.photo_caption LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"foo%", "bar", "foo%", "bar"}, values)
|
||||
})
|
||||
t.Run("OneTermEscaped", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"k.keyword", "p.photo_caption"}, "\\|bar")
|
||||
|
||||
assert.Equal(t, "k.keyword LIKE ? OR p.photo_caption LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"|bar", "|bar"}, values)
|
||||
})
|
||||
t.Run("TwoTermsEscaped", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"k.keyword", "p.photo_caption"}, "foo*%|\\|bar")
|
||||
|
||||
assert.Equal(t, "k.keyword LIKE ? OR k.keyword LIKE ? OR p.photo_caption LIKE ? OR p.photo_caption LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"foo%", "|bar", "foo%", "|bar"}, values)
|
||||
})
|
||||
t.Run("OneFilename", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"files.file_name"}, " 2790/07/27900704_070228_D6D51B6C.jpg")
|
||||
|
||||
@@ -399,30 +411,20 @@ func TestOrLikeCols(t *testing.T) {
|
||||
assert.Equal(t, "files.file_name LIKE ? OR files.file_name LIKE ? OR photos.photo_name LIKE ? OR photos.photo_name LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"1990%", "2790/07/27900704_070228_D6D51B6C.jpg", "1990%", "2790/07/27900704_070228_D6D51B6C.jpg"}, values)
|
||||
})
|
||||
}
|
||||
t.Run("OneFilenameEscaped", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"files.file_name"}, " 2790/07/27900704_070228_D6D\\|51B6C.jpg")
|
||||
|
||||
func TestSplit(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
values := Split("", "")
|
||||
|
||||
assert.Equal(t, []string{}, values)
|
||||
assert.Equal(t, "files.file_name LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{" 2790/07/27900704_070228_D6D|51B6C.jpg"}, values)
|
||||
})
|
||||
t.Run("FooBar", func(t *testing.T) {
|
||||
values := Split(" foo | Bar ", "&")
|
||||
t.Run("TwoFilenamesEscaped", func(t *testing.T) {
|
||||
where, values := OrLikeCols([]string{"files.file_name", "photos.photo_name"}, "1990*|2790/07/27900704_070228_D6D\\|51B6C.jpg")
|
||||
|
||||
assert.Equal(t, []string{" foo | Bar "}, values)
|
||||
})
|
||||
t.Run("FooAndBar", func(t *testing.T) {
|
||||
values := Split(" foo & Bar ", "o")
|
||||
|
||||
assert.Equal(t, []string{"f", "& Bar"}, values)
|
||||
})
|
||||
t.Run("FooAndBarAndBaz", func(t *testing.T) {
|
||||
values := Split(" foo & Bar&BAZ ", "&")
|
||||
|
||||
assert.Equal(t, []string{"foo", "Bar", "BAZ"}, values)
|
||||
assert.Equal(t, "files.file_name LIKE ? OR files.file_name LIKE ? OR photos.photo_name LIKE ? OR photos.photo_name LIKE ?", where)
|
||||
assert.Equal(t, []interface{}{"1990%", "2790/07/27900704_070228_D6D|51B6C.jpg", "1990%", "2790/07/27900704_070228_D6D|51B6C.jpg"}, values)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSplitOr(t *testing.T) {
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
values := SplitOr("")
|
||||
|
||||
@@ -304,7 +304,7 @@ func TestPhotosFilterFilename(t *testing.T) {
|
||||
t.Run("CenterPipe", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
|
||||
f.Filename = "20|22/vacat|ion/photo|41.jpg"
|
||||
f.Filename = `20\|22/vacat\|ion/photo\|41.jpg`
|
||||
f.Merged = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
@@ -313,7 +313,7 @@ func TestPhotosFilterFilename(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, 0, len(photos))
|
||||
assert.Equal(t, 1, len(photos))
|
||||
})
|
||||
t.Run("EndsWithPipe", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
@@ -482,7 +482,7 @@ func TestPhotosFilterFilename(t *testing.T) {
|
||||
t.Run("OrSearch3", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
|
||||
f.Filename = "*photo|41.jpg | *&photo31.jpg"
|
||||
f.Filename = "*photo\\|41.jpg | *&photo31.jpg"
|
||||
f.Merged = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
@@ -490,7 +490,7 @@ func TestPhotosFilterFilename(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, len(photos), 1)
|
||||
assert.Equal(t, len(photos), 2)
|
||||
})
|
||||
t.Run("OrSearch4", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
@@ -790,7 +790,7 @@ func TestPhotosQueryFilename(t *testing.T) {
|
||||
t.Run("CenterPipe", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
|
||||
f.Query = "filename:\"20|22/vacat|ion/photo|41.jpg\""
|
||||
f.Query = "filename:\"20\\|22/vacat\\|ion/photo\\|41.jpg\""
|
||||
f.Merged = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
@@ -799,7 +799,7 @@ func TestPhotosQueryFilename(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, 0, len(photos))
|
||||
assert.Equal(t, 1, len(photos))
|
||||
})
|
||||
t.Run("EndsWithPipe", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
@@ -971,7 +971,7 @@ func TestPhotosQueryFilename(t *testing.T) {
|
||||
t.Run("OrSearch3", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
|
||||
f.Query = "filename:\"*photo|41.jpg | *&photo31.jpg\""
|
||||
f.Query = "filename:\"*photo\\|41.jpg | *&photo31.jpg\""
|
||||
f.Merged = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
@@ -980,8 +980,7 @@ func TestPhotosQueryFilename(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// TODO: Manual search also finds one result only.
|
||||
assert.Equal(t, len(photos), 1)
|
||||
assert.Equal(t, len(photos), 2)
|
||||
})
|
||||
t.Run("OrSearch4", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
|
||||
@@ -304,7 +304,7 @@ func TestPhotosFilterName(t *testing.T) {
|
||||
t.Run("CenterPipe", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
|
||||
f.Name = "photo|41"
|
||||
f.Name = "photo\\|41"
|
||||
f.Merged = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
@@ -313,7 +313,7 @@ func TestPhotosFilterName(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, 0, len(photos))
|
||||
assert.Equal(t, 1, len(photos))
|
||||
})
|
||||
t.Run("EndsWithPipe", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
@@ -482,7 +482,7 @@ func TestPhotosFilterName(t *testing.T) {
|
||||
t.Run("OrSearch3", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
|
||||
f.Name = "photo|41 | &photo31"
|
||||
f.Name = "photo\\|41 | &photo31"
|
||||
f.Merged = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
@@ -490,7 +490,7 @@ func TestPhotosFilterName(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, 1, len(photos))
|
||||
assert.Equal(t, 2, len(photos))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -781,7 +781,7 @@ func TestPhotosQueryName(t *testing.T) {
|
||||
t.Run("CenterPipe", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
|
||||
f.Query = "name:\"photo|41\""
|
||||
f.Query = "name:\"photo\\|41\""
|
||||
f.Merged = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
@@ -790,7 +790,7 @@ func TestPhotosQueryName(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, 0, len(photos))
|
||||
assert.Equal(t, 1, len(photos))
|
||||
})
|
||||
t.Run("EndsWithPipe", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
@@ -957,7 +957,7 @@ func TestPhotosQueryName(t *testing.T) {
|
||||
t.Run("OrSearch3", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
|
||||
f.Query = "name:\"photo|41 | &photo31\""
|
||||
f.Query = "name:\"photo\\|41 | &photo31\""
|
||||
f.Merged = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
@@ -965,7 +965,7 @@ func TestPhotosQueryName(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, 1, len(photos))
|
||||
assert.Equal(t, 2, len(photos))
|
||||
})
|
||||
t.Run("OrSearch4", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
|
||||
@@ -2357,4 +2357,38 @@ func TestPhotos(t *testing.T) {
|
||||
assert.Empty(t, p.PhotoTitle)
|
||||
}
|
||||
})
|
||||
t.Run("name photo|41", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
|
||||
f.Name = "photo\\|41"
|
||||
f.Merged = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, 1, len(photos))
|
||||
|
||||
for _, p := range photos {
|
||||
assert.NotEmpty(t, p.PhotoName)
|
||||
}
|
||||
})
|
||||
t.Run("name:photo|41", func(t *testing.T) {
|
||||
var f form.SearchPhotos
|
||||
|
||||
f.Query = "name:photo\\|41"
|
||||
f.Merged = true
|
||||
|
||||
photos, _, err := Photos(f)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, 1, len(photos))
|
||||
|
||||
for _, p := range photos {
|
||||
assert.NotEmpty(t, p.PhotoName)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
79
pkg/txt/split.go
Normal file
79
pkg/txt/split.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package txt
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
EscapeRune = '\\'
|
||||
OrRune = '|'
|
||||
AndRune = '&'
|
||||
)
|
||||
|
||||
// Splits a string using a separator allowing escaping that separator with escape and optionally trims the results
|
||||
// If trimming then Trims each result, and doesn't return empty sets
|
||||
// Only applies the escape if the following character is escape or separator
|
||||
// otherwise it keeps both runes
|
||||
func SplitWithEscape(s string, separator rune, escape rune, trim bool) (result []string) {
|
||||
if s == "" {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
result = []string{}
|
||||
|
||||
if !strings.ContainsRune(s, separator) {
|
||||
result = append(result, s)
|
||||
} else {
|
||||
escaped := false
|
||||
var upTo strings.Builder
|
||||
|
||||
for _, rune := range s {
|
||||
if escaped {
|
||||
if rune == escape || rune == separator {
|
||||
upTo.WriteRune(rune)
|
||||
} else {
|
||||
upTo.WriteRune(escape)
|
||||
upTo.WriteRune(rune)
|
||||
}
|
||||
escaped = false
|
||||
} else if rune == escape {
|
||||
escaped = true
|
||||
} else if rune == separator {
|
||||
if trim {
|
||||
if t := strings.TrimSpace(upTo.String()); t != "" {
|
||||
result = append(result, t)
|
||||
}
|
||||
} else {
|
||||
result = append(result, upTo.String())
|
||||
}
|
||||
upTo.Reset()
|
||||
} else {
|
||||
upTo.WriteRune(rune)
|
||||
}
|
||||
}
|
||||
if trim {
|
||||
if t := strings.TrimSpace(upTo.String()); t != "" {
|
||||
result = append(result, t)
|
||||
}
|
||||
} else {
|
||||
result = append(result, upTo.String())
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Splits a string using a separator allowing escaping that separator with escape
|
||||
// Trims each result, and doesn't return empty sets
|
||||
// Only applies the escape if the following character is escape or separator
|
||||
// otherwise it keeps both runes
|
||||
func TrimmedSplitWithEscape(s string, separator rune, escape rune) (result []string) {
|
||||
return SplitWithEscape(s, separator, escape, true)
|
||||
}
|
||||
|
||||
// Splits a string using a separator allowing escaping that separator with escape
|
||||
// Does not trim each result, and will return sets containing just white space if included in string
|
||||
// Only applies the escape if the following character is escape or separator
|
||||
// otherwise it keeps both runes
|
||||
func UnTrimmedSplitWithEscape(s string, separator rune, escape rune) (result []string) {
|
||||
return SplitWithEscape(s, separator, escape, false)
|
||||
}
|
||||
263
pkg/txt/split_test.go
Normal file
263
pkg/txt/split_test.go
Normal file
@@ -0,0 +1,263 @@
|
||||
package txt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSplitWithEscape(t *testing.T) {
|
||||
t.Run("TrimmedEmptyString", func(t *testing.T) {
|
||||
testString := ""
|
||||
expected := []string{}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedNoSeparator", func(t *testing.T) {
|
||||
testString := "I Have No Separators"
|
||||
expected := []string{testString}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedOneEscapeNoSeparator", func(t *testing.T) {
|
||||
testString := `I Have An \ Escape`
|
||||
expected := []string{testString}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedOneSeparatorNoEscape", func(t *testing.T) {
|
||||
testString := "I Have A|Separator"
|
||||
expected := []string{"I Have A", "Separator"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedOneSeparatorNoEscapeLeadingSpaces", func(t *testing.T) {
|
||||
testString := " I Have A|Separator"
|
||||
expected := []string{"I Have A", "Separator"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedOneSeparatorNoEscapeTrailingSpaces", func(t *testing.T) {
|
||||
testString := "I Have A|Separator "
|
||||
expected := []string{"I Have A", "Separator"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedOneSeparatorNoEscapeLeadingSeparatorSpaces", func(t *testing.T) {
|
||||
testString := "I Have A |Separator"
|
||||
expected := []string{"I Have A", "Separator"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedOneSeparatorNoEscapeTrailingSeparatorSpaces", func(t *testing.T) {
|
||||
testString := "I Have A| Separator"
|
||||
expected := []string{"I Have A", "Separator"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedOneSeparatorNoEscapeSpacesEverywhere", func(t *testing.T) {
|
||||
testString := " I Have A | Separator "
|
||||
expected := []string{"I Have A", "Separator"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedOneSeparatorOneEscapedSeparator", func(t *testing.T) {
|
||||
testString := `I Have A|Separator and an \|Escape`
|
||||
expected := []string{"I Have A", "Separator and an |Escape"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedMultipleSeparators", func(t *testing.T) {
|
||||
testString := "One|Two|Three|Four|Five"
|
||||
expected := []string{"One", "Two", "Three", "Four", "Five"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedMultipleSeparatorsLeadingBlank", func(t *testing.T) {
|
||||
testString := "|One|Two|Three|Four|Five"
|
||||
expected := []string{"One", "Two", "Three", "Four", "Five"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedMultipleSeparatorsTrailingBlank", func(t *testing.T) {
|
||||
testString := "One|Two|Three|Four|Five|"
|
||||
expected := []string{"One", "Two", "Three", "Four", "Five"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedMultipleSeparatorsLeadingBlankWithSpaces", func(t *testing.T) {
|
||||
testString := " | One | Two | Three | Four | Five "
|
||||
expected := []string{"One", "Two", "Three", "Four", "Five"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedMultipleSeparatorsTrailingBlankWithSpaces", func(t *testing.T) {
|
||||
testString := " One | Two | Three | Four | Five | "
|
||||
expected := []string{"One", "Two", "Three", "Four", "Five"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedMultipleSeparatorsTrailingBlankWithSpacesAndEscapes", func(t *testing.T) {
|
||||
testString := ` One | Two | Three | Four | Five | Si\x | Sev\|en | `
|
||||
expected := []string{"One", "Two", "Three", "Four", "Five", `Si\x`, "Sev|en"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedFooBar", func(t *testing.T) {
|
||||
testString := ` foo & Bar&BAZ `
|
||||
expected := []string{"foo", "Bar", "BAZ"}
|
||||
actual := SplitWithEscape(testString, AndRune, EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("TrimmedFooBarNoSeparator", func(t *testing.T) {
|
||||
testString := ` foo & Bar&BAZ `
|
||||
expected := []string{` foo & Bar&BAZ `}
|
||||
actual := SplitWithEscape(testString, OrRune, EscapeRune, true)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
|
||||
t.Run("UnTrimmedEmptyString", func(t *testing.T) {
|
||||
testString := ""
|
||||
expected := []string{}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedNoSeparator", func(t *testing.T) {
|
||||
testString := "I Have No Separators"
|
||||
expected := []string{testString}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedOneEscapeNoSeparator", func(t *testing.T) {
|
||||
testString := `I Have An \ Escape`
|
||||
expected := []string{testString}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedOneSeparatorNoEscape", func(t *testing.T) {
|
||||
testString := "I Have A|Separator"
|
||||
expected := []string{"I Have A", "Separator"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedOneSeparatorNoEscapeLeadingSpaces", func(t *testing.T) {
|
||||
testString := " I Have A|Separator"
|
||||
expected := []string{" I Have A", "Separator"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedOneSeparatorNoEscapeTrailingSpaces", func(t *testing.T) {
|
||||
testString := "I Have A|Separator "
|
||||
expected := []string{"I Have A", "Separator "}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedOneSeparatorNoEscapeLeadingSeparatorSpaces", func(t *testing.T) {
|
||||
testString := "I Have A |Separator"
|
||||
expected := []string{"I Have A ", "Separator"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedOneSeparatorNoEscapeTrailingSeparatorSpaces", func(t *testing.T) {
|
||||
testString := "I Have A| Separator"
|
||||
expected := []string{"I Have A", " Separator"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedOneSeparatorNoEscapeSpacesEverywhere", func(t *testing.T) {
|
||||
testString := " I Have A | Separator "
|
||||
expected := []string{" I Have A ", " Separator "}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedOneSeparatorOneEscapedSeparator", func(t *testing.T) {
|
||||
testString := `I Have A|Separator and an \|Escape`
|
||||
expected := []string{"I Have A", "Separator and an |Escape"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedMultipleSeparators", func(t *testing.T) {
|
||||
testString := "One|Two|Three|Four|Five"
|
||||
expected := []string{"One", "Two", "Three", "Four", "Five"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedMultipleSeparatorsLeadingBlank", func(t *testing.T) {
|
||||
testString := "|One|Two|Three|Four|Five"
|
||||
expected := []string{"", "One", "Two", "Three", "Four", "Five"}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedMultipleSeparatorsTrailingBlank", func(t *testing.T) {
|
||||
testString := "One|Two|Three|Four|Five|"
|
||||
expected := []string{"One", "Two", "Three", "Four", "Five", ""}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedMultipleSeparatorsLeadingBlankWithSpaces", func(t *testing.T) {
|
||||
testString := " | One | Two | Three | Four | Five "
|
||||
expected := []string{" ", " One ", " Two ", " Three ", " Four ", " Five "}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedMultipleSeparatorsTrailingBlankWithSpaces", func(t *testing.T) {
|
||||
testString := " One | Two | Three | Four | Five | "
|
||||
expected := []string{" One ", " Two ", " Three ", " Four ", " Five ", " "}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedMultipleSeparatorsTrailingBlankWithSpacesAndEscapes", func(t *testing.T) {
|
||||
testString := ` One | Two | Three | Four | Five | Si\x | Sev\|en | `
|
||||
expected := []string{" One ", " Two ", " Three ", " Four ", " Five ", ` Si\x `, " Sev|en ", " "}
|
||||
actual := SplitWithEscape(testString, '|', EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedFooBar", func(t *testing.T) {
|
||||
testString := ` foo & Bar&BAZ `
|
||||
expected := []string{" foo ", " Bar", "BAZ "}
|
||||
actual := SplitWithEscape(testString, AndRune, EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
t.Run("UnTrimmedFooBarNoSeparator", func(t *testing.T) {
|
||||
testString := ` foo & Bar&BAZ `
|
||||
expected := []string{` foo & Bar&BAZ `}
|
||||
actual := SplitWithEscape(testString, OrRune, EscapeRune, false)
|
||||
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user