174 lines
4.7 KiB
Go
174 lines
4.7 KiB
Go
/*
|
|
Copyright The containerd Authors.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
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"
|
|
}
|