Skip to content

Commit

Permalink
add embedded message field support
Browse files Browse the repository at this point in the history
  • Loading branch information
Adphi committed Jun 28, 2021
1 parent b171a23 commit 8e813ed
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 90 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@
*.out
*.so
*.test
debug
.idea
4 changes: 4 additions & 0 deletions patch/go.proto
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ message Options {
// For an enum value, this renames the generated Go const.
optional string name = 1;

// The embedded option changes the field as embedded in the generated Go struct.
// Only message types are allowed to be embedded, oneof fields are not embeddable.
optional bool embedded = 2;

// The getter option renames the generated getter method (default: Get<Field>)
// so a custom getter can be implemented in its place.
optional string getter = 10; // TODO: implement this
Expand Down
112 changes: 62 additions & 50 deletions patch/gopb/go.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 39 additions & 14 deletions patch/patcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ type Patcher struct {
objectRenames map[types.Object]string
tags map[protogen.GoIdent]string
fieldTags map[types.Object]string
embedded map[protogen.GoIdent]string
fieldEmbeds map[types.Object]string
}

// NewPatcher returns an initialized Patcher for gen.
Expand All @@ -63,6 +65,8 @@ func NewPatcher(gen *protogen.Plugin) (*Patcher, error) {
objectRenames: make(map[types.Object]string),
tags: make(map[protogen.GoIdent]string),
fieldTags: make(map[types.Object]string),
embedded: make(map[protogen.GoIdent]string),
fieldEmbeds: make(map[types.Object]string),
}
return p, p.scan()
}
Expand Down Expand Up @@ -243,7 +247,7 @@ func (p *Patcher) scanOneof(o *protogen.Oneof) {
newName = lint.Name(newName, lints.InitialismsMap())
}
if newName != "" {
p.RenameField(ident.WithChild(m.GoIdent, o.GoName), newName) // Oneof
p.RenameField(ident.WithChild(m.GoIdent, o.GoName), newName, false) // Oneof
p.RenameMethod(ident.WithChild(m.GoIdent, "Get"+o.GoName), "Get"+newName) // Getter
ifName := ident.WithPrefix(o.GoIdent, "is")
newIfName := "is" + p.nameFor(m.GoIdent) + "_" + newName
Expand Down Expand Up @@ -276,14 +280,24 @@ func (p *Patcher) scanField(f *protogen.Field) {
}
newName = lint.Name(newName, lints.InitialismsMap())
}
// check embed for message types
embedded := f.Message != nil && f.Oneof == nil && opts.GetEmbedded()
if embedded {
// use the embedded field message type's go name or rename option if defined
if mOpts := messageOptions(f.Message); mOpts.GetName() != "" {
newName = mOpts.GetName()
} else {
newName = f.Message.GoIdent.GoName
}
}
if newName != "" {
if o != nil {
p.RenameType(f.GoIdent, p.nameFor(m.GoIdent)+"_"+newName) // Oneof wrapper struct
p.RenameField(ident.WithChild(f.GoIdent, f.GoName), newName) // Oneof wrapper field
p.RenameType(f.GoIdent, p.nameFor(m.GoIdent)+"_"+newName) // Oneof wrapper struct
p.RenameField(ident.WithChild(f.GoIdent, f.GoName), newName, false) // Oneof wrapper field (not embeddable)
ifName := ident.WithPrefix(o.GoIdent, "is")
p.RenameMethod(ident.WithChild(f.GoIdent, ifName.GoName), p.nameFor(ifName)) // Oneof interface method
} else {
p.RenameField(ident.WithChild(m.GoIdent, f.GoName), newName) // Field
p.RenameField(ident.WithChild(m.GoIdent, f.GoName), newName, embedded) // Field
}
p.RenameMethod(ident.WithChild(m.GoIdent, "Get"+f.GoName), "Get"+newName) // Getter
}
Expand Down Expand Up @@ -344,9 +358,12 @@ func (p *Patcher) RenameValue(id protogen.GoIdent, newName string) {
// The id argument specifies a GoName from GoImportPath, e.g.: "github.com/org/repo/example".FooMessage.BarField
// newName should be the unqualified name (after the dot).
// The value of id.GoName should be the original generated identifier name, not a renamed identifier.
func (p *Patcher) RenameField(id protogen.GoIdent, newName string) {
func (p *Patcher) RenameField(id protogen.GoIdent, newName string, embedded bool) {
p.renames[id] = newName
p.fieldRenames[id] = newName
if embedded {
p.embedded[id] = newName
}
log.Printf("Rename field:\t%s.%s → %s", id.GoImportPath, id.GoName, newName)
}

Expand Down Expand Up @@ -510,6 +527,9 @@ func (p *Patcher) checkGoFiles() error {
continue
}
p.objectRenames[obj] = name
if _, ok := p.embedded[id]; ok {
p.fieldEmbeds[obj] = name
}
}

// Map struct tags.
Expand Down Expand Up @@ -648,7 +668,7 @@ func (p *Patcher) serializeGoFiles(res *pluginpb.CodeGeneratorResponse) error {
func (p *Patcher) patchGoFiles() error {
log.Printf("\nDefs")
for id, obj := range p.info.Defs {
p.patchIdent(id, obj)
p.patchIdent(id, obj, true)
p.patchTags(id, obj)
// if id.IsExported() {
// f := p.fset.File(id.NamePos)
Expand All @@ -658,27 +678,32 @@ func (p *Patcher) patchGoFiles() error {

log.Printf("\nUses\n")
for id, obj := range p.info.Uses {
p.patchIdent(id, obj)
p.patchIdent(id, obj, false)
}

log.Printf("\nUnresolved\n")
for _, f := range p.filesByName {
for _, id := range f.Unresolved {
p.patchIdent(id, nil)
p.patchIdent(id, nil, false)
}
}

return nil
}

func (p *Patcher) patchIdent(id *ast.Ident, obj types.Object) {
func (p *Patcher) patchIdent(id *ast.Ident, obj types.Object, isDecl bool) {
name := p.objectRenames[obj]
if name != "" {
p.patchComments(id, name)
id.Name = name
log.Printf("Renamed %s:\t%s → %s", typeString(obj), id.Name, name)
} else {
if name == "" {
// log.Printf("Unresolved:\t%v", id)
return
}
p.patchComments(id, name)
if _, ok := p.fieldEmbeds[obj]; ok && isDecl {
log.Printf("Renamed %s:\t%s → %s (embedded)", typeString(obj), id.Name, name)
id.Name = ""
} else {
log.Printf("Renamed %s:\t%s → %s", typeString(obj), id.Name, name)
id.Name = name
}
}

Expand Down
Loading

0 comments on commit 8e813ed

Please sign in to comment.