aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/ettle/strcase/convert.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/ettle/strcase/convert.go')
-rw-r--r--vendor/github.com/ettle/strcase/convert.go297
1 files changed, 297 insertions, 0 deletions
diff --git a/vendor/github.com/ettle/strcase/convert.go b/vendor/github.com/ettle/strcase/convert.go
new file mode 100644
index 000000000..70fedb144
--- /dev/null
+++ b/vendor/github.com/ettle/strcase/convert.go
@@ -0,0 +1,297 @@
+package strcase
+
+import "strings"
+
+// WordCase is an enumeration of the ways to format a word.
+type WordCase int
+
+const (
+ // Original - Preserve the original input strcase
+ Original WordCase = iota
+ // LowerCase - All letters lower cased (example)
+ LowerCase
+ // UpperCase - All letters upper cased (EXAMPLE)
+ UpperCase
+ // TitleCase - Only first letter upper cased (Example)
+ TitleCase
+ // CamelCase - TitleCase except lower case first word (exampleText)
+ // Notably, even if the first word is an initialism, it will be lower
+ // cased. This is important for code generators where capital letters
+ // mean exported functions. i.e. jsonString(), not JSONString()
+ CamelCase
+)
+
+// We have 3 convert functions for performance reasons
+// The general convert could handle everything, but is not optimized
+//
+// The other two functions are optimized for the general use cases - that is the non-custom caser functions
+// Case 1: Any Case and supports Go Initialisms
+// Case 2: UpperCase words, which don't need to support initialisms since everything is in upper case
+
+// convertWithoutInitialims only works for to UpperCase and LowerCase
+//nolint:gocyclo
+func convertWithoutInitialisms(input string, delimiter rune, wordCase WordCase) string {
+ input = strings.TrimSpace(input)
+ runes := []rune(input)
+ if len(runes) == 0 {
+ return ""
+ }
+
+ var b strings.Builder
+ b.Grow(len(input) * 2) // In case we need to write delimiters where they weren't before
+
+ var prev, curr rune
+ next := runes[0] // 0 length will have already returned so safe to index
+ inWord := false
+ firstWord := true
+ for i := 0; i < len(runes); i++ {
+ prev = curr
+ curr = next
+ if i+1 == len(runes) {
+ next = 0
+ } else {
+ next = runes[i+1]
+ }
+
+ switch defaultSplitFn(prev, curr, next) {
+ case SkipSplit:
+ if inWord && delimiter != 0 {
+ b.WriteRune(delimiter)
+ }
+ inWord = false
+ continue
+ case Split:
+ if inWord && delimiter != 0 {
+ b.WriteRune(delimiter)
+ }
+ inWord = false
+ }
+ switch wordCase {
+ case UpperCase:
+ b.WriteRune(toUpper(curr))
+ case LowerCase:
+ b.WriteRune(toLower(curr))
+ case TitleCase:
+ if inWord {
+ b.WriteRune(toLower(curr))
+ } else {
+ b.WriteRune(toUpper(curr))
+ }
+ case CamelCase:
+ if inWord {
+ b.WriteRune(toLower(curr))
+ } else if firstWord {
+ b.WriteRune(toLower(curr))
+ firstWord = false
+ } else {
+ b.WriteRune(toUpper(curr))
+ }
+ default:
+ // Must be original case
+ b.WriteRune(curr)
+ }
+ inWord = inWord || true
+ }
+ return b.String()
+}
+
+// convertWithGoInitialisms changes a input string to a certain case with a
+// delimiter, respecting go initialisms but not skip runes
+//nolint:gocyclo
+func convertWithGoInitialisms(input string, delimiter rune, wordCase WordCase) string {
+ input = strings.TrimSpace(input)
+ runes := []rune(input)
+ if len(runes) == 0 {
+ return ""
+ }
+
+ var b strings.Builder
+ b.Grow(len(input) * 2) // In case we need to write delimiters where they weren't before
+
+ firstWord := true
+
+ addWord := func(start, end int) {
+ if start == end {
+ return
+ }
+
+ if !firstWord && delimiter != 0 {
+ b.WriteRune(delimiter)
+ }
+
+ // Don't bother with initialisms if the word is longer than 5
+ // A quick proxy to avoid the extra memory allocations
+ if end-start <= 5 {
+ key := strings.ToUpper(string(runes[start:end]))
+ if golintInitialisms[key] {
+ if !firstWord || wordCase != CamelCase {
+ b.WriteString(key)
+ firstWord = false
+ return
+ }
+ }
+ }
+
+ for i := start; i < end; i++ {
+ r := runes[i]
+ switch wordCase {
+ case UpperCase:
+ panic("use convertWithoutInitialisms instead")
+ case LowerCase:
+ b.WriteRune(toLower(r))
+ case TitleCase:
+ if i == start {
+ b.WriteRune(toUpper(r))
+ } else {
+ b.WriteRune(toLower(r))
+ }
+ case CamelCase:
+ if !firstWord && i == start {
+ b.WriteRune(toUpper(r))
+ } else {
+ b.WriteRune(toLower(r))
+ }
+ default:
+ b.WriteRune(r)
+ }
+ }
+ firstWord = false
+ }
+
+ var prev, curr rune
+ next := runes[0] // 0 length will have already returned so safe to index
+ wordStart := 0
+ for i := 0; i < len(runes); i++ {
+ prev = curr
+ curr = next
+ if i+1 == len(runes) {
+ next = 0
+ } else {
+ next = runes[i+1]
+ }
+
+ switch defaultSplitFn(prev, curr, next) {
+ case Split:
+ addWord(wordStart, i)
+ wordStart = i
+ case SkipSplit:
+ addWord(wordStart, i)
+ wordStart = i + 1
+ }
+ }
+
+ if wordStart != len(runes) {
+ addWord(wordStart, len(runes))
+ }
+ return b.String()
+}
+
+// convert changes a input string to a certain case with a delimiter,
+// respecting arbitrary initialisms and skip characters
+//nolint:gocyclo
+func convert(input string, fn SplitFn, delimiter rune, wordCase WordCase,
+ initialisms map[string]bool) string {
+ input = strings.TrimSpace(input)
+ runes := []rune(input)
+ if len(runes) == 0 {
+ return ""
+ }
+
+ var b strings.Builder
+ b.Grow(len(input) * 2) // In case we need to write delimiters where they weren't before
+
+ firstWord := true
+ var skipIndexes []int
+
+ addWord := func(start, end int) {
+ // If you have nothing good to say, say nothing at all
+ if start == end || len(skipIndexes) == end-start {
+ skipIndexes = nil
+ return
+ }
+
+ // If you have something to say, start with a delimiter
+ if !firstWord && delimiter != 0 {
+ b.WriteRune(delimiter)
+ }
+
+ // Check if you're an initialism
+ // Note - we don't check skip characters here since initialisms
+ // will probably never have junk characters in between
+ // I'm open to it if there is a use case
+ if initialisms != nil {
+ var word strings.Builder
+ for i := start; i < end; i++ {
+ word.WriteRune(toUpper(runes[i]))
+ }
+ key := word.String()
+ if initialisms[key] {
+ if !firstWord || wordCase != CamelCase {
+ b.WriteString(key)
+ firstWord = false
+ return
+ }
+ }
+ }
+
+ skipIdx := 0
+ for i := start; i < end; i++ {
+ if len(skipIndexes) > 0 && skipIdx < len(skipIndexes) && i == skipIndexes[skipIdx] {
+ skipIdx++
+ continue
+ }
+ r := runes[i]
+ switch wordCase {
+ case UpperCase:
+ b.WriteRune(toUpper(r))
+ case LowerCase:
+ b.WriteRune(toLower(r))
+ case TitleCase:
+ if i == start {
+ b.WriteRune(toUpper(r))
+ } else {
+ b.WriteRune(toLower(r))
+ }
+ case CamelCase:
+ if !firstWord && i == start {
+ b.WriteRune(toUpper(r))
+ } else {
+ b.WriteRune(toLower(r))
+ }
+ default:
+ b.WriteRune(r)
+ }
+ }
+ firstWord = false
+ skipIndexes = nil
+ }
+
+ var prev, curr rune
+ next := runes[0] // 0 length will have already returned so safe to index
+ wordStart := 0
+ for i := 0; i < len(runes); i++ {
+ prev = curr
+ curr = next
+ if i+1 == len(runes) {
+ next = 0
+ } else {
+ next = runes[i+1]
+ }
+
+ switch fn(prev, curr, next) {
+ case Skip:
+ skipIndexes = append(skipIndexes, i)
+ case Split:
+ addWord(wordStart, i)
+ wordStart = i
+ case SkipSplit:
+ addWord(wordStart, i)
+ wordStart = i + 1
+ }
+ }
+
+ if wordStart != len(runes) {
+ addWord(wordStart, len(runes))
+ }
+ return b.String()
+}