github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/util/string.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 package util 7 8 import "github.com/yuin/goldmark/util" 9 10 func isSnakeCaseUpper(c byte) bool { 11 return 'A' <= c && c <= 'Z' 12 } 13 14 func isSnakeCaseLowerOrNumber(c byte) bool { 15 return 'a' <= c && c <= 'z' || '0' <= c && c <= '9' 16 } 17 18 // ToSnakeCase convert the input string to snake_case format. 19 // 20 // Some samples. 21 // 22 // "FirstName" => "first_name" 23 // "HTTPServer" => "http_server" 24 // "NoHTTPS" => "no_https" 25 // "GO_PATH" => "go_path" 26 // "GO PATH" => "go_path" // space is converted to underscore. 27 // "GO-PATH" => "go_path" // hyphen is converted to underscore. 28 func ToSnakeCase(input string) string { 29 if len(input) == 0 { 30 return "" 31 } 32 33 var res []byte 34 if len(input) == 1 { 35 c := input[0] 36 if isSnakeCaseUpper(c) { 37 res = []byte{c + 'a' - 'A'} 38 } else if isSnakeCaseLowerOrNumber(c) { 39 res = []byte{c} 40 } else { 41 res = []byte{'_'} 42 } 43 } else { 44 res = make([]byte, 0, len(input)*4/3) 45 pos := 0 46 needSep := false 47 for pos < len(input) { 48 c := input[pos] 49 if c >= 0x80 { 50 res = append(res, c) 51 pos++ 52 continue 53 } 54 isUpper := isSnakeCaseUpper(c) 55 if isUpper || isSnakeCaseLowerOrNumber(c) { 56 end := pos + 1 57 if isUpper { 58 // skip the following upper letters 59 for end < len(input) && isSnakeCaseUpper(input[end]) { 60 end++ 61 } 62 if end-pos > 1 && end < len(input) && isSnakeCaseLowerOrNumber(input[end]) { 63 end-- 64 } 65 } 66 // skip the following lower or number letters 67 for end < len(input) && (isSnakeCaseLowerOrNumber(input[end]) || input[end] >= 0x80) { 68 end++ 69 } 70 if needSep { 71 res = append(res, '_') 72 } 73 res = append(res, input[pos:end]...) 74 pos = end 75 needSep = true 76 } else { 77 res = append(res, '_') 78 pos++ 79 needSep = false 80 } 81 } 82 for i := 0; i < len(res); i++ { 83 if isSnakeCaseUpper(res[i]) { 84 res[i] += 'a' - 'A' 85 } 86 } 87 } 88 return util.BytesToReadOnlyString(res) 89 }