github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/protobuf/plugin/fieldpath/fieldpath.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package fieldpath
    18  
    19  import (
    20  	"strings"
    21  
    22  	"github.com/containerd/containerd/protobuf/plugin"
    23  	"github.com/gogo/protobuf/gogoproto"
    24  	"github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
    25  	"github.com/gogo/protobuf/protoc-gen-gogo/generator"
    26  )
    27  
    28  type fieldpathGenerator struct {
    29  	*generator.Generator
    30  	generator.PluginImports
    31  	typeurlPkg generator.Single
    32  }
    33  
    34  func init() {
    35  	generator.RegisterPlugin(new(fieldpathGenerator))
    36  }
    37  
    38  func (p *fieldpathGenerator) Name() string {
    39  	return "fieldpath"
    40  }
    41  
    42  func (p *fieldpathGenerator) Init(g *generator.Generator) {
    43  	p.Generator = g
    44  }
    45  
    46  func (p *fieldpathGenerator) Generate(file *generator.FileDescriptor) {
    47  	p.PluginImports = generator.NewPluginImports(p.Generator)
    48  	p.typeurlPkg = p.NewImport("github.com/containerd/typeurl")
    49  
    50  	for _, m := range file.Messages() {
    51  		if m.DescriptorProto.GetOptions().GetMapEntry() {
    52  			continue
    53  		}
    54  		if plugin.FieldpathEnabled(file.FileDescriptorProto, m.DescriptorProto) {
    55  			p.generateMessage(m)
    56  		}
    57  	}
    58  }
    59  
    60  func (p *fieldpathGenerator) generateMessage(m *generator.Descriptor) {
    61  	ccTypeName := generator.CamelCaseSlice(m.TypeName())
    62  
    63  	p.P()
    64  	p.P(`// Field returns the value for the given fieldpath as a string, if defined.`)
    65  	p.P(`// If the value is not defined, the second value will be false.`)
    66  	p.P("func(m *", ccTypeName, ") Field(fieldpath []string) (string, bool) {")
    67  	p.In()
    68  
    69  	var (
    70  		fields    []*descriptor.FieldDescriptorProto
    71  		unhandled []*descriptor.FieldDescriptorProto
    72  	)
    73  
    74  	for _, f := range m.Field {
    75  		if f.IsBool() || f.IsString() || isLabelsField(f) || isAnyField(f) || isMessageField(f) {
    76  			fields = append(fields, f)
    77  		} else {
    78  			unhandled = append(unhandled, f)
    79  		}
    80  	}
    81  
    82  	if len(fields) > 0 {
    83  		p.P(`if len(fieldpath) == 0 {`)
    84  		p.In()
    85  		p.P(`return "", false`)
    86  		p.Out()
    87  		p.P(`}`)
    88  		p.P()
    89  		p.P("switch fieldpath[0] {")
    90  
    91  		for _, f := range unhandled {
    92  			p.P("// unhandled: ", f.GetName())
    93  		}
    94  
    95  		for _, f := range fields {
    96  			fName := generator.CamelCase(*f.Name)
    97  			if gogoproto.IsCustomName(f) {
    98  				fName = gogoproto.GetCustomName(f)
    99  			}
   100  
   101  			p.P(`case "`, f.GetName(), `":`)
   102  			p.In()
   103  			switch {
   104  			case isLabelsField(f):
   105  				p.P(`// Labels fields have been special-cased by name. If this breaks,`)
   106  				p.P(`// add better special casing to fieldpath plugin.`)
   107  				p.P(`if len(m.`, fName, `) == 0 {`)
   108  				p.In()
   109  				p.P(`return "", false`)
   110  				p.Out()
   111  				p.P("}")
   112  				p.P(`value, ok := m.`, fName, `[strings.Join(fieldpath[1:], ".")]`)
   113  				p.P(`return value, ok`)
   114  			case isAnyField(f):
   115  				p.P(`decoded, err := `, p.typeurlPkg.Use(), `.UnmarshalAny(m.`, fName, `)`)
   116  				p.P(`if err != nil {`)
   117  				p.In()
   118  				p.P(`return "", false`)
   119  				p.Out()
   120  				p.P(`}`)
   121  				p.P()
   122  				p.P(`adaptor, ok := decoded.(interface { Field([]string) (string, bool) })`)
   123  				p.P(`if !ok {`)
   124  				p.In()
   125  				p.P(`return "", false`)
   126  				p.Out()
   127  				p.P(`}`)
   128  				p.P(`return adaptor.Field(fieldpath[1:])`)
   129  			case isMessageField(f):
   130  				p.P(`// NOTE(stevvooe): This is probably not correct in many cases.`)
   131  				p.P(`// We assume that the target message also implements the Field`)
   132  				p.P(`// method, which isn't likely true in a lot of cases.`)
   133  				p.P(`//`)
   134  				p.P(`// If you have a broken build and have found this comment,`)
   135  				p.P(`// you may be closer to a solution.`)
   136  				p.P(`if m.`, fName, ` == nil {`)
   137  				p.In()
   138  				p.P(`return "", false`)
   139  				p.Out()
   140  				p.P(`}`)
   141  				p.P()
   142  				p.P(`return m.`, fName, `.Field(fieldpath[1:])`)
   143  			case f.IsString():
   144  				p.P(`return string(m.`, fName, `), len(m.`, fName, `) > 0`)
   145  			case f.IsBool():
   146  				p.P(`return fmt.Sprint(m.`, fName, `), true`)
   147  			}
   148  			p.Out()
   149  		}
   150  
   151  		p.P(`}`)
   152  	} else {
   153  		for _, f := range unhandled {
   154  			p.P("// unhandled: ", f.GetName())
   155  		}
   156  	}
   157  
   158  	p.P(`return "", false`)
   159  	p.Out()
   160  	p.P("}")
   161  }
   162  
   163  func isMessageField(f *descriptor.FieldDescriptorProto) bool {
   164  	return !f.IsRepeated() && f.IsMessage() && f.GetTypeName() != ".google.protobuf.Timestamp"
   165  }
   166  
   167  func isLabelsField(f *descriptor.FieldDescriptorProto) bool {
   168  	return f.IsMessage() && f.GetName() == "labels" && strings.HasSuffix(f.GetTypeName(), ".LabelsEntry")
   169  }
   170  
   171  func isAnyField(f *descriptor.FieldDescriptorProto) bool {
   172  	return !f.IsRepeated() && f.IsMessage() && f.GetTypeName() == ".google.protobuf.Any"
   173  }