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  }