Slug: Combine various slugify fixes for special character handling (#73164)

* combine various slugify fixes for special character handling

* a couple more test cases

* update more tests

* goimports
This commit is contained in:
Dan Cech 2023-08-10 13:12:50 -07:00 committed by GitHub
parent 3bc3c4f2bb
commit dd97038b00
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 40 additions and 14 deletions

View File

@ -42,6 +42,7 @@ import (
var (
simpleSlugger = &slugger{
isValidCharacter: validCharacter,
replaceCharacter: '-',
replacementMap: getDefaultReplacements(),
omitMap: getDefaultOmitments(),
}
@ -64,14 +65,12 @@ func validCharacter(c rune) bool {
if c >= '0' && c <= '9' {
return true
}
if c == '_' || c == '-' {
return true
}
return false
}
// Slugifier based on settings
type slugger struct {
replaceCharacter rune
isValidCharacter func(c rune) bool
replacementMap map[rune]string
omitMap map[rune]struct{}
@ -81,37 +80,52 @@ type slugger struct {
func (s slugger) Slugify(value string) string {
value = strings.ToLower(value)
var buffer bytes.Buffer
lastCharacterWasInvalid := false
for len(value) > 0 {
c, size := utf8.DecodeRuneInString(value)
value = value[size:]
if newCharacter, ok := s.replacementMap[c]; ok {
if lastCharacterWasInvalid {
buffer.WriteRune(s.replaceCharacter)
}
buffer.WriteString(newCharacter)
lastCharacterWasInvalid = false
continue
}
if s.isValidCharacter(c) {
if lastCharacterWasInvalid {
buffer.WriteRune(s.replaceCharacter)
}
buffer.WriteRune(c)
lastCharacterWasInvalid = false
continue
}
if _, ok := s.omitMap[c]; ok {
lastCharacterWasInvalid = true
continue
}
p := make([]byte, 4)
size = utf8.EncodeRune(p, c)
for i := 0; i < size; i++ {
buffer.WriteString(fmt.Sprintf("%%%x", p[i]))
if lastCharacterWasInvalid {
buffer.WriteRune(s.replaceCharacter)
}
for i := 0; i < size; i++ {
buffer.WriteString(fmt.Sprintf("%x", p[i]))
}
lastCharacterWasInvalid = true
}
return buffer.String()
return strings.Trim(buffer.String(), string(s.replaceCharacter))
}
func getDefaultOmitments() map[rune]struct{} {
return map[rune]struct{}{
' ': {},
',': {},
'"': {},
'\'': {},
@ -122,13 +136,21 @@ func getDefaultOmitments() map[rune]struct{} {
'.': {},
'(': {},
')': {},
'-': {},
'_': {},
'[': {},
']': {},
'/': {},
'\\': {},
'!': {},
'{': {},
'}': {},
'%': {},
}
}
func getDefaultReplacements() map[rune]string {
return map[rune]string{
' ': "-",
'&': "and",
'@': "at",
'©': "c",

View File

@ -9,12 +9,16 @@ func TestSlugify(t *testing.T) {
results["hello-playground"] = "Hello, playground"
results["00a4bc92-3695-5702-9ddf-6719fdf11567"] = "😢 😣 😤 😥 😦 😧 😨 😩 😪 😫 😬 Hello, it's paradise"
results["61db60b5-f1e7-5853-9b81-0f074fc268ea"] = "😢 😣 😤 😥 😦 😧 😨 😩 😪 😫 😬"
results["%f0%9f%98%a2--"] = "😢 -"
results["a-"] = "?,a . \n "
results["f09f98a2"] = "😢 -"
results["a"] = "?,a . \n "
results["0a68eb57-c88a-5f34-9e9d-27f85e68af4f"] = "" // empty input has a slug!
results["3cbb528a-0ebf-54ad-bed2-2a188cd1824e"] = "方向盤後面 hi this is a test خلف المقو"
results["cong-hoa-xa-hoi-chu-nghia-viet-nam"] = "Cộng hòa xã hội chủ nghĩa Việt Nam"
results["noi-nang-canh-canh-ben-long-bieng-khuay"] = "Nỗi nàng canh cánh bên lòng biếng khuây" // This line in a poem called Truyen Kieu
results["hello-playground"] = "Hello / playground"
results["hello-playground"] = "Hello % playground"
results["hello-and-playground"] = "Hello & //% playground"
results["hello-2a-23-playground"] = "Hello *# playground"
for slug, original := range results {
actual := Slugify(original)

View File

@ -64,7 +64,7 @@ func TestSaveExternalServiceRoleCommand_Validate(t *testing.T) {
Permissions: []Permission{{Action: "users:read", Scope: "users:id:1"}},
},
wantErr: false,
wantID: "thisis-a-very-strange-___-app-name",
wantID: "thisis-a-very-strange-app-name",
},
{
name: "invalid empty Action",

View File

@ -77,9 +77,9 @@ func TestSlugifyTitle(t *testing.T) {
testCases := map[string]string{
"Grafana Play Home": "grafana-play-home",
"snöräv-över-ån": "snorav-over-an",
"漢字": "%e6%bc%a2%e5%ad%97", // "han-zi", // Hanzi for hanzi
"🇦🇶": "%f0%9f%87%a6%f0%9f%87%b6", // flag of Antarctica-emoji, using fallback
"𒆠": "%f0%92%86%a0", // cuneiform Ki, using fallback
"漢字": "e6bca2-e5ad97", // "han-zi", // Hanzi for hanzi
"🇦🇶": "f09f87a6-f09f87b6", // flag of Antarctica-emoji, using fallback
"𒆠": "f09286a0", // cuneiform Ki, using fallback
}
for input, expected := range testCases {