github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/cmd/internal/protobuf/v3/message.go (about)

     1  package v3
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"github.com/iancoleman/strcase"
     7  	"github.com/unionj-cloud/go-doudou/cmd/internal/astutils"
     8  	"github.com/unionj-cloud/go-doudou/toolkit/sliceutils"
     9  	"github.com/unionj-cloud/go-doudou/toolkit/stringutils"
    10  	"reflect"
    11  	"regexp"
    12  	"strings"
    13  	"unicode"
    14  )
    15  
    16  var _ ProtobufType = (*Enum)(nil)
    17  var _ ProtobufType = (*Message)(nil)
    18  
    19  type ProtobufType interface {
    20  	GetName() string
    21  	String() string
    22  	Inner() bool
    23  }
    24  
    25  var MessageStore = make(map[string]Message)
    26  
    27  var EnumStore = make(map[string]Enum)
    28  
    29  var ImportStore = make(map[string]struct{})
    30  
    31  var MessageNames []string
    32  
    33  type EnumField struct {
    34  	Name   string
    35  	Number int
    36  }
    37  
    38  func newEnumField(field string, index int) EnumField {
    39  	return EnumField{
    40  		Name:   strings.ToUpper(strcase.ToSnake(field)),
    41  		Number: index,
    42  	}
    43  }
    44  
    45  type Enum struct {
    46  	Name   string
    47  	Fields []EnumField
    48  }
    49  
    50  func (e Enum) Inner() bool {
    51  	return false
    52  }
    53  
    54  func (e Enum) String() string {
    55  	return e.Name
    56  }
    57  
    58  func (e Enum) GetName() string {
    59  	return e.Name
    60  }
    61  
    62  func NewEnum(enumMeta astutils.EnumMeta) Enum {
    63  	var fields []EnumField
    64  	for i, field := range enumMeta.Values {
    65  		fields = append(fields, newEnumField(field, i))
    66  	}
    67  	return Enum{
    68  		Name:   strcase.ToCamel(enumMeta.Name),
    69  		Fields: fields,
    70  	}
    71  }
    72  
    73  // Message represents protobuf message definition
    74  type Message struct {
    75  	Name       string
    76  	Fields     []Field
    77  	Comments   []string
    78  	IsInner    bool
    79  	IsScalar   bool
    80  	IsMap      bool
    81  	IsRepeated bool
    82  	IsTopLevel bool
    83  }
    84  
    85  func (m Message) Inner() bool {
    86  	return m.IsInner
    87  }
    88  
    89  func (m Message) GetName() string {
    90  	return m.Name
    91  }
    92  
    93  func (m Message) String() string {
    94  	return m.Name
    95  }
    96  
    97  // NewMessage returns message instance from astutils.StructMeta
    98  func NewMessage(structmeta astutils.StructMeta) Message {
    99  	var fields []Field
   100  	for i, field := range structmeta.Fields {
   101  		fields = append(fields, newField(field, i+1))
   102  	}
   103  	return Message{
   104  		Name:       strcase.ToCamel(structmeta.Name),
   105  		Fields:     fields,
   106  		Comments:   structmeta.Comments,
   107  		IsTopLevel: true,
   108  	}
   109  }
   110  
   111  // Field represents protobuf message field definition
   112  type Field struct {
   113  	Name     string
   114  	Type     ProtobufType
   115  	Number   int
   116  	Comments []string
   117  	JsonName string
   118  }
   119  
   120  func newField(field astutils.FieldMeta, index int) Field {
   121  	t := MessageOf(field.Type)
   122  	if t.Inner() {
   123  		message := t.(Message)
   124  		message.Name = strcase.ToCamel(field.Name)
   125  		t = message
   126  	}
   127  	jsonName := field.DocName
   128  	if stringutils.IsEmpty(jsonName) {
   129  		jsonName = strcase.ToLowerCamel(field.Name)
   130  	}
   131  	return Field{
   132  		Name:     strcase.ToSnake(field.Name),
   133  		Type:     t,
   134  		Number:   index,
   135  		Comments: field.Comments,
   136  		JsonName: jsonName,
   137  	}
   138  }
   139  
   140  var (
   141  	Double = Message{
   142  		Name:     "double",
   143  		IsScalar: true,
   144  	}
   145  	Float = Message{
   146  		Name:     "float",
   147  		IsScalar: true,
   148  	}
   149  	Int32 = Message{
   150  		Name:     "int32",
   151  		IsScalar: true,
   152  	}
   153  	Int64 = Message{
   154  		Name:     "int64",
   155  		IsScalar: true,
   156  	}
   157  	Uint32 = Message{
   158  		Name:     "uint32",
   159  		IsScalar: true,
   160  	}
   161  	Uint64 = Message{
   162  		Name:     "uint64",
   163  		IsScalar: true,
   164  	}
   165  	Bool = Message{
   166  		Name:     "bool",
   167  		IsScalar: true,
   168  	}
   169  	String = Message{
   170  		Name:     "string",
   171  		IsScalar: true,
   172  	}
   173  	Bytes = Message{
   174  		Name:     "bytes",
   175  		IsScalar: true,
   176  	}
   177  	Any = Message{
   178  		Name: "google.protobuf.Any",
   179  	}
   180  	Empty = Message{
   181  		Name: "google.protobuf.Empty",
   182  	}
   183  )
   184  
   185  func MessageOf(ft string) ProtobufType {
   186  	if astutils.IsVarargs(ft) {
   187  		ft = astutils.ToSlice(ft)
   188  	}
   189  	ft = strings.TrimLeft(ft, "*")
   190  	switch ft {
   191  	case "int", "int8", "int16", "int32", "byte", "rune", "complex64", "complex128":
   192  		return Int32
   193  	case "uint", "uint8", "uint16", "uint32":
   194  		return Uint32
   195  	case "int64":
   196  		return Int64
   197  	case "uint64", "uintptr":
   198  		return Uint64
   199  	case "bool":
   200  		return Bool
   201  	case "string", "error", "[]rune":
   202  		return String
   203  	case "[]byte", "v3.FileModel", "os.File":
   204  		return Bytes
   205  	case "float32":
   206  		return Float
   207  	case "float64":
   208  		return Double
   209  	default:
   210  		return handleDefaultCase(ft)
   211  	}
   212  }
   213  
   214  var anonystructre *regexp.Regexp
   215  
   216  func init() {
   217  	anonystructre = regexp.MustCompile(`anonystruct«(.*)»`)
   218  }
   219  
   220  func handleDefaultCase(ft string) ProtobufType {
   221  	if strings.HasPrefix(ft, "map[") {
   222  		elem := ft[strings.Index(ft, "]")+1:]
   223  		key := ft[4:strings.Index(ft, "]")]
   224  		keyMessage := MessageOf(key)
   225  		if reflect.DeepEqual(keyMessage, Float) || reflect.DeepEqual(keyMessage, Double) || reflect.DeepEqual(keyMessage, Bytes) {
   226  			panic("floating point types and bytes cannot be key_type of maps, please refer to https://developers.google.com/protocol-buffers/docs/proto3#maps")
   227  		}
   228  		elemMessage := MessageOf(elem)
   229  		if strings.HasPrefix(elemMessage.GetName(), "map<") {
   230  			panic("the value_type cannot be another map, please refer to https://developers.google.com/protocol-buffers/docs/proto3#maps")
   231  		}
   232  		return Message{
   233  			Name:  fmt.Sprintf("map<%s, %s>", keyMessage, elemMessage),
   234  			IsMap: true,
   235  		}
   236  	}
   237  	if strings.HasPrefix(ft, "[") {
   238  		elem := ft[strings.Index(ft, "]")+1:]
   239  		elemMessage := MessageOf(elem)
   240  		if strings.HasPrefix(elemMessage.GetName(), "map<") {
   241  			panic("map fields cannot be repeated, please refer to https://developers.google.com/protocol-buffers/docs/proto3#maps")
   242  		}
   243  		return Message{
   244  			Name:       fmt.Sprintf("repeated %s", elemMessage),
   245  			IsRepeated: true,
   246  		}
   247  	}
   248  	if anonystructre.MatchString(ft) {
   249  		result := anonystructre.FindStringSubmatch(ft)
   250  		var structmeta astutils.StructMeta
   251  		json.Unmarshal([]byte(result[1]), &structmeta)
   252  		message := NewMessage(structmeta)
   253  		message.IsInner = true
   254  		message.IsTopLevel = false
   255  		return message
   256  	}
   257  	var title string
   258  	if !strings.Contains(ft, ".") {
   259  		title = ft
   260  	}
   261  	if stringutils.IsEmpty(title) {
   262  		title = ft[strings.LastIndex(ft, ".")+1:]
   263  	}
   264  	if stringutils.IsNotEmpty(title) {
   265  		if unicode.IsUpper(rune(title[0])) {
   266  			if sliceutils.StringContains(MessageNames, title) {
   267  				return Message{
   268  					Name:       strcase.ToCamel(title),
   269  					IsTopLevel: true,
   270  				}
   271  			}
   272  		}
   273  		if e, ok := EnumStore[title]; ok {
   274  			return e
   275  		}
   276  	}
   277  	ImportStore["google/protobuf/any.proto"] = struct{}{}
   278  	return Any
   279  }