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 }