Changes: https://github.com/containerd/typeurl/compare/7f6e6d160d67...v2.1.0 Signed-off-by: Akihiro Suda <akihiro.suda.cz@hco.ntt.co.jp>
		
			
				
	
	
		
			189 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			5.3 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 main
 | 
						|
 | 
						|
import (
 | 
						|
	"google.golang.org/protobuf/compiler/protogen"
 | 
						|
	"google.golang.org/protobuf/reflect/protoreflect"
 | 
						|
)
 | 
						|
 | 
						|
type generator struct {
 | 
						|
	out *protogen.GeneratedFile
 | 
						|
}
 | 
						|
 | 
						|
func newGenerator(out *protogen.GeneratedFile) *generator {
 | 
						|
	gen := generator{out: out}
 | 
						|
	return &gen
 | 
						|
}
 | 
						|
 | 
						|
func (gen *generator) genFieldMethod(m *protogen.Message) {
 | 
						|
	p := gen.out
 | 
						|
 | 
						|
	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 *", m.GoIdent, ") Field(fieldpath []string) (string, bool) {")
 | 
						|
 | 
						|
	var (
 | 
						|
		fields    []*protogen.Field
 | 
						|
		unhandled []*protogen.Field
 | 
						|
	)
 | 
						|
 | 
						|
	for _, f := range m.Fields {
 | 
						|
		if f.Desc.Kind() == protoreflect.BoolKind ||
 | 
						|
			f.Desc.Kind() == protoreflect.StringKind ||
 | 
						|
			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.P(`return "", false`)
 | 
						|
		p.P("}")
 | 
						|
 | 
						|
		p.P("switch fieldpath[0] {")
 | 
						|
 | 
						|
		for _, f := range unhandled {
 | 
						|
			p.P("// unhandled: ", f.Desc.Name())
 | 
						|
		}
 | 
						|
 | 
						|
		for _, f := range fields {
 | 
						|
			p.P(`case "`, f.Desc.Name(), `":`)
 | 
						|
			switch {
 | 
						|
			case isLabelsField(f):
 | 
						|
				stringsJoin := gen.out.QualifiedGoIdent(protogen.GoIdent{
 | 
						|
					GoImportPath: "strings",
 | 
						|
					GoName:       "Join",
 | 
						|
				})
 | 
						|
 | 
						|
				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.", f.GoName, ") == 0 {")
 | 
						|
				p.P(`return "", false`)
 | 
						|
				p.P("}")
 | 
						|
				p.P("value, ok := m.", f.GoName, "[", stringsJoin, `(fieldpath[1:], ".")]`)
 | 
						|
				p.P("return value, ok")
 | 
						|
			case isAnyField(f):
 | 
						|
				typeurlUnmarshalAny := gen.out.QualifiedGoIdent(protogen.GoIdent{
 | 
						|
					GoImportPath: "github.com/containerd/typeurl/v2",
 | 
						|
					GoName:       "UnmarshalAny",
 | 
						|
				})
 | 
						|
 | 
						|
				p.P("decoded, err := ", typeurlUnmarshalAny, "(m.", f.GoName, ")")
 | 
						|
				p.P("if err != nil {")
 | 
						|
				p.P(`return "", false`)
 | 
						|
				p.P("}")
 | 
						|
				p.P("adaptor, ok := decoded.(interface{ Field([]string) (string, bool) })")
 | 
						|
				p.P("if !ok {")
 | 
						|
				p.P(`return "", false`)
 | 
						|
				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.", f.GoName, " == nil {")
 | 
						|
				p.P(`return "", false`)
 | 
						|
				p.P("}")
 | 
						|
				p.P("return m.", f.GoName, ".Field(fieldpath[1:])")
 | 
						|
			case f.Desc.Kind() == protoreflect.StringKind:
 | 
						|
				p.P("return string(m.", f.GoName, "), len(m.", f.GoName, ") > 0")
 | 
						|
			case f.Desc.Kind() == protoreflect.BoolKind:
 | 
						|
				fmtSprint := gen.out.QualifiedGoIdent(protogen.GoIdent{
 | 
						|
					GoImportPath: "fmt",
 | 
						|
					GoName:       "Sprint",
 | 
						|
				})
 | 
						|
 | 
						|
				p.P("return ", fmtSprint, "(m.", f.GoName, "), true")
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		p.P("}")
 | 
						|
	} else {
 | 
						|
		for _, f := range unhandled {
 | 
						|
			p.P("// unhandled: ", f.Desc.Name())
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	p.P(`return "", false`)
 | 
						|
	p.P("}")
 | 
						|
}
 | 
						|
 | 
						|
func isMessageField(f *protogen.Field) bool {
 | 
						|
	return f.Desc.Kind() == protoreflect.MessageKind && f.Desc.Cardinality() != protoreflect.Repeated && f.Message.GoIdent.GoName != "Timestamp"
 | 
						|
}
 | 
						|
 | 
						|
func isLabelsField(f *protogen.Field) bool {
 | 
						|
	return f.Desc.Kind() == protoreflect.MessageKind && f.Desc.Name() == "labels"
 | 
						|
}
 | 
						|
 | 
						|
func isAnyField(f *protogen.Field) bool {
 | 
						|
	return f.Desc.Kind() == protoreflect.MessageKind && f.Message.GoIdent.GoName == "Any"
 | 
						|
}
 | 
						|
 | 
						|
func collectChildlen(parent *protogen.Message) ([]*protogen.Message, error) {
 | 
						|
	var children []*protogen.Message
 | 
						|
	for _, child := range parent.Messages {
 | 
						|
		if child.Desc.IsMapEntry() {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		children = append(children, child)
 | 
						|
 | 
						|
		xs, err := collectChildlen(child)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
		children = append(children, xs...)
 | 
						|
	}
 | 
						|
	return children, nil
 | 
						|
}
 | 
						|
 | 
						|
func generate(plugin *protogen.Plugin, input *protogen.File) error {
 | 
						|
	var messages []*protogen.Message
 | 
						|
	for _, m := range input.Messages {
 | 
						|
		messages = append(messages, m)
 | 
						|
		children, err := collectChildlen(m)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		messages = append(messages, children...)
 | 
						|
	}
 | 
						|
 | 
						|
	if len(messages) == 0 {
 | 
						|
		// Don't generate a Go file, if that would be empty.
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	file := plugin.NewGeneratedFile(input.GeneratedFilenamePrefix+"_fieldpath.pb.go", input.GoImportPath)
 | 
						|
	file.P("// Code generated by protoc-gen-go-fieldpath. DO NOT EDIT.")
 | 
						|
	file.P("// source: ", input.Desc.Path())
 | 
						|
	file.P("package ", input.GoPackageName)
 | 
						|
 | 
						|
	gen := newGenerator(file)
 | 
						|
 | 
						|
	for _, m := range messages {
 | 
						|
		gen.genFieldMethod(m)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 |