diff --git a/example/main.go b/example/main.go index 58ec7db..b67c4df 100644 --- a/example/main.go +++ b/example/main.go @@ -2,34 +2,53 @@ package main import ( "fmt" - . "github.com/gobeam/string-manipulation" + . "github.com/gobeam/Stringy" ) - func main() { - //str := New("HelloMyName") - //fmt.Println(str.Between("hello","name").ToUpper()) - //teaseString := New("Hello My name is Roshan.") - //fmt.Println(teaseString.Tease(100,"...")) + strBetween := New("HelloMyName") + fmt.Println(strBetween.Between("hello","name").ToUpper()) - //replaceFirst := New("Hello My name is Roshan and his name is Alis.") - //fmt.Println(replaceFirst.ReplaceFirst("name", "nau")) - // - //replaceLast := New("Hello My name is Roshan and his name is Alis.") - //fmt.Println(replaceLast.ReplaceLast("name", "nau")) + teaseString := New("Hello My name is Roshan. I am full stack developer") + fmt.Println(teaseString.Tease(100,"...")) - //snakeCase := New("hey man how are you") - //fmt.Println(snakeCase.ToSnake().ToLower()) - //camelCase := New("any__Yoko _._-_po122ΩΩΩß##s_si") - //fmt.Println(camelCase.CamelCase()) + replaceFirst := New("Hello My name is Roshan and his name is Alis.") + fmt.Println(replaceFirst.ReplaceFirst("name", "nombre")) - kebabCase := New("any__Yoko _._-_po122ΩΩΩß##s_si") - fmt.Println(kebabCase.KebabCase()) + replaceLast := New("Hello My name is Roshan and his name is Alis.") + fmt.Println(replaceLast.ReplaceLast("name", "nombre")) - //fmt.Println(strcase.ToKebab("any__Yoko _._-_po122ΩΩΩß##s_si")) + snakeCase := New("ThisIsOne___messed up string. Can we Really Snake Case It?") + fmt.Println(snakeCase.SnakeCase("?","").Get()) - //matchFirstCap := regexp.MustCompile("[-._][^a-z0-9]*") - //rslt := matchFirstCap.ReplaceAllString("Any__ _._-_pos_si"," ") - //fmt.Println(rslt) -} \ No newline at end of file + camelCase := New("ThisIsOne___messed up string. Can we Really camel-case It ?##") + fmt.Println(camelCase.CamelCase("?","","#","")) + + delimiterString := New("ThisIsOne___messed up string. Can we Really delimeter-case It?") + fmt.Println(delimiterString.Delimited("?").Get()) + + contains := New("hello mam how are you??") + fmt.Println(contains.ContainsAll("mams","?")) + + lines := New("fòô\r\nbàř\nyolo123") + fmt.Println(lines.Lines()) + + reverse := New("This is only test") + fmt.Println(reverse.Reverse()) + + pad := New("Roshan") + fmt.Println(pad.Pad(0, "0","both")) + fmt.Println(pad.Pad(0, "0","left")) + fmt.Println(pad.Pad(0, "0","right")) + + shuffleString := New("roshan") + fmt.Println(shuffleString.Shuffle()) + + cleanString := New("special@#remove%%%%") + fmt.Println(cleanString.RemoveSpecialCharacter()) + + boolString := New("off") + fmt.Println(boolString.Boolean()) + +} diff --git a/helper.go b/helper.go new file mode 100644 index 0000000..aa9cd89 --- /dev/null +++ b/helper.go @@ -0,0 +1,69 @@ +package string_manipulation + +import ( + "errors" + "regexp" + "strings" + "unicode" +) + +func caseHelper(input string,isCamel bool, rule ...string) []string { + if !isCamel { + re := regexp.MustCompile("([a-z])([A-Z])") + input = re.ReplaceAllString(input, "$1 $2") + } + input = strings.Join(strings.Fields(strings.TrimSpace(input)), " ") + if len(rule) <= 1 { + rule = []string{".", " ", "_", " ", "-", " "} + } + if len(rule) > 1 && len(rule)%2 != 0 { + panic(errors.New("odd number rule provided please provide in even count")) + } + rule = append(rule, ".", " ", "_", " ", "-", " ") + + replacer := strings.NewReplacer(rule ...) + input = replacer.Replace(input) + words := strings.Fields(input) + return words +} + +func contains(slice []string, item string) bool { + set := make(map[string]struct{}, len(slice)) + for _, s := range slice { + set[s] = struct{}{} + } + _, ok := set[item] + return ok +} + +func getInput(i input) (input string) { + if i.Result != "" { + input = i.Result + } else { + input = i.Input + } + return +} + +func replaceStr(input, search, replace, types string) string { + lcInput := strings.ToLower(input) + lcSearch := strings.ToLower(search) + if input == "" || !strings.Contains(lcInput, lcSearch) { + return input + } + var start int + if types == "last" { + start = strings.LastIndex(lcInput, lcSearch) + } else { + start = strings.Index(lcInput, lcSearch) + } + end := start + len(search) + return input[:start] + replace + input[end:] +} + +func ucfirst(val string) string { + for i, v := range val { + return string(unicode.ToUpper(v)) + val[i+1:] + } + return "" +} diff --git a/message.go b/message.go index e62475a..cec980d 100644 --- a/message.go +++ b/message.go @@ -3,4 +3,9 @@ package string_manipulation const ( First = "first" Last = "last" -) \ No newline at end of file + Left = "left" + Right = "right" + Both = "both" +) +var False = []string{"off","no","0","false"} +var True = []string{"on","yes","1","True"} \ No newline at end of file diff --git a/str.go b/str.go deleted file mode 100644 index d7c1a28..0000000 --- a/str.go +++ /dev/null @@ -1,179 +0,0 @@ -package string_manipulation - -import ( - "fmt" - "regexp" - "strings" - "unicode" -) - -type input struct { - Input string - Result string - Error error -} - -func caseHelper(input, key string,)(result string) { - matchSpecial := regexp.MustCompile("[^-._A-Za-z0-9]*") - input = matchSpecial.ReplaceAllString(input,"") - matchWord := regexp.MustCompile("[-._]*[^A-Za-z0-9]") - input = matchWord.ReplaceAllString(input,fmt.Sprintf("%s",key)) - for _, word := range strings.Fields(strings.TrimSpace(input)) { - result += ucfirst(word) - } - return -} - -func (i *input) KebabCase() StringManipulation { - input := getInput(*i) - i.Result = caseHelper(input, "-") - return i -} - -func ucfirst(val string) string { - for i, v := range val { - return string(unicode.ToUpper(v)) + val[i+1:] - } - return "" -} - -func (i *input) UcFirst() string { - input := getInput(*i) - return ucfirst(input) -} - -func (i *input) LcFirst() string { - input := getInput(*i) - for i, v := range input { - return string(unicode.ToUpper(v)) + input[i+1:] - } - return input -} - -func (i *input) SnakeCase() StringManipulation { - input := getInput(*i) - matchFirstCap := regexp.MustCompile("(.)([A-Z][a-z]+)") - matchAllCap := regexp.MustCompile("([a-z0-9])([A-Z])") - snake := matchFirstCap.ReplaceAllString(input, "${1}_${2}") - snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") - i.Result = strings.Join(strings.Fields(strings.TrimSpace(snake)), "_") - return i -} - -func (i *input) CamelCase() string { - input := getInput(*i) - matchSpecial := regexp.MustCompile("[^-._A-Za-z0-9]*") - input = matchSpecial.ReplaceAllString(input,"") - matchWord := regexp.MustCompile("[-._]*[^A-Za-z0-9]") - input = matchWord.ReplaceAllString(input," ") - var result string - for _, word := range strings.Fields(strings.TrimSpace(input)) { - result += ucfirst(word) - } - return result - -} - -func (i *input) Get() string { - return getInput(*i) -} - -func (i *input) Slugify() string { - input := getInput(*i) - return strings.ReplaceAll(input, " ", "-") -} - -func replaceStr(input, search, replace, types string) string { - lcInput := strings.ToLower(input) - lcSearch := strings.ToLower(search) - if input == "" || !strings.Contains(lcInput, lcSearch) { - return input - } - var start int - if types == "last" { - start = strings.LastIndex(lcInput, lcSearch) - } else { - start = strings.Index(lcInput, lcSearch) - } - end := start + len(search) - return input[:start] + replace + input[end:] -} - -func (i *input) ReplaceFirst(search, replace string) string { - input := getInput(*i) - return replaceStr(input, search, replace, First) -} - -func (i *input) ReplaceLast(search, replace string) string { - input := getInput(*i) - return replaceStr(input, search, replace, Last) -} - -func (i *input) Tease(length int, indicator string) string { - input := getInput(*i) - if input == "" || len(input) < length { - return input - } - return input[:length] + indicator -} - -type StringManipulation interface { - Between(start, end string) StringManipulation - Get() string - ToUpper() string - UcFirst() string - LcFirst() string - CamelCase() string - SnakeCase() StringManipulation - KebabCase() StringManipulation - Slugify() string - ReplaceFirst(search, replace string) string - ReplaceLast(search, replace string) string - ToLower() string - Tease(length int, indicator string) string -} - -func New(val string) StringManipulation { - return &input{Input: val} -} - -func getInput(i input) (input string) { - if i.Result != "" { - input = i.Result - } else { - input = i.Input - } - return -} - -func (i *input) ToUpper() string { - input := getInput(*i) - return strings.ToUpper(input) -} - -func (i *input) ToLower() (result string) { - input := getInput(*i) - return strings.ToLower(input) -} - -func (i *input) Between(start, end string) StringManipulation { - if start == "" && end == "" || i.Input == "" { - return i - } - - input := strings.ToLower(i.Input) - lcStart := strings.ToLower(start) - lcEnd := strings.ToLower(end) - var startIndex, endIndex int - - if len(start) > 0 && strings.Contains(input, lcStart) { - startIndex = len(start) - } - if len(end) > 0 && strings.Contains(input, lcEnd) { - endIndex = strings.Index(input, lcEnd) - } else if len(input) > 0 { - endIndex = len(input) - } - i.Result = i.Input[startIndex:endIndex] - return i -} diff --git a/stringy.go b/stringy.go new file mode 100644 index 0000000..8a39da4 --- /dev/null +++ b/stringy.go @@ -0,0 +1,253 @@ +package string_manipulation + +import ( + "errors" + "math" + "math/rand" + "regexp" + "strings" + "time" + "unicode" +) + +// input is struct that holds input form user and result +type input struct { + Input string + Result string +} + +// StringManipulation is an interface that holds all abstract methods to manipulate strings +type StringManipulation interface { + Between(start, end string) StringManipulation + Boolean() bool + CamelCase(rule ...string) string + ContainsAll(check ...string) bool + Delimited(delimiter string, rule ...string) StringManipulation + Get() string + KebabCase(rule ...string) StringManipulation + LcFirst() string + Lines() []string + Pad(length int, with, padType string) string + RemoveSpecialCharacter() string + ReplaceFirst(search, replace string) string + ReplaceLast(search, replace string) string + Reverse() string + Shuffle() string + Surround(with string) string + SnakeCase(rule ...string) StringManipulation + Tease(length int, indicator string) string + ToLower() string + ToUpper() string + UcFirst() string +} + +// New func returns pointer to input struct +func New(val string) StringManipulation { + return &input{Input: val} +} + +// Between takes two param start and end which are string +// return value after omitting start and end part of input +func (i *input) Between(start, end string) StringManipulation { + if start == "" && end == "" || i.Input == "" { + return i + } + + input := strings.ToLower(i.Input) + lcStart := strings.ToLower(start) + lcEnd := strings.ToLower(end) + var startIndex, endIndex int + + if len(start) > 0 && strings.Contains(input, lcStart) { + startIndex = len(start) + } + if len(end) > 0 && strings.Contains(input, lcEnd) { + endIndex = strings.Index(input, lcEnd) + } else if len(input) > 0 { + endIndex = len(input) + } + i.Result = i.Input[startIndex:endIndex] + return i +} + +// Boolean dunc return boolean value of string value like on, off, 0, 1, yes, no +// returns boolean value of string input +func (i *input) Boolean() bool { + input := getInput(*i) + inputLower := strings.ToLower(input) + off := contains(False, inputLower) + if off { + return false + } + on := contains(True, inputLower) + if on { + return true + } + panic(errors.New("invalid string value to test boolean value")) +} + +// CamelCase function returns camel case value of string +// Example input: hello user +// Result : HelloUser +func (i *input) CamelCase(rule ...string) string { + input := getInput(*i) + // removing excess space + wordArray := caseHelper(input, true, rule ...) + for i, word := range wordArray { + wordArray[i] = ucfirst(word) + } + return strings.Join(wordArray, "") +} + +// ContainsAll function takes multiple string param and +// checks if they are present in input +func (i *input) ContainsAll(check ...string) bool { + input := getInput(*i) + for _, item := range check { + if !strings.Contains(input, item) { + return false + } + } + return true +} + +// Delimited function joins the string by passed delimeter +func (i *input) Delimited(delimiter string, rule ...string) StringManipulation { + input := getInput(*i) + if strings.TrimSpace(delimiter) == "" { + delimiter = "." + } + wordArray := caseHelper(input, false, rule ...) + i.Result = strings.Join(wordArray, delimiter) + return i +} + +func (i *input) Get() string { + return getInput(*i) +} + +func (i *input) KebabCase(rule ...string) StringManipulation { + input := getInput(*i) + wordArray := caseHelper(input, false, rule ...) + i.Result = strings.Join(wordArray, "-") + return i +} + +func (i *input) LcFirst() string { + input := getInput(*i) + for i, v := range input { + return string(unicode.ToUpper(v)) + input[i+1:] + } + return input +} + +func (i *input) Lines() []string { + input := getInput(*i) + matchWord := regexp.MustCompile(`[\s]*[\W]\pN`) + result := matchWord.ReplaceAllString(input, " ") + return strings.Fields(strings.TrimSpace(result)) +} + +func (i *input) Pad(length int, with, padType string) string { + input := getInput(*i) + inputLength := len(input) + padLength := len(with) + if inputLength >= length { + return input + } + switch padType { + case Right: + var count = 1 + ((length - padLength) / padLength) + var result = input + strings.Repeat(with, count) + return result[:length] + case Left: + var count = 1 + ((length - padLength) / padLength) + var result = strings.Repeat(with, count) + input + return result[(len(result) - length):] + case Both: + length := (float64(length - inputLength)) / float64(2) + repeat := math.Ceil(length / float64(padLength)) + return strings.Repeat(with, int(repeat))[:int(math.Floor(float64(length)))] + input + strings.Repeat(with, int(repeat))[:int(math.Ceil(float64(length)))] + default: + return input + } +} + +func (i *input) RemoveSpecialCharacter() string { + input := getInput(*i) + var result strings.Builder + for i := 0; i < len(input); i++ { + b := input[i] + if ('a' <= b && b <= 'z') || + ('A' <= b && b <= 'Z') || + ('0' <= b && b <= '9') || + b == ' ' { + result.WriteByte(b) + } + } + return result.String() +} + +func (i *input) ReplaceFirst(search, replace string) string { + input := getInput(*i) + return replaceStr(input, search, replace, First) +} + +func (i *input) ReplaceLast(search, replace string) string { + input := getInput(*i) + return replaceStr(input, search, replace, Last) +} +func (i *input) Reverse() string { + input := getInput(*i) + r := []rune(input) + for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 { + r[i], r[j] = r[j], r[i] + } + return string(r) +} + +func (i *input) Shuffle() string { + input := getInput(*i) + rand.Seed(time.Now().Unix()) + + inRune := []rune(input) + rand.Shuffle(len(inRune), func(i, j int) { + inRune[i], inRune[j] = inRune[j], inRune[i] + }) + return string(inRune) +} + +func (i *input) SnakeCase(rule ...string) StringManipulation { + input := getInput(*i) + wordArray := caseHelper(input, false, rule ...) + i.Result = strings.Join(wordArray, "_") + return i +} + +func (i *input) Surround(with string) string { + input := getInput(*i) + return with + input + with +} + +func (i *input) Tease(length int, indicator string) string { + input := getInput(*i) + if input == "" || len(input) < length { + return input + } + return input[:length] + indicator +} + +func (i *input) ToLower() (result string) { + input := getInput(*i) + return strings.ToLower(input) +} + +func (i *input) ToUpper() string { + input := getInput(*i) + return strings.ToUpper(input) +} + +func (i *input) UcFirst() string { + input := getInput(*i) + return ucfirst(input) +}