code.gitea.io/gitea@v1.19.3/modules/util/string.go (about)

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