diff --git a/.gitignore b/.gitignore index 35620a1..200554a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ # Dependency directories vendor + +# IDE Files +.idea/ diff --git a/str/str.go b/str/str.go index fcdb32c..856a124 100644 --- a/str/str.go +++ b/str/str.go @@ -1,6 +1,10 @@ package str -import "unicode" +import ( + "regexp" + "strings" + "unicode" +) func UpperFirst(input string) string { if len(input) == 0 { @@ -19,3 +23,217 @@ func InSlice(input interface{}, expects ...interface{}) bool { } return false } + +// Return the remainder of a string after the first occurrence of a given value. +func After(subject string, search string) string { + if len(search) == 0 { + return subject + } + results := strings.SplitN(subject, search, 2) + return results[len(results)-1] +} + +// Return the remainder of a string after the last occurrence of a given value. +func AfterLast(subject string, search string) string { + if len(search) == 0 { + return subject + } + position := strings.LastIndex(subject, search) + + if position == -1 { + return subject + } + + return subject[position+len(search):] +} + +// Get the portion of a string before the first occurrence of a given value. +func Before(subject string, search string) string { + if len(search) == 0 { + return subject + } + position := strings.Index(subject, search) + + if position == -1 { + return subject + } + + return subject[:position] +} + +func BeforeLast(subject string, search string) string { + if len(search) == 0 { + return subject + } + position := strings.LastIndex(subject, search) + + if position == -1 { + return subject + } + + return subject[:position] +} + +func Between(subject string, from string, to string) string { + if len(from) == 0 || len(to) == 0 { + return subject + } + + return BeforeLast(After(subject, from), to) +} + +func Contains(haystack string, needle string) bool { + if len(needle) == 0 { + return false + } + + return strings.Contains(haystack, needle) +} + +func ContainsFromSlice(haystack string, needles []string) bool { + if len(needles) == 0 { + return false + } + + for _, needle := range needles { + if Contains(haystack, needle){ + return true + } + } + + return false +} + +func ContainsAllFromSlice(haystack string, needles []string) bool { + if len(needles) == 0 { + return false + } + + for _, needle := range needles { + if !Contains(haystack, needle){ + return false + } + } + + return true +} + +func EndsWith(haystack string, needle string) bool { + if len(needle) == 0 { + return false + } + + return strings.HasSuffix(haystack, needle) +} + +func StartsWith(haystack string, needle string) bool { + if len(needle) == 0 { + return false + } + + return strings.HasPrefix(haystack, needle) +} + +func Lower(value string) string { + return strings.ToLower(value) +} + +func Upper(value string) string { + return strings.ToUpper(value) +} + +func Finish(value string, cap string) string { + quoted := regexp.QuoteMeta(cap) + + re := regexp.MustCompile("(?:"+ quoted +")+$") + return re.ReplaceAllString(value, "") + cap +} + +func Start(value string, prefix string) string { + quoted := regexp.QuoteMeta(prefix) + + re := regexp.MustCompile("^(?:"+ quoted +")+") + return prefix + re.ReplaceAllString(value, "") +} + +// +//func Title(value string) string { +// // TODO +// return "" +//} +// +// func Kebab(vale string) string { +// // TODO +// return "" +// } +// +// func Length(value string) int { +// // TODO +// return 0 +// } +// +// func LimitCharacters(value string, limit int, end string) string{ +// // TODO +// return "" +// } +// +// func LimitWords(value string, limit int, end string) string{ +// // TODO +// return "" +// } +// +// func PadBoth(value string, length int, pad string) string { +// // TODO +// return "" +// } +// +// func PadLeft(value string, length int, pad string) string { +// // TODO +// return "" +// } +// +// func PadRight(value string, length int, pad string) string { +// // TODO +// return "" +// } +// +// func ReplaceArray(search string, replace []string, subject string) string { +// // TODO +// return "" +// } +// +// func ReplaceFirst(search string, replace string, subject string) string { +// // TODO +// return "" +// } +// +// func ReplaceLast(search string, replace string, subject string) string { +// // TODO +// return "" +// } +// +// func Slug(value string) string { +// // TODO +// return "" +// } +// +// func SlugWithDelimiter(value string, delimiter string) string { +// // TODO +// return "" +// } +// +// func Snake(value string) string { +// // TODO +// return "" +// } +// +// func Studly(value string) string { +// // TODO +// return "" +// } +// +// func UcFirst(value string) string { +// // TODO +// return "" +// } +// diff --git a/test/str_test.go b/test/str_test.go index c5719c3..3780880 100644 --- a/test/str_test.go +++ b/test/str_test.go @@ -35,3 +35,159 @@ func Test_in_slice_with_multiple_one_matched_parameters(t *testing.T) { func Test_in_slice_with_integer(t *testing.T) { require.True(t, str.InSlice(1, 0, 1)) } + +func Test_After(t *testing.T) { + // TODO: What if nothing is found? + require.Equal(t, "", str.After("", "")) + require.Equal(t, "", str.After("", "han")) + require.Equal(t, "hannah", str.After("hannah", "")) + require.Equal(t, "nah", str.After("hannah", "han")) + require.Equal(t, "nah", str.After("hannah", "n")) + require.Equal(t, "nah", str.After("eee hannah", "han")) + require.Equal(t, "nah", str.After("ééé hannah", "han")) + require.Equal(t, "hannah", str.After("hannah", "xxxx")) + require.Equal(t, "nah", str.After("han0nah", "0")) + require.Equal(t, "nah", str.After("han2nah", "2")) +} + +func Test_AfterLast(t *testing.T) { + // TODO: What if nothing is found? + require.Equal(t, "", str.After("", "")) + require.Equal(t, "", str.After("", "han")) + require.Equal(t, "hannah", str.After("hannah", "")) + require.Equal(t, "tte", str.AfterLast("yvette", "yve")) + require.Equal(t, "e", str.AfterLast("yvette", "t")) + require.Equal(t, "e", str.AfterLast("ééé yvette", "t")) + require.Equal(t, "", str.AfterLast("yvette", "tte")) + require.Equal(t, "yvette", str.AfterLast("yvette", "xxxx")) + require.Equal(t, "te", str.AfterLast("yv0et0te", "0")) + require.Equal(t, "te", str.AfterLast("yv0et0te", "0")) + require.Equal(t, "te", str.AfterLast("yv2et2te", "2")) + require.Equal(t, "foo", str.AfterLast("----foo", "---")) +} + +func Test_Before(t *testing.T) { + require.Equal(t, "hannah", str.Before("hannah", "")) + require.Equal(t, "han", str.Before("hannah", "nah")) + require.Equal(t, "ha", str.Before("hannah", "n")) + require.Equal(t, "ééé ", str.Before("ééé hannah", "han")) + require.Equal(t, "hannah", str.Before("hannah", "xxxx")) + require.Equal(t, "han", str.Before("han0nah", "0")) + require.Equal(t, "han", str.Before("han0nah", "0")) + require.Equal(t, "han", str.Before("han2nah", "2")) +} + +func Test_BeforeLast(t *testing.T) { + require.Equal(t, "yve", str.BeforeLast("yvette", "tte")) + require.Equal(t, "yvet", str.BeforeLast("yvette", "t")) + require.Equal(t, "ééé ", str.BeforeLast("ééé yvette", "yve")) + require.Equal(t, "", str.BeforeLast("yvette", "yve")) + require.Equal(t, "yvette", str.BeforeLast("yvette", "xxxx")) + require.Equal(t, "yvette", str.BeforeLast("yvette", "")) + require.Equal(t, "yv0et", str.BeforeLast("yv0et0te", "0")) + require.Equal(t, "yv0et", str.BeforeLast("yv0et0te", "0")) + require.Equal(t, "yv2et", str.BeforeLast("yv2et2te", "2")) +} + +func Test_Between(t *testing.T) { + require.Equal(t, "abc", str.Between("abc", "", "c")) + require.Equal(t, "abc", str.Between("abc", "a", "")) + require.Equal(t, "abc", str.Between("abc", "", "")) + require.Equal(t, "b", str.Between("abc", "a", "c")) + require.Equal(t, "b", str.Between("dddabc", "a", "c")) + require.Equal(t, "b", str.Between("abcddd", "a", "c")) + require.Equal(t, "b", str.Between("dddabcddd", "a", "c")) + require.Equal(t, "nn", str.Between("hannah", "ha", "ah")) + require.Equal(t, "a]ab[b", str.Between("[a]ab[b]", "[", "]")) + require.Equal(t, "foo", str.Between("foofoobar", "foo", "bar")) + require.Equal(t, "bar", str.Between("foobarbar", "foo", "bar")) +} + +func Test_Contains(t *testing.T) { + require.True(t, str.Contains("taylor", "ylo")) + require.True(t, str.Contains("taylor", "taylor")) + require.False(t, str.Contains("taylor", "xxx")) + require.False(t, str.Contains("taylor", "")) + require.False(t, str.Contains("", "")) +} + +func Test_ContainsFromSlice(t *testing.T) { + require.True(t, str.ContainsFromSlice("taylor", []string{"ylo"})) + require.True(t, str.ContainsFromSlice("taylor", []string{"xxx", "ylo"})) + require.False(t, str.ContainsFromSlice("taylor", []string{"xxx"})) + require.False(t, str.ContainsFromSlice("taylor", []string{})) + require.False(t, str.ContainsFromSlice("taylor", []string{""})) +} + +func Test_ContainsAllFromSlice(t *testing.T) { + require.True(t, str.ContainsAllFromSlice("This is my name", []string{"This", "is"})) + require.True(t, str.ContainsAllFromSlice("This is my name", []string{"my", "ame"})) + require.True(t, str.ContainsAllFromSlice("taylor", []string{"tay", "ylo"})) + require.False(t, str.ContainsAllFromSlice("taylor", []string{"xxx", "ylo"})) + require.False(t, str.ContainsAllFromSlice("taylor", []string{"xxx", "tay"})) + require.False(t, str.ContainsAllFromSlice("This is my name", []string{"are", "name"})) + require.False(t, str.ContainsAllFromSlice("taylor", []string{})) + require.False(t, str.ContainsAllFromSlice("taylor", []string{"", ""})) +} + +func Test_EndsWith(t *testing.T) { + require.True(t, str.EndsWith("This is my name", "name")) + require.True(t, str.EndsWith("This is my name", "e")) + require.True(t, str.EndsWith("jason", "on")) + require.True(t, str.EndsWith("7", "7")) + require.True(t, str.EndsWith("a7", "7")) + require.False(t, str.EndsWith("jason", "no")) + require.False(t, str.EndsWith("jason", "")) + require.False(t, str.EndsWith("", "")) + // Test for multibyte string support + require.True(t, str.EndsWith("Jönköping", "öping")) + require.True(t, str.EndsWith("Malmö", "mö")) + require.True(t, str.EndsWith("Malmö", "mö")) + require.False(t, str.EndsWith("Jönköping", "oping")) + require.False(t, str.EndsWith("Malmö", "mo")) + require.True(t, str.EndsWith("你好", "好")) + require.False(t, str.EndsWith("你好", "你")) + require.False(t, str.EndsWith("你好", "a")) +} + +func Test_StartsWith(t *testing.T) { + require.True(t, str.StartsWith("jason", "jas")) + require.True(t, str.StartsWith("jason", "jason")) + require.True(t, str.StartsWith("7a", "7")) + require.True(t, str.StartsWith("7", "7")) + require.False(t, str.StartsWith("jason", "J")) + require.False(t, str.StartsWith("jason", "")) + require.False(t, str.StartsWith("", "")) + // Test for multibyte string support + require.True(t, str.StartsWith("Jönköping", "Jö")) + require.True(t, str.StartsWith("Malmö", "Malmö")) + require.True(t, str.StartsWith("你好", "你")) + require.False(t, str.StartsWith("Jönköping", "Jonko")) + require.False(t, str.StartsWith("Malmö", "Malmo")) + require.False(t, str.StartsWith("你好", "好")) + require.False(t, str.StartsWith("你好", "a")) +} + +func Test_Lower(t *testing.T) { + require.Equal(t, "foo bar baz", str.Lower("FOO BAR BAZ")) + require.Equal(t, "foo bar baz", str.Lower("fOo Bar bAz")) +} + +func Test_Upper(t *testing.T) { + require.Equal(t, "FOO BAR BAZ", str.Upper("foo bar baz")) + require.Equal(t, "FOO BAR BAZ", str.Upper("fOo Bar bAZ")) +} + +func Test_Finish(t *testing.T) { + require.Equal(t, "abbc", str.Finish("ab", "bc")) + require.Equal(t, "abbc", str.Finish("abbcbc", "bc")) + require.Equal(t, "abcbbc", str.Finish("abcbbcbc", "bc")) + require.Equal(t, "this/string/", str.Finish("this/string", "/")) + require.Equal(t, "this/string/", str.Finish("this/string/", "/")) +} + +func Test_Start(t *testing.T) { + require.Equal(t, "/test/string", str.Start("test/string", "/")) + require.Equal(t, "/test/string", str.Start("/test/string", "/")) + require.Equal(t, "/test/string", str.Start("//test/string", "/")) +}