cosmossdk.io/client/v2@v2.0.0-beta.1/internal/strcase/kebab.go (about) 1 /* 2 * The MIT License (MIT) 3 * 4 * Copyright (c) 2015 Ian Coleman 5 * Copyright (c) 2018 Ma_124, <github.com/Ma124> 6 * Copyright (c) 2022 Cosmos SDK Developers, <github.com/cosmos/cosmos-sdk> 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a copy 9 * of this software and associated documentation files (the "Software"), to deal 10 * in the Software without restriction, including without limitation the rights 11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 * copies of the Software, and to permit persons to whom the Software is 13 * furnished to do so, Subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be included in all 16 * copies or Substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 * SOFTWARE. 25 */ 26 27 package strcase 28 29 import ( 30 "strings" 31 ) 32 33 // ToKebab converts a string to kebab-case 34 func ToKebab(s string) string { 35 return ToDelimited(s, '-', false) 36 } 37 38 // ToScreamingKebab converts a string to SCREAMING-KEBAB-CASE 39 func ToScreamingKebab(s string) string { 40 return ToDelimited(s, '-', true) 41 } 42 43 // ToSnake converts a string to snake_case 44 func ToSnake(s string) string { 45 return ToDelimited(s, '_', false) 46 } 47 48 // ToScreamingSnake converts a string to SCREAMING_SNAKE_CASE 49 func ToScreamingSnake(s string) string { 50 return ToDelimited(s, '_', true) 51 } 52 53 // ToDelimited converts a string to delimited.snake.case 54 // (in this case `delimiter = '.'`) 55 func ToDelimited(s string, delimiter uint8, screaming bool) string { 56 s = strings.TrimSpace(s) 57 n := strings.Builder{} 58 n.Grow(len(s) + 2) // nominal 2 bytes of extra space for inserted delimiters 59 for i, v := range []byte(s) { 60 vIsCap := v >= 'A' && v <= 'Z' 61 vIsLow := v >= 'a' && v <= 'z' 62 if vIsLow && screaming { 63 v += 'A' 64 v -= 'a' 65 } else if vIsCap && !screaming { 66 v += 'a' 67 v -= 'A' 68 } 69 70 // treat acronyms as words, eg for JSONData -> JSON is a whole word 71 if i+1 < len(s) { 72 next := s[i+1] 73 vIsNum := v >= '0' && v <= '9' 74 nextIsCap := next >= 'A' && next <= 'Z' 75 nextIsLow := next >= 'a' && next <= 'z' 76 nextIsNum := next >= '0' && next <= '9' 77 // add underscore if next letter case type is changed 78 if (vIsCap && (nextIsLow || nextIsNum)) || (vIsLow && (nextIsCap || nextIsNum)) || (vIsNum && (nextIsCap || nextIsLow)) { 79 if vIsCap && nextIsLow { 80 if prevIsCap := i > 0 && s[i-1] >= 'A' && s[i-1] <= 'Z'; prevIsCap { 81 n.WriteByte(delimiter) 82 } 83 } 84 n.WriteByte(v) 85 if (vIsLow && nextIsCap) || (vIsNum && nextIsCap) || (vIsNum && nextIsLow) { 86 n.WriteByte(delimiter) 87 } 88 continue 89 } 90 } 91 92 if v == ' ' || v == '_' || v == '-' || v == '.' { 93 // replace space/underscore/hyphen/dot with delimiter 94 n.WriteByte(delimiter) 95 } else { 96 n.WriteByte(v) 97 } 98 } 99 100 return n.String() 101 }