...

Source file src/github.com/grpc-ecosystem/grpc-gateway/v2/internal/casing/camel.go

Documentation: github.com/grpc-ecosystem/grpc-gateway/v2/internal/casing

     1  package casing
     2  
     3  import (
     4  	"path/filepath"
     5  	"strings"
     6  )
     7  
     8  // Camel returns the CamelCased name.
     9  //
    10  // This was moved from the now deprecated github.com/golang/protobuf/protoc-gen-go/generator package
    11  //
    12  // If there is an interior underscore followed by a lower case letter,
    13  // drop the underscore and convert the letter to upper case.
    14  // There is a remote possibility of this rewrite causing a name collision,
    15  // but it's so remote we're prepared to pretend it's nonexistent - since the
    16  // C++ generator lowercases names, it's extremely unlikely to have two fields
    17  // with different capitalizations.
    18  // In short, _my_field_name_2 becomes XMyFieldName_2.
    19  func Camel(s string) string {
    20  	if s == "" {
    21  		return ""
    22  	}
    23  	t := make([]byte, 0, 32)
    24  	i := 0
    25  	if s[0] == '_' {
    26  		// Need a capital letter; drop the '_'.
    27  		t = append(t, 'X')
    28  		i++
    29  	}
    30  	// Invariant: if the next letter is lower case, it must be converted
    31  	// to upper case.
    32  	// That is, we process a word at a time, where words are marked by _ or
    33  	// upper case letter. Digits are treated as words.
    34  	for ; i < len(s); i++ {
    35  		c := s[i]
    36  		if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
    37  			continue // Skip the underscore in s.
    38  		}
    39  		if isASCIIDigit(c) {
    40  			t = append(t, c)
    41  			continue
    42  		}
    43  		// Assume we have a letter now - if not, it's a bogus identifier.
    44  		// The next word is a sequence of characters that must start upper case.
    45  		if isASCIILower(c) {
    46  			c ^= ' ' // Make it a capital letter.
    47  		}
    48  		t = append(t, c) // Guaranteed not lower case.
    49  		// Accept lower case sequence that follows.
    50  		for i+1 < len(s) && isASCIILower(s[i+1]) {
    51  			i++
    52  			t = append(t, s[i])
    53  		}
    54  	}
    55  	return string(t)
    56  }
    57  
    58  // CamelIdentifier returns the CamelCased identifier without affecting the package name/path if any.
    59  func CamelIdentifier(s string) string {
    60  	const dot = "."
    61  	if !strings.Contains(s, dot) {
    62  		return Camel(s)
    63  	}
    64  	identifier := filepath.Ext(s)
    65  	path := strings.TrimSuffix(s, identifier)
    66  	identifier = strings.TrimPrefix(identifier, dot)
    67  	return path + dot + Camel(identifier)
    68  }
    69  
    70  // JSONCamelCase converts a snake_case identifier to a camelCase identifier,
    71  // according to the protobuf JSON specification.
    72  func JSONCamelCase(s string) string {
    73  	var b []byte
    74  	var wasUnderscore bool
    75  	for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
    76  		c := s[i]
    77  		if c != '_' {
    78  			if wasUnderscore && isASCIILower(c) {
    79  				c -= 'a' - 'A' // convert to uppercase
    80  			}
    81  			b = append(b, c)
    82  		}
    83  		wasUnderscore = c == '_'
    84  	}
    85  	return string(b)
    86  }
    87  
    88  // And now lots of helper functions.
    89  
    90  // Is c an ASCII lower-case letter?
    91  func isASCIILower(c byte) bool {
    92  	return 'a' <= c && c <= 'z'
    93  }
    94  
    95  // Is c an ASCII digit?
    96  func isASCIIDigit(c byte) bool {
    97  	return '0' <= c && c <= '9'
    98  }
    99  

View as plain text