mirror of
https://github.com/gravitational/teleport.git
synced 2026-11-02 14:59:53 +08:00
* 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/`
298 lines
7.3 KiB
Go
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, " ")
|
|
}
|