Skip to content

Commit 18f9540

Browse files
author
Tim Bunce
committed
Make safe for use with concurrent goroutines (fix race hazards)
Also removes the forcing of go version 1.13 since it's not needed.
1 parent 6c8aaf6 commit 18f9540

File tree

4 files changed

+77
-17
lines changed

4 files changed

+77
-17
lines changed

doc.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,23 @@ Example:
3838
}
3939
textSub := slug.Make("water is hot")
4040
fmt.Println(textSub) // Will print: "sand-is-hot"
41+
42+
// as above but goroutine safe, without race hazard
43+
44+
slugger := slug.New() // captures current global defaults
45+
46+
enText := slugger.MakeLang("This & that", "en")
47+
fmt.Println(enText) // Will print: "this-and-that"
48+
49+
slugger.Lowercase = false // Keep uppercase characters
50+
deUppercaseText := slugger.MakeLang("Diese & Dass", "de")
51+
fmt.Println(deUppercaseText) // Will print: "Diese-und-Dass"
52+
53+
slugger.CustomSub = map[string]string{
54+
"water": "sand",
55+
}
56+
textSub := slugger.Make("water is hot")
57+
fmt.Println(textSub) // Will print: "sand-is-hot"
4158
}
4259
4360
Requests or bugs?

go.mod

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11
module github.com/gosimple/slug
22

3-
go 1.13
4-
53
require github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be

slug.go

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,26 @@ var (
3636
regexpMultipleDashes = regexp.MustCompile("-+")
3737
)
3838

39+
// Slugger configures slug generation
40+
type Slugger struct {
41+
// CustomSub stores custom substitution map
42+
CustomSub map[string]string
43+
// CustomRuneSub stores custom rune substitution map
44+
CustomRuneSub map[rune]string
45+
46+
// MaxLength stores maximum slug length.
47+
// It's smart so it will cat slug after full word.
48+
// By default slugs aren't shortened.
49+
// If MaxLength is smaller than length of the first word, then returned
50+
// slug will contain only substring from the first word truncated
51+
// after MaxLength.
52+
MaxLength int
53+
54+
// Lowercase defines if the resulting slug is transformed to lowercase.
55+
// Default is true.
56+
Lowercase bool
57+
}
58+
3959
//=============================================================================
4060

4161
// Make returns slug generated from provided string. Will use "en" as language
@@ -47,12 +67,28 @@ func Make(s string) (slug string) {
4767
// MakeLang returns slug generated from provided string and will use provided
4868
// language for chars substitution.
4969
func MakeLang(s string, lang string) (slug string) {
70+
return New().MakeLang(s, lang)
71+
}
72+
73+
// New returns a Slugger initialized with the current global defaults
74+
func New() Slugger {
75+
return Slugger{
76+
CustomSub: CustomSub,
77+
CustomRuneSub: CustomRuneSub,
78+
MaxLength: MaxLength,
79+
Lowercase: Lowercase,
80+
}
81+
}
82+
83+
// MakeLang returns slug generated from provided string and will use provided
84+
// language for chars substitution.
85+
func (sl Slugger) MakeLang(s string, lang string) (slug string) {
5086
slug = strings.TrimSpace(s)
5187

5288
// Custom substitutions
5389
// Always substitute runes first
54-
slug = SubstituteRune(slug, CustomRuneSub)
55-
slug = Substitute(slug, CustomSub)
90+
slug = SubstituteRune(slug, sl.CustomRuneSub)
91+
slug = Substitute(slug, sl.CustomSub)
5692

5793
// Process string with selected substitution language.
5894
// Catch ISO 3166-1, ISO 639-1:2002 and ISO 639-3:2007.
@@ -84,7 +120,7 @@ func MakeLang(s string, lang string) (slug string) {
84120
// Process all non ASCII symbols
85121
slug = unidecode.Unidecode(slug)
86122

87-
if Lowercase {
123+
if sl.Lowercase {
88124
slug = strings.ToLower(slug)
89125
}
90126

@@ -93,8 +129,8 @@ func MakeLang(s string, lang string) (slug string) {
93129
slug = regexpMultipleDashes.ReplaceAllString(slug, "-")
94130
slug = strings.Trim(slug, "-_")
95131

96-
if MaxLength > 0 {
97-
slug = smartTruncate(slug)
132+
if sl.MaxLength > 0 {
133+
slug = smartTruncate(slug, sl.MaxLength)
98134
}
99135

100136
return slug
@@ -131,20 +167,20 @@ func SubstituteRune(s string, sub map[rune]string) string {
131167
return buf.String()
132168
}
133169

134-
func smartTruncate(text string) string {
135-
if len(text) < MaxLength {
170+
func smartTruncate(text string, maxLength int) string {
171+
if len(text) < maxLength {
136172
return text
137173
}
138174

139175
var truncated string
140176
words := strings.SplitAfter(text, "-")
141-
// If MaxLength is smaller than length of the first word return word
142-
// truncated after MaxLength.
143-
if len(words[0]) > MaxLength {
144-
return words[0][:MaxLength]
177+
// If maxLength is smaller than length of the first word return word
178+
// truncated after maxLength.
179+
if len(words[0]) > maxLength {
180+
return words[0][:maxLength]
145181
}
146182
for _, word := range words {
147-
if len(truncated)+len(word)-1 <= MaxLength {
183+
if len(truncated)+len(word)-1 <= maxLength {
148184
truncated = truncated + word
149185
} else {
150186
break
@@ -159,8 +195,17 @@ func smartTruncate(text string) string {
159195
// It should be in range of the MaxLength var if specified.
160196
// All output from slug.Make(text) should pass this test.
161197
func IsSlug(text string) bool {
198+
return Slugger{MaxLength: MaxLength}.IsSlug(text)
199+
}
200+
201+
// IsSlug returns True if provided text does not contain white characters,
202+
// punctuation, all letters are lower case and only from ASCII range.
203+
// It could contain `-` and `_` but not at the beginning or end of the text.
204+
// It should be in range of the MaxLength var if specified.
205+
// All output from slug.Make(text) should pass this test.
206+
func (sl Slugger) IsSlug(text string) bool {
162207
if text == "" ||
163-
(MaxLength > 0 && len(text) > MaxLength) ||
208+
(sl.MaxLength > 0 && len(text) > sl.MaxLength) ||
164209
text[0] == '-' || text[0] == '_' ||
165210
text[len(text)-1] == '-' || text[len(text)-1] == '_' {
166211
return false

slug_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ func BenchmarkSmartTruncateShort(b *testing.B) {
403403
b.ReportAllocs()
404404
b.ResetTimer()
405405
for n := 0; n < b.N; n++ {
406-
smartTruncate(shortStr)
406+
smartTruncate(shortStr, MaxLength)
407407
}
408408
}
409409

@@ -428,7 +428,7 @@ func BenchmarkSmartTruncateLong(b *testing.B) {
428428
b.ReportAllocs()
429429
b.ResetTimer()
430430
for n := 0; n < b.N; n++ {
431-
smartTruncate(longStr)
431+
smartTruncate(longStr, MaxLength)
432432
}
433433
}
434434

0 commit comments

Comments
 (0)