mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2024-12-29 12:17:48 +00:00
c7d07d9ba5
Copy generator.CamelCase for camel-casing names, with one change: Convert '.' in names to '_'. This removes the need for the CamelCaseSlice function which operates on a []string representing a name split along '.'s. Add protogen.Message. Reformat generated code. Add regenerate.bash, largely copied from regenerate.sh. Change-Id: Iecf0bfc43b552f53e458499a328b933b0c9c5f82 Reviewed-on: https://go-review.googlesource.com/130915 Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
117 lines
3.3 KiB
Go
117 lines
3.3 KiB
Go
package protogen
|
|
|
|
import (
|
|
"go/token"
|
|
"strconv"
|
|
"strings"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// A GoIdent is a Go identifier.
|
|
type GoIdent string
|
|
|
|
// A GoImportPath is the import path of a Go package. e.g., "google.golang.org/genproto/protobuf".
|
|
type GoImportPath string
|
|
|
|
func (p GoImportPath) String() string { return strconv.Quote(string(p)) }
|
|
|
|
// A GoPackageName is the name of a Go package. e.g., "protobuf".
|
|
type GoPackageName string
|
|
|
|
// cleanPacakgeName converts a string to a valid Go package name.
|
|
func cleanPackageName(name string) GoPackageName {
|
|
name = strings.Map(badToUnderscore, name)
|
|
// Identifier must not be keyword: insert _.
|
|
if token.Lookup(name).IsKeyword() {
|
|
name = "_" + name
|
|
}
|
|
// Identifier must not begin with digit: insert _.
|
|
if r, _ := utf8.DecodeRuneInString(name); unicode.IsDigit(r) {
|
|
name = "_" + name
|
|
}
|
|
return GoPackageName(name)
|
|
}
|
|
|
|
// badToUnderscore is the mapping function used to generate Go names from package names,
|
|
// which can be dotted in the input .proto file. It replaces non-identifier characters such as
|
|
// dot or dash with underscore.
|
|
func badToUnderscore(r rune) rune {
|
|
if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
|
|
return r
|
|
}
|
|
return '_'
|
|
}
|
|
|
|
// baseName returns the last path element of the name, with the last dotted suffix removed.
|
|
func baseName(name string) string {
|
|
// First, find the last element
|
|
if i := strings.LastIndex(name, "/"); i >= 0 {
|
|
name = name[i+1:]
|
|
}
|
|
// Now drop the suffix
|
|
if i := strings.LastIndex(name, "."); i >= 0 {
|
|
name = name[:i]
|
|
}
|
|
return name
|
|
}
|
|
|
|
// camelCase converts a name to CamelCase.
|
|
//
|
|
// If there is an interior underscore followed by a lower case letter,
|
|
// drop the underscore and convert the letter to upper case.
|
|
// There is a remote possibility of this rewrite causing a name collision,
|
|
// but it's so remote we're prepared to pretend it's nonexistent - since the
|
|
// C++ generator lowercases names, it's extremely unlikely to have two fields
|
|
// with different capitalizations.
|
|
func camelCase(s string) GoIdent {
|
|
if s == "" {
|
|
return ""
|
|
}
|
|
var t []byte
|
|
i := 0
|
|
// Invariant: if the next letter is lower case, it must be converted
|
|
// to upper case.
|
|
// That is, we process a word at a time, where words are marked by _ or
|
|
// upper case letter. Digits are treated as words.
|
|
for ; i < len(s); i++ {
|
|
c := s[i]
|
|
switch {
|
|
case c == '.':
|
|
t = append(t, '_') // Convert . to _.
|
|
case c == '_' && (i == 0 || s[i-1] == '.'):
|
|
// Convert initial _ to X so we start with a capital letter.
|
|
// Do the same for _ after .; not strictly necessary, but matches
|
|
// historic behavior.
|
|
t = append(t, 'X')
|
|
case c == '_' && i+1 < len(s) && isASCIILower(s[i+1]):
|
|
// Skip the underscore in s.
|
|
case isASCIIDigit(c):
|
|
t = append(t, c)
|
|
default:
|
|
// Assume we have a letter now - if not, it's a bogus identifier.
|
|
// The next word is a sequence of characters that must start upper case.
|
|
if isASCIILower(c) {
|
|
c ^= ' ' // Make it a capital letter.
|
|
}
|
|
t = append(t, c) // Guaranteed not lower case.
|
|
// Accept lower case sequence that follows.
|
|
for i+1 < len(s) && isASCIILower(s[i+1]) {
|
|
i++
|
|
t = append(t, s[i])
|
|
}
|
|
}
|
|
}
|
|
return GoIdent(t)
|
|
}
|
|
|
|
// Is c an ASCII lower-case letter?
|
|
func isASCIILower(c byte) bool {
|
|
return 'a' <= c && c <= 'z'
|
|
}
|
|
|
|
// Is c an ASCII digit?
|
|
func isASCIIDigit(c byte) bool {
|
|
return '0' <= c && c <= '9'
|
|
}
|