Files
teleport/integrations/operator/crdgen/tree.go
T
Hugo Shaka 9e1f53d33a Move operator into integrations/ (#22618)
* Move operator into `integrations/`

* fixup! Move operator into `integrations/`

* fixup! fixup! Move operator into `integrations/`

* fixup! fixup! fixup! Move operator into `integrations/`

* fixup! fixup! fixup! fixup! Move operator into `integrations/`
2023-03-06 17:59:02 +00:00

298 lines
7.3 KiB
Go

/*
Copyright 2021-2022 Gravitational, Inc.
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 (
"fmt"
"strconv"
"strings"
"github.com/gogo/protobuf/gogoproto"
"github.com/gogo/protobuf/proto"
gogodesc "github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
gogogen "github.com/gogo/protobuf/protoc-gen-gogo/generator"
)
// Forest is a forest of file trees. It's a wrapper for generator.Generator.
type Forest struct {
*gogogen.Generator
messageMap map[*gogodesc.DescriptorProto]*Message
}
// File is a wrapper for generator.FileDescriptor
type File struct {
parent *Forest
desc *gogodesc.FileDescriptorProto
locations map[string]Location
messageMap map[*gogodesc.DescriptorProto]*Message
messageByName map[string]*Message
Messages []*Message
}
// Message is a wrapper for generator.Descriptor.
type Message struct {
index int
parent *File
parentMsg *Message
desc *gogodesc.DescriptorProto
Fields []*Field
fieldMap map[string]*Field
}
// Field is wrapper for descriptor.FieldDescriptorProto.
type Field struct {
index int
parent *Message
desc *gogodesc.FieldDescriptorProto
}
// Location is a wrapper for descriptor.SourceCodeInfo_Location.
type Location struct {
loc *gogodesc.SourceCodeInfo_Location
}
func (forest *Forest) addFile(fileDesc *gogodesc.FileDescriptorProto) *File {
locs := fileDesc.GetSourceCodeInfo().GetLocation()
msgs := fileDesc.GetMessageType()
file := File{
parent: forest,
desc: fileDesc,
locations: make(map[string]Location, len(locs)),
Messages: make([]*Message, len(msgs)),
messageMap: make(map[*gogodesc.DescriptorProto]*Message, len(msgs)),
messageByName: make(map[string]*Message, len(msgs)),
}
// Build locations map.
for _, loc := range locs {
path := loc.GetPath()
pathStr := make([]string, len(path))
for i, point := range path {
pathStr[i] = strconv.Itoa(int(point))
}
file.locations[strings.Join(pathStr, ",")] = Location{loc: loc}
}
// Build messages list.
for i, msgDesc := range msgs {
msgDesc.GetName()
flds := msgDesc.GetField()
message := Message{
index: i,
desc: msgDesc,
parent: &file,
Fields: make([]*Field, 0, len(flds)),
fieldMap: make(map[string]*Field, len(flds)),
}
for j, fld := range flds {
field := Field{
index: j,
parent: &message,
desc: fld,
}
message.Fields = append(message.Fields, &field)
message.fieldMap[field.Name()] = &field
}
file.Messages[i] = &message
file.messageMap[msgDesc] = &message
file.messageByName[message.Name()] = &message
forest.messageMap[msgDesc] = &message
}
return &file
}
func (file File) Forest() *Forest {
return file.parent
}
func (file File) Name() string {
return file.desc.GetName()
}
func (file File) Package() string {
return file.desc.GetPackage()
}
func (message Message) Name() string {
return message.desc.GetName()
}
func (message Message) File() *File {
return message.parent
}
func (message Message) Forest() *Forest {
return message.parent.Forest()
}
func (message Message) Path() string {
if message.parentMsg != nil {
return fmt.Sprintf("%s,4,%d", message.parentMsg.Path(), message.index)
}
return fmt.Sprintf("4,%d", message.index)
}
func (message Message) GetField(name string) (*Field, bool) {
field, ok := message.fieldMap[name]
return field, ok
}
func (message Message) LeadingComments() string {
return message.File().locations[message.Path()].LeadingComments()
}
func (field Field) Name() string {
return field.desc.GetName()
}
func (field Field) Message() *Message {
return field.parent
}
func (field Field) File() *File {
return field.parent.File()
}
func (field Field) Forest() *Forest {
return field.parent.Forest()
}
func (field Field) Path() string {
return fmt.Sprintf("%s,2,%d", field.parent.Path(), field.index)
}
func (field Field) TypeMessage() *Message {
if !field.IsMessage() {
return nil
}
forest := field.Forest()
obj := forest.ObjectNamed(field.TypeName())
desc, ok := obj.(*gogogen.Descriptor)
if !ok {
return nil
}
return forest.messageMap[desc.DescriptorProto]
}
func (field Field) TypeName() string {
return field.desc.GetTypeName()
}
func (field Field) CastType() string {
return gogoproto.GetCastType(field.desc)
}
func (field Field) CustomType() string {
return gogoproto.GetCustomType(field.desc)
}
func (field Field) JSONName() string {
if res := gogoproto.GetJsonTag(field.desc); res != nil {
return strings.Split(*res, ",")[0]
}
return ""
}
func (field Field) IsTime() bool {
isStdTime := gogoproto.IsStdTime(field.desc)
isGoogleTime := strings.HasSuffix(field.TypeName(), "google.protobuf.Timestamp")
isCastToTime := field.CastType() == "time.Time"
return isStdTime || isGoogleTime || isCastToTime
}
// IsDuration returns true if field stores a duration value (protobuf or cast to a standard library type)
func (field Field) IsDuration() bool {
isStdDuration := gogoproto.IsStdDuration(field.desc)
isGoogleDuration := strings.HasSuffix(field.TypeName(), "google.protobuf.Duration")
isCastToCustomDuration := field.CastType() == "Duration"
isCastToDuration := field.CastType() == "time.Duration"
return isStdDuration || isGoogleDuration || isCastToDuration || isCastToCustomDuration
}
// IsMap returns true if field stores a map.
func (field Field) IsMap() bool {
return field.Forest().IsMap(field.desc)
}
// IsMessage returns true if field is a message.
func (field Field) IsMessage() bool {
return field.desc.IsMessage()
}
func (field Field) IsString() bool {
return field.desc.IsString()
}
func (field Field) IsBool() bool {
return field.desc.IsBool() || field.TypeName() == ".types.BoolValue"
}
func (field Field) IsInt64() bool {
return field.desc.GetType() == gogodesc.FieldDescriptorProto_TYPE_INT64
}
func (field Field) IsUint64() bool {
return field.desc.GetType() == gogodesc.FieldDescriptorProto_TYPE_UINT64
}
func (field Field) IsInt32() bool {
return field.desc.GetType() == gogodesc.FieldDescriptorProto_TYPE_INT32
}
func (field Field) IsUint32() bool {
return field.desc.GetType() == gogodesc.FieldDescriptorProto_TYPE_UINT32
}
func (field Field) IsRepeated() bool {
return field.desc.IsRepeated()
}
func (field Field) IsRequired() bool {
return field.desc.IsRequired()
}
func (field Field) IsOptional() bool {
return field.desc.GetLabel() == gogodesc.FieldDescriptorProto_LABEL_OPTIONAL
}
func (field Field) IsNullable() bool {
return proto.GetBoolExtension(field.desc.GetOptions(), gogoproto.E_Nullable, true)
}
func (field Field) LeadingComments() string {
return field.File().locations[field.Path()].LeadingComments()
}
func (location Location) LeadingComments() string {
lines := strings.Split(location.loc.GetLeadingComments(), "\n")
n := len(lines)
if n == 0 {
return ""
}
if lines[n-1] == "" {
lines = lines[:n-1]
}
for i, line := range lines {
lines[i] = strings.TrimSpace(line)
}
return strings.Join(lines, " ")
}