go.uber.org/yarpc@v1.72.1/internal/protoplugin-v2/utils.go (about)

     1  // Copyright (c) 2022 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package protopluginv2
    22  
    23  // goCamelCase camel-cases a protobuf name for use as a Go identifier.
    24  //
    25  // If there is an interior underscore followed by a lower case letter,
    26  // drop the underscore and convert the letter to upper case.
    27  func goCamelCase(s string) string {
    28  	// Invariant: if the next letter is lower case, it must be converted
    29  	// to upper case.
    30  	// That is, we process a word at a time, where words are marked by _ or
    31  	// upper case letter. Digits are treated as words.
    32  	var b []byte
    33  	for i := 0; i < len(s); i++ {
    34  		c := s[i]
    35  		switch {
    36  		case c == '.' && i+1 < len(s) && isASCIILower(s[i+1]):
    37  			// Skip over '.' in ".{{lowercase}}".
    38  		case c == '.':
    39  			b = append(b, '_') // convert '.' to '_'
    40  		case c == '_' && (i == 0 || s[i-1] == '.'):
    41  			// Convert initial '_' to ensure we start with a capital letter.
    42  			// Do the same for '_' after '.' to match historic behavior.
    43  			b = append(b, 'X') // convert '_' to 'X'
    44  		case c == '_' && i+1 < len(s) && isASCIILower(s[i+1]):
    45  			// Skip over '_' in "_{{lowercase}}".
    46  		case isASCIIDigit(c):
    47  			b = append(b, c)
    48  		default:
    49  			// Assume we have a letter now - if not, it's a bogus identifier.
    50  			// The next word is a sequence of characters that must start upper case.
    51  			if isASCIILower(c) {
    52  				c -= 'a' - 'A' // convert lowercase to uppercase
    53  			}
    54  			b = append(b, c)
    55  
    56  			// Accept lower case sequence that follows.
    57  			for ; i+1 < len(s) && isASCIILower(s[i+1]); i++ {
    58  				b = append(b, s[i+1])
    59  			}
    60  		}
    61  	}
    62  	return string(b)
    63  }
    64  
    65  func isASCIILower(c byte) bool {
    66  	return 'a' <= c && c <= 'z'
    67  }
    68  
    69  func isASCIIDigit(c byte) bool {
    70  	return '0' <= c && c <= '9'
    71  }