Skip to content

Instantly share code, notes, and snippets.

@ydnar
Created June 4, 2020 19:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ydnar/327d33e4c9fa54f08b6d7f0e31880bf7 to your computer and use it in GitHub Desktop.
Save ydnar/327d33e4c9fa54f08b6d7f0e31880bf7 to your computer and use it in GitHub Desktop.
protoc-gen-good
package main
import (
"errors"
"flag"
"fmt"
"os"
"path/filepath"
"strings"
"unicode"
gengo "google.golang.org/protobuf/cmd/protoc-gen-go/internal_gengo"
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/descriptorpb"
)
func main() {
if len(os.Args) == 2 && os.Args[1] == "--version" {
fmt.Fprintf(os.Stderr, "%v %v\n", filepath.Base(os.Args[0]), "1.0")
os.Exit(1)
}
var (
flags flag.FlagSet
plugins = flags.String("plugins", "", "deprecated option")
importPrefix = flags.String("import_prefix", "", "deprecated option")
)
protogen.Options{
ParamFunc: flags.Set,
}.Run(func(gen *protogen.Plugin) error {
if *plugins != "" {
return errors.New("protoc-gen-go: plugins are not supported; use 'protoc --go-grpc_out=...' to generate gRPC")
}
if *importPrefix != "" {
return errors.New("protoc-gen-go: import_prefix is not supported")
}
for _, f := range gen.Files {
if f.Generate {
fixNames(f)
gengo.GenerateFile(gen, f)
}
}
return nil
})
}
func fixNames(f *protogen.File) {
for _, e := range f.Enums {
fmt.Fprintf(os.Stderr, "Enum: %s\n", e.GoIdent.String())
for _, v := range e.Values {
fmt.Fprintf(os.Stderr, "Value: %s\n", v.GoIdent.GoName)
}
}
for _, m := range f.Messages {
fmt.Fprintf(os.Stderr, "Message: %s\n", m.GoIdent.String())
for _, field := range m.Fields {
options := field.Desc.Options().(*descriptorpb.FieldOptions)
fmt.Fprintf(os.Stderr, "Field: %s → %s → %s Ident: %v → %v\n",
field.Desc.Name(), field.GoName, fixCamelCase(field.GoName),
field.GoIdent.GoName, fixCamelCase(field.GoIdent.GoName))
field.GoName = fixCamelCase(field.GoName)
field.GoIdent.GoName = fixCamelCase(field.GoIdent.GoName)
}
}
}
// fixCamelCase returns a different name if it should be different.
// Copied from: https://github.com/golang/lint/blob/master/lint.go
func fixCamelCase(name string) string {
// Fast path for simple case: "" and "_"
if name == "" || name == "_" {
return name
}
runes := []rune(name)
// Ensure first character is uppercase.
runes[0] = unicode.ToUpper(runes[0])
// Split camelCase at any lower->upper transition, and split on underscores.
// Check each word for common initialisms.
w, i := 0, 0 // index of start of word, scan
for i+1 <= len(runes) {
eow := false // whether we hit the end of a word
if i+1 == len(runes) {
eow = true
} else if runes[i+1] == '_' {
// underscore; shift the remainder forward over any run of underscores
eow = true
n := 1
for i+n+1 < len(runes) && runes[i+n+1] == '_' {
n++
}
// Leave at most one underscore if the underscore is between two digits
if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
n--
}
copy(runes[i+1:], runes[i+n+1:])
runes = runes[:len(runes)-n]
} else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
// lower->non-lower
eow = true
}
i++
if !eow {
continue
}
// [w,i) is a word.
word := string(runes[w:i])
if u := strings.ToUpper(word); commonInitialisms[u] {
// Keep consistent case, which is lowercase only at the start.
if w == 0 && unicode.IsLower(runes[w]) {
u = strings.ToLower(u)
}
// All the common initialisms are ASCII,
// so we can replace the bytes exactly.
copy(runes[w:], []rune(u))
} else if w > 0 && strings.ToLower(word) == word {
// already all lowercase, and not the first word, so uppercase the first character.
runes[w] = unicode.ToUpper(runes[w])
}
w = i
}
return string(runes)
}
// commonInitialisms is a set of common initialisms.
// Only add entries that are highly unlikely to be non-initialisms.
// For instance, "ID" is fine (Freudian code is rare), but "AND" is not.
// Copied from: https://github.com/golang/lint/blob/master/lint.go
var commonInitialisms = map[string]bool{
"ACL": true,
"API": true,
"ASCII": true,
"CPU": true,
"CSS": true,
"CORS": true,
"DNS": true,
"EOF": true,
"GUID": true,
"HTML": true,
"HTTP": true,
"HTTPS": true,
"ID": true,
"IMAP": true,
"IP": true,
"JSON": true,
"LHS": true,
"QPS": true,
"RAM": true,
"RFC": true,
"RHS": true,
"RPC": true,
"SLA": true,
"SMTP": true,
"SQL": true,
"SSH": true,
"TCP": true,
"TLS": true,
"TTL": true,
"UDP": true,
"UI": true,
"UID": true,
"UUID": true,
"URI": true,
"URL": true,
"UTF8": true,
"VM": true,
"XML": true,
"XMPP": true,
"XSRF": true,
"XSS": true,
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment