Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add possibility to set custom separator #27

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 5 additions & 4 deletions languages_substitution.go
Expand Up @@ -16,14 +16,15 @@ func init() {
}
}

// BUG: Initialized on init so also impossible to set custom separator.
var defaultSub = map[rune]string{
'"': "",
'\'': "",
'’': "",
'‒': "-", // figure dash
'–': "-", // en dash
'—': "-", // em dash
'―': "-", // horizontal bar
'‒': string(Separator), // figure dash
'–': string(Separator), // en dash
'—': string(Separator), // em dash
'―': string(Separator), // horizontal bar
}

var deSub = map[rune]string{
Expand Down
27 changes: 18 additions & 9 deletions slug.go
Expand Up @@ -28,8 +28,13 @@ var (
// after MaxLength.
MaxLength int

regexpNonAuthorizedChars = regexp.MustCompile("[^a-z0-9-_]")
regexpMultipleDashes = regexp.MustCompile("-+")
// Separator configure default separator for all slugs.
// By default set to '-'.
Separator = '-'

// BUG: Not possible to change separator from default.
regexpNonAuthorizedChars = regexp.MustCompile("[^a-z0-9-_" + string(Separator) + "]")
regexpMultipleSeparators = regexp.MustCompile(string(Separator) + "+")
)

//=============================================================================
Expand Down Expand Up @@ -76,14 +81,15 @@ func MakeLang(s string, lang string) (slug string) {
slug = strings.ToLower(slug)

// Process all remaining symbols
slug = regexpNonAuthorizedChars.ReplaceAllString(slug, "-")
slug = regexpMultipleDashes.ReplaceAllString(slug, "-")
slug = strings.Trim(slug, "-_")
slug = regexpNonAuthorizedChars.ReplaceAllString(slug, string(Separator))
slug = regexpMultipleSeparators.ReplaceAllString(slug, string(Separator))

if MaxLength > 0 {
slug = smartTruncate(slug)
}

slug = strings.Trim(slug, "-_"+string(Separator))

return slug
}

Expand Down Expand Up @@ -137,7 +143,7 @@ func smartTruncate(text string) string {
break
}
}
return strings.Trim(truncated, "-")
return truncated
}

// IsSlug returns True if provided text does not contain white characters,
Expand All @@ -148,12 +154,15 @@ func smartTruncate(text string) string {
func IsSlug(text string) bool {
if text == "" ||
(MaxLength > 0 && len(text) > MaxLength) ||
text[0] == '-' || text[0] == '_' ||
text[len(text)-1] == '-' || text[len(text)-1] == '_' {
strings.HasPrefix(text, "-") || strings.HasPrefix(text, "_") ||
strings.HasPrefix(text, string(Separator)) ||
strings.HasSuffix(text, "-") || strings.HasSuffix(text, "_") ||
strings.HasSuffix(text, string(Separator)) {
return false
}
for _, c := range text {
if (c < 'a' || c > 'z') && c != '-' && c != '_' && (c < '0' || c > '9') {
if (c < 'a' || c > 'z') && (c < '0' || c > '9') &&
c != '-' && c != '_' && c != Separator {
return false
}
}
Expand Down
90 changes: 90 additions & 0 deletions slug_test.go
Expand Up @@ -208,6 +208,7 @@ func TestSlugMakeSmartTruncate(t *testing.T) {
{"DOBROSLAWZYBORT", 100, "dobroslawzybort"},
{"Dobroslaw Zybort", 100, "dobroslaw-zybort"},
{"Dobroslaw Zybort", 12, "dobroslaw"},
{"Dobroslaw-Zybort_-_-", 11, "dobroslaw"},
{" Dobroslaw Zybort ?", 12, "dobroslaw"},
{"Ala ma 6 kotów.", 10, "ala-ma-6"},
{"Dobrosław Żybort", 5, "dobro"},
Expand Down Expand Up @@ -266,6 +267,95 @@ func TestIsSlug(t *testing.T) {
})
}

func TestSeparatorSlug(t *testing.T) {
MaxLength = 0
type args struct {
separator rune
text string
}
tests := []struct {
name string
args args
want string
}{
{"separator -", args{'-', "test---slug"}, "test-slug"},
{"separator _", args{'_', "test___slug"}, "test_slug"},
{"separator /", args{'/', "test///slug"}, "test/slug"},
{"separator ☺", args{'☺', "test slug"}, "test☺slug"},
{"remove ASCII first", args{'☺', "test☺☺slug ☺"}, "testslug"},
}
for _, tt := range tests {
Separator = tt.args.separator

t.Run(tt.name, func(t *testing.T) {
if got := Make(tt.args.text); got != tt.want {
t.Errorf("Make() = %v, want %v", got, tt.want)
}
})
}
// Global state...
Separator = '-'
}

func TestSeparatorSlugMakeLang(t *testing.T) {
var testCases = []struct {
separator rune
lang string
in string
want string
}{
// & fun.
{'_', "de", "This & that", "this_und_that"},
{'_', "en", "This & that", "this_and_that"},
{'_', "test", "This & that", "this_and_that"}, // unknown lang, fallback to "en"
// Test defaultSub.
{'_', "de", "1\"2'3’4‒5–6—7―8", "1234_5_6_7_8"},
{'_', "en", "1\"2'3’4‒5–6—7―8", "1234_5_6_7_8"},
}

for index, smlt := range testCases {
Separator = smlt.separator
got := MakeLang(smlt.in, smlt.lang)
if got != smlt.want {
t.Errorf(
"%d. MakeLang(%#v, %#v) = %#v; want %#v",
index, smlt.in, smlt.lang, got, smlt.want)
}
}
// Global state...
Separator = '-'
}

func TestSeparatorIsSlug(t *testing.T) {
MaxLength = 0
type args struct {
separator rune
text string
}
tests := []struct {
name string
args args
want bool
}{
{"separator -", args{'-', "test slug"}, true},
{"separator _", args{'_', "test slug"}, true},
{"separator /", args{'/', "test slug"}, true},
{"separator ☺", args{'☺', "test slug"}, true},
}
for _, tt := range tests {
Separator = tt.args.separator

t.Run(tt.name, func(t *testing.T) {
slug := Make(tt.args.text)
if got := IsSlug(slug); got != tt.want {
t.Errorf("IsSlug('%s') = %v, want %v", slug, got, tt.want)
}
})
}
// Global state...
Separator = '-'
}

func BenchmarkMakeShortAscii(b *testing.B) {
b.ReportAllocs()
for n := 0; n < b.N; n++ {
Expand Down