istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/util/strcase/camelcase.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package strcase 16 17 import ( 18 "bytes" 19 "strings" 20 ) 21 22 // CamelCase converts the string into camel case string 23 func CamelCase(s string) string { 24 if s == "" { 25 return "" 26 } 27 t := make([]byte, 0, 32) 28 i := 0 29 if isWordSeparator(s[0]) { 30 // Need a capital letter; drop the '_'. 31 t = append(t, 'X') 32 i++ 33 } 34 // Invariant: if the next letter is lower case, it must be converted 35 // to upper case. 36 // That is, we process a word at a time, where words are marked by _, - or 37 // upper case letter. Digits are treated as words. 38 for ; i < len(s); i++ { 39 c := s[i] 40 if isWordSeparator(c) { 41 // Skip the separate and capitalize the next letter. 42 continue 43 } 44 if isASCIIDigit(c) { 45 t = append(t, c) 46 continue 47 } 48 // Assume we have a letter now - if not, it's a bogus identifier. 49 // The next word is a sequence of characters that must start upper case. 50 if isASCIILower(c) { 51 c ^= ' ' // Make it a capital letter. 52 } 53 t = append(t, c) // Guaranteed not lower case. 54 // Accept lower case sequence that follows. 55 for i+1 < len(s) && isASCIILower(s[i+1]) { 56 i++ 57 t = append(t, s[i]) 58 } 59 } 60 return string(t) 61 } 62 63 // CamelCaseWithSeparator splits the given string by the separator, converts the parts to CamelCase and then re-joins them. 64 func CamelCaseWithSeparator(n string, sep string) string { 65 p := strings.Split(n, sep) 66 for i := 0; i < len(p); i++ { 67 p[i] = CamelCase(p[i]) 68 } 69 return strings.Join(p, "") 70 } 71 72 // CamelCaseToKebabCase converts "MyName" to "my-name" 73 func CamelCaseToKebabCase(s string) string { 74 switch s { 75 case "HTTPAPISpec": 76 return "http-api-spec" 77 case "HTTPRoute": 78 return "http-route" 79 case "HTTPAPISpecBinding": 80 return "http-api-spec-binding" 81 default: 82 var out bytes.Buffer 83 for i := range s { 84 if 'A' <= s[i] && s[i] <= 'Z' { 85 if i > 0 { 86 out.WriteByte('-') 87 } 88 out.WriteByte(s[i] - 'A' + 'a') 89 } else { 90 out.WriteByte(s[i]) 91 } 92 } 93 return out.String() 94 } 95 } 96 97 func isWordSeparator(c byte) bool { 98 return c == '_' || c == '-' 99 } 100 101 // Is c an ASCII lower-case letter? 102 func isASCIILower(c byte) bool { 103 return 'a' <= c && c <= 'z' 104 } 105 106 // Is c an ASCII digit? 107 func isASCIIDigit(c byte) bool { 108 return '0' <= c && c <= '9' 109 }