209 lines
5.4 KiB
Go
209 lines
5.4 KiB
Go
|
/*
|
||
|
Copyright 2015 The Kubernetes 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 protobuf
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
|
||
|
"k8s.io/gengo/generator"
|
||
|
"k8s.io/gengo/namer"
|
||
|
"k8s.io/gengo/types"
|
||
|
)
|
||
|
|
||
|
type localNamer struct {
|
||
|
localPackage types.Name
|
||
|
}
|
||
|
|
||
|
func (n localNamer) Name(t *types.Type) string {
|
||
|
if t.Key != nil && t.Elem != nil {
|
||
|
return fmt.Sprintf("map<%s, %s>", n.Name(t.Key), n.Name(t.Elem))
|
||
|
}
|
||
|
if len(n.localPackage.Package) != 0 && n.localPackage.Package == t.Name.Package {
|
||
|
return t.Name.Name
|
||
|
}
|
||
|
return t.Name.String()
|
||
|
}
|
||
|
|
||
|
type protobufNamer struct {
|
||
|
packages []*protobufPackage
|
||
|
packagesByPath map[string]*protobufPackage
|
||
|
}
|
||
|
|
||
|
func NewProtobufNamer() *protobufNamer {
|
||
|
return &protobufNamer{
|
||
|
packagesByPath: make(map[string]*protobufPackage),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (n *protobufNamer) Name(t *types.Type) string {
|
||
|
if t.Kind == types.Map {
|
||
|
return fmt.Sprintf("map<%s, %s>", n.Name(t.Key), n.Name(t.Elem))
|
||
|
}
|
||
|
return t.Name.String()
|
||
|
}
|
||
|
|
||
|
func (n *protobufNamer) List() []generator.Package {
|
||
|
packages := make([]generator.Package, 0, len(n.packages))
|
||
|
for i := range n.packages {
|
||
|
packages = append(packages, n.packages[i])
|
||
|
}
|
||
|
return packages
|
||
|
}
|
||
|
|
||
|
func (n *protobufNamer) Add(p *protobufPackage) {
|
||
|
if _, ok := n.packagesByPath[p.PackagePath]; !ok {
|
||
|
n.packagesByPath[p.PackagePath] = p
|
||
|
n.packages = append(n.packages, p)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (n *protobufNamer) GoNameToProtoName(name types.Name) types.Name {
|
||
|
if p, ok := n.packagesByPath[name.Package]; ok {
|
||
|
return types.Name{
|
||
|
Name: name.Name,
|
||
|
Package: p.PackageName,
|
||
|
Path: p.ImportPath(),
|
||
|
}
|
||
|
}
|
||
|
for _, p := range n.packages {
|
||
|
if _, ok := p.FilterTypes[name]; ok {
|
||
|
return types.Name{
|
||
|
Name: name.Name,
|
||
|
Package: p.PackageName,
|
||
|
Path: p.ImportPath(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return types.Name{Name: name.Name}
|
||
|
}
|
||
|
|
||
|
func protoSafePackage(name string) string {
|
||
|
pkg := strings.Replace(name, "/", ".", -1)
|
||
|
return strings.Replace(pkg, "-", "_", -1)
|
||
|
}
|
||
|
|
||
|
type typeNameSet map[types.Name]*protobufPackage
|
||
|
|
||
|
// assignGoTypeToProtoPackage looks for Go and Protobuf types that are referenced by a type in
|
||
|
// a package. It will not recurse into protobuf types.
|
||
|
func assignGoTypeToProtoPackage(p *protobufPackage, t *types.Type, local, global typeNameSet, optional map[types.Name]struct{}) {
|
||
|
newT, isProto := isFundamentalProtoType(t)
|
||
|
if isProto {
|
||
|
t = newT
|
||
|
}
|
||
|
if otherP, ok := global[t.Name]; ok {
|
||
|
if _, ok := local[t.Name]; !ok {
|
||
|
p.Imports.AddType(&types.Type{
|
||
|
Kind: types.Protobuf,
|
||
|
Name: otherP.ProtoTypeName(),
|
||
|
})
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
if t.Name.Package == p.PackagePath {
|
||
|
// Associate types only to their own package
|
||
|
global[t.Name] = p
|
||
|
}
|
||
|
if _, ok := local[t.Name]; ok {
|
||
|
return
|
||
|
}
|
||
|
// don't recurse into existing proto types
|
||
|
if isProto {
|
||
|
p.Imports.AddType(t)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
local[t.Name] = p
|
||
|
for _, m := range t.Members {
|
||
|
if namer.IsPrivateGoName(m.Name) {
|
||
|
continue
|
||
|
}
|
||
|
field := &protoField{}
|
||
|
tag := reflect.StructTag(m.Tags).Get("protobuf")
|
||
|
if tag == "-" {
|
||
|
continue
|
||
|
}
|
||
|
if err := protobufTagToField(tag, field, m, t, p.ProtoTypeName()); err == nil && field.Type != nil {
|
||
|
assignGoTypeToProtoPackage(p, field.Type, local, global, optional)
|
||
|
continue
|
||
|
}
|
||
|
assignGoTypeToProtoPackage(p, m.Type, local, global, optional)
|
||
|
}
|
||
|
// TODO: should methods be walked?
|
||
|
if t.Elem != nil {
|
||
|
assignGoTypeToProtoPackage(p, t.Elem, local, global, optional)
|
||
|
}
|
||
|
if t.Key != nil {
|
||
|
assignGoTypeToProtoPackage(p, t.Key, local, global, optional)
|
||
|
}
|
||
|
if t.Underlying != nil {
|
||
|
if t.Kind == types.Alias && isOptionalAlias(t) {
|
||
|
optional[t.Name] = struct{}{}
|
||
|
}
|
||
|
assignGoTypeToProtoPackage(p, t.Underlying, local, global, optional)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// isTypeApplicableToProtobuf checks to see if a type is relevant for protobuf processing.
|
||
|
// Currently, it filters out functions and private types.
|
||
|
func isTypeApplicableToProtobuf(t *types.Type) bool {
|
||
|
// skip functions -- we don't care about them for protobuf
|
||
|
if t.Kind == types.Func || (t.Kind == types.DeclarationOf && t.Underlying.Kind == types.Func) {
|
||
|
return false
|
||
|
}
|
||
|
// skip private types
|
||
|
if namer.IsPrivateGoName(t.Name.Name) {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (n *protobufNamer) AssignTypesToPackages(c *generator.Context) error {
|
||
|
global := make(typeNameSet)
|
||
|
for _, p := range n.packages {
|
||
|
local := make(typeNameSet)
|
||
|
optional := make(map[types.Name]struct{})
|
||
|
p.Imports = NewImportTracker(p.ProtoTypeName())
|
||
|
for _, t := range c.Order {
|
||
|
if t.Name.Package != p.PackagePath {
|
||
|
continue
|
||
|
}
|
||
|
if !isTypeApplicableToProtobuf(t) {
|
||
|
// skip types that we don't care about, like functions
|
||
|
continue
|
||
|
}
|
||
|
assignGoTypeToProtoPackage(p, t, local, global, optional)
|
||
|
}
|
||
|
p.FilterTypes = make(map[types.Name]struct{})
|
||
|
p.LocalNames = make(map[string]struct{})
|
||
|
p.OptionalTypeNames = make(map[string]struct{})
|
||
|
for k, v := range local {
|
||
|
if v == p {
|
||
|
p.FilterTypes[k] = struct{}{}
|
||
|
p.LocalNames[k.Name] = struct{}{}
|
||
|
if _, ok := optional[k]; ok {
|
||
|
p.OptionalTypeNames[k.Name] = struct{}{}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|