containerd/protobuf/plugin/fieldpath/fieldpath.go
Michael Crosby d22160c28e Vendor typeurl package
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
2017-09-19 09:43:55 -04:00

158 lines
4.1 KiB
Go

package fieldpath
import (
"strings"
"github.com/containerd/containerd/protobuf/plugin"
"github.com/gogo/protobuf/gogoproto"
"github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
"github.com/gogo/protobuf/protoc-gen-gogo/generator"
)
type fieldpathGenerator struct {
*generator.Generator
generator.PluginImports
typeurlPkg generator.Single
}
func init() {
generator.RegisterPlugin(new(fieldpathGenerator))
}
func (p *fieldpathGenerator) Name() string {
return "fieldpath"
}
func (p *fieldpathGenerator) Init(g *generator.Generator) {
p.Generator = g
}
func (p *fieldpathGenerator) Generate(file *generator.FileDescriptor) {
p.PluginImports = generator.NewPluginImports(p.Generator)
p.typeurlPkg = p.NewImport("github.com/containerd/typeurl")
for _, m := range file.Messages() {
if m.DescriptorProto.GetOptions().GetMapEntry() {
continue
}
if plugin.FieldpathEnabled(file.FileDescriptorProto, m.DescriptorProto) {
p.generateMessage(m)
}
}
}
func (p *fieldpathGenerator) generateMessage(m *generator.Descriptor) {
ccTypeName := generator.CamelCaseSlice(m.TypeName())
p.P()
p.P(`// Field returns the value for the given fieldpath as a string, if defined.`)
p.P(`// If the value is not defined, the second value will be false.`)
p.P("func(m *", ccTypeName, ") Field(fieldpath []string) (string, bool) {")
p.In()
var (
fields []*descriptor.FieldDescriptorProto
unhandled []*descriptor.FieldDescriptorProto
)
for _, f := range m.Field {
if f.IsBool() || f.IsString() || isLabelsField(f) || isAnyField(f) || isMessageField(f) {
fields = append(fields, f)
} else {
unhandled = append(unhandled, f)
}
}
if len(fields) > 0 {
p.P(`if len(fieldpath) == 0 {`)
p.In()
p.P(`return "", false`)
p.Out()
p.P(`}`)
p.P()
p.P("switch fieldpath[0] {")
for _, f := range unhandled {
p.P("// unhandled: ", f.GetName())
}
for _, f := range fields {
fName := generator.CamelCase(*f.Name)
if gogoproto.IsCustomName(f) {
fName = gogoproto.GetCustomName(f)
}
p.P(`case "`, f.GetName(), `":`)
p.In()
switch {
case isLabelsField(f):
p.P(`// Labels fields have been special-cased by name. If this breaks,`)
p.P(`// add better special casing to fieldpath plugin.`)
p.P(`if len(m.`, fName, `) == 0 {`)
p.In()
p.P(`return "", false`)
p.Out()
p.P("}")
p.P(`value, ok := m.`, fName, `[strings.Join(fieldpath[1:], ".")]`)
p.P(`return value, ok`)
case isAnyField(f):
p.P(`decoded, err := `, p.typeurlPkg.Use(), `.UnmarshalAny(m.`, fName, `)`)
p.P(`if err != nil {`)
p.In()
p.P(`return "", false`)
p.Out()
p.P(`}`)
p.P()
p.P(`adaptor, ok := decoded.(interface { Field([]string) (string, bool) })`)
p.P(`if !ok {`)
p.In()
p.P(`return "", false`)
p.Out()
p.P(`}`)
p.P(`return adaptor.Field(fieldpath[1:])`)
case isMessageField(f):
p.P(`// NOTE(stevvooe): This is probably not correct in many cases.`)
p.P(`// We assume that the target message also implements the Field`)
p.P(`// method, which isn't likely true in a lot of cases.`)
p.P(`//`)
p.P(`// If you have a broken build and have found this comment,`)
p.P(`// you may be closer to a solution.`)
p.P(`if m.`, fName, ` == nil {`)
p.In()
p.P(`return "", false`)
p.Out()
p.P(`}`)
p.P()
p.P(`return m.`, fName, `.Field(fieldpath[1:])`)
case f.IsString():
p.P(`return string(m.`, fName, `), len(m.`, fName, `) > 0`)
case f.IsBool():
p.P(`return fmt.Sprint(m.`, fName, `), true`)
}
p.Out()
}
p.P(`}`)
} else {
for _, f := range unhandled {
p.P("// unhandled: ", f.GetName())
}
}
p.P(`return "", false`)
p.Out()
p.P("}")
}
func isMessageField(f *descriptor.FieldDescriptorProto) bool {
return !f.IsRepeated() && f.IsMessage() && f.GetTypeName() != ".google.protobuf.Timestamp"
}
func isLabelsField(f *descriptor.FieldDescriptorProto) bool {
return f.IsMessage() && f.GetName() == "labels" && strings.HasSuffix(f.GetTypeName(), ".LabelsEntry")
}
func isAnyField(f *descriptor.FieldDescriptorProto) bool {
return !f.IsRepeated() && f.IsMessage() && f.GetTypeName() == ".google.protobuf.Any"
}