blob: 815f080e6bfa73858bc7990f96f3db979f9c3e95 [file] [log] [blame]
Damien Neil220c2022018-08-15 11:24:18 -07001// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// The protoc-gen-go binary is a protoc plugin to generate a Go protocol
6// buffer package.
7package main
8
9import (
Damien Neil7779e052018-09-07 14:14:06 -070010 "bytes"
11 "compress/gzip"
12 "crypto/sha256"
13 "encoding/hex"
Damien Neil3cf6e622018-09-11 13:53:14 -070014 "flag"
Damien Neil7779e052018-09-07 14:14:06 -070015 "fmt"
Damien Neilebc699d2018-09-13 08:50:13 -070016 "math"
Damien Neil7779e052018-09-07 14:14:06 -070017 "strconv"
Damien Neilcab8dfe2018-09-06 14:51:28 -070018 "strings"
Damien Neil7779e052018-09-07 14:14:06 -070019
20 "github.com/golang/protobuf/proto"
21 descpb "github.com/golang/protobuf/protoc-gen-go/descriptor"
Damien Neil220c2022018-08-15 11:24:18 -070022 "google.golang.org/proto/protogen"
Damien Neil46abb572018-09-07 12:45:37 -070023 "google.golang.org/proto/reflect/protoreflect"
Damien Neil220c2022018-08-15 11:24:18 -070024)
25
Damien Neild4127922018-09-12 11:13:49 -070026// generatedCodeVersion indicates a version of the generated code.
27// It is incremented whenever an incompatibility between the generated code and
28// proto package is introduced; the generated code references
29// a constant, proto.ProtoPackageIsVersionN (where N is generatedCodeVersion).
30const generatedCodeVersion = 2
31
Damien Neil46abb572018-09-07 12:45:37 -070032const protoPackage = "github.com/golang/protobuf/proto"
33
Damien Neil220c2022018-08-15 11:24:18 -070034func main() {
Damien Neil3cf6e622018-09-11 13:53:14 -070035 var flags flag.FlagSet
36 // TODO: Decide what to do for backwards compatibility with plugins=grpc.
37 flags.String("plugins", "", "")
38 opts := &protogen.Options{
39 ParamFunc: flags.Set,
40 }
41 protogen.Run(opts, func(gen *protogen.Plugin) error {
Damien Neil220c2022018-08-15 11:24:18 -070042 for _, f := range gen.Files {
43 if !f.Generate {
44 continue
45 }
46 genFile(gen, f)
47 }
48 return nil
49 })
50}
51
Damien Neilcab8dfe2018-09-06 14:51:28 -070052type File struct {
53 *protogen.File
Damien Neil46abb572018-09-07 12:45:37 -070054 locationMap map[string][]*descpb.SourceCodeInfo_Location
55 descriptorVar string // var containing the gzipped FileDescriptorProto
56 init []string
Damien Neilcab8dfe2018-09-06 14:51:28 -070057}
58
59func genFile(gen *protogen.Plugin, file *protogen.File) {
60 f := &File{
61 File: file,
62 locationMap: make(map[string][]*descpb.SourceCodeInfo_Location),
63 }
64 for _, loc := range file.Proto.GetSourceCodeInfo().GetLocation() {
65 key := pathKey(loc.Path)
66 f.locationMap[key] = append(f.locationMap[key], loc)
67 }
68
Damien Neil46abb572018-09-07 12:45:37 -070069 // Determine the name of the var holding the file descriptor:
70 //
71 // fileDescriptor_<hash of filename>
72 filenameHash := sha256.Sum256([]byte(f.Desc.Path()))
73 f.descriptorVar = fmt.Sprintf("fileDescriptor_%s", hex.EncodeToString(filenameHash[:8]))
74
Damien Neil082ce922018-09-06 10:23:53 -070075 g := gen.NewGeneratedFile(f.GeneratedFilenamePrefix+".pb.go", f.GoImportPath)
Damien Neil220c2022018-08-15 11:24:18 -070076 g.P("// Code generated by protoc-gen-go. DO NOT EDIT.")
Damien Neilabc6fc12018-08-23 14:39:30 -070077 g.P("// source: ", f.Desc.Path())
Damien Neil220c2022018-08-15 11:24:18 -070078 g.P()
Damien Neilcab8dfe2018-09-06 14:51:28 -070079 const filePackageField = 2 // FileDescriptorProto.package
80 genComment(g, f, []int32{filePackageField})
81 g.P()
Damien Neil082ce922018-09-06 10:23:53 -070082 g.P("package ", f.GoPackageName)
Damien Neilc7d07d92018-08-22 13:46:02 -070083 g.P()
Damien Neild4127922018-09-12 11:13:49 -070084 g.P("// This is a compile-time assertion to ensure that this generated file")
85 g.P("// is compatible with the proto package it is being compiled against.")
86 g.P("// A compilation error at this line likely means your copy of the")
87 g.P("// proto package needs to be updated.")
88 g.P("const _ = ", protogen.GoIdent{
89 GoImportPath: protoPackage,
90 GoName: fmt.Sprintf("ProtoPackageIsVersion%d", generatedCodeVersion),
91 }, "// please upgrade the proto package")
92 g.P()
Damien Neilc7d07d92018-08-22 13:46:02 -070093
Damien Neil46abb572018-09-07 12:45:37 -070094 for _, enum := range f.Enums {
95 genEnum(gen, g, f, enum)
96 }
Damien Neilcab8dfe2018-09-06 14:51:28 -070097 for _, message := range f.Messages {
98 genMessage(gen, g, f, message)
Damien Neilc7d07d92018-08-22 13:46:02 -070099 }
Damien Neil220c2022018-08-15 11:24:18 -0700100
Damien Neil46abb572018-09-07 12:45:37 -0700101 if len(f.init) != 0 {
102 g.P("func init() {")
103 for _, s := range f.init {
104 g.P(s)
105 }
106 g.P("}")
107 g.P()
108 }
109
Damien Neil7779e052018-09-07 14:14:06 -0700110 genFileDescriptor(gen, g, f)
111}
112
Damien Neilcab8dfe2018-09-06 14:51:28 -0700113func genFileDescriptor(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File) {
Damien Neil7779e052018-09-07 14:14:06 -0700114 // Trim the source_code_info from the descriptor.
115 // Marshal and gzip it.
116 descProto := proto.Clone(f.Proto).(*descpb.FileDescriptorProto)
117 descProto.SourceCodeInfo = nil
118 b, err := proto.Marshal(descProto)
119 if err != nil {
120 gen.Error(err)
121 return
122 }
123 var buf bytes.Buffer
124 w, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression)
125 w.Write(b)
126 w.Close()
127 b = buf.Bytes()
128
Damien Neil46abb572018-09-07 12:45:37 -0700129 g.P("func init() { proto.RegisterFile(", strconv.Quote(f.Desc.Path()), ", ", f.descriptorVar, ") }")
Damien Neil7779e052018-09-07 14:14:06 -0700130 g.P()
Damien Neil46abb572018-09-07 12:45:37 -0700131 g.P("var ", f.descriptorVar, " = []byte{")
Damien Neil7779e052018-09-07 14:14:06 -0700132 g.P("// ", len(b), " bytes of a gzipped FileDescriptorProto")
133 for len(b) > 0 {
134 n := 16
135 if n > len(b) {
136 n = len(b)
137 }
138
139 s := ""
140 for _, c := range b[:n] {
141 s += fmt.Sprintf("0x%02x,", c)
142 }
143 g.P(s)
144
145 b = b[n:]
146 }
147 g.P("}")
148 g.P()
Damien Neil220c2022018-08-15 11:24:18 -0700149}
Damien Neilc7d07d92018-08-22 13:46:02 -0700150
Damien Neil46abb572018-09-07 12:45:37 -0700151func genEnum(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File, enum *protogen.Enum) {
152 genComment(g, f, enum.Path)
153 // TODO: deprecation
154 g.P("type ", enum.GoIdent, " int32")
155 g.P("const (")
156 for _, value := range enum.Values {
157 genComment(g, f, value.Path)
158 // TODO: deprecation
159 g.P(value.GoIdent, " ", enum.GoIdent, " = ", value.Desc.Number())
160 }
161 g.P(")")
162 g.P()
163 nameMap := enum.GoIdent.GoName + "_name"
164 g.P("var ", nameMap, " = map[int32]string{")
165 generated := make(map[protoreflect.EnumNumber]bool)
166 for _, value := range enum.Values {
167 duplicate := ""
168 if _, present := generated[value.Desc.Number()]; present {
169 duplicate = "// Duplicate value: "
170 }
171 g.P(duplicate, value.Desc.Number(), ": ", strconv.Quote(string(value.Desc.Name())), ",")
172 generated[value.Desc.Number()] = true
173 }
174 g.P("}")
175 g.P()
176 valueMap := enum.GoIdent.GoName + "_value"
177 g.P("var ", valueMap, " = map[string]int32{")
178 for _, value := range enum.Values {
179 g.P(strconv.Quote(string(value.Desc.Name())), ": ", value.Desc.Number(), ",")
180 }
181 g.P("}")
182 g.P()
183 if enum.Desc.Syntax() != protoreflect.Proto3 {
184 g.P("func (x ", enum.GoIdent, ") Enum() *", enum.GoIdent, " {")
185 g.P("p := new(", enum.GoIdent, ")")
186 g.P("*p = x")
187 g.P("return p")
188 g.P("}")
189 g.P()
190 }
191 g.P("func (x ", enum.GoIdent, ") String() string {")
192 g.P("return ", protogen.GoIdent{GoImportPath: protoPackage, GoName: "EnumName"}, "(", enum.GoIdent, "_name, int32(x))")
193 g.P("}")
194 g.P()
195
196 if enum.Desc.Syntax() != protoreflect.Proto3 {
197 g.P("func (x *", enum.GoIdent, ") UnmarshalJSON(data []byte) error {")
198 g.P("value, err := ", protogen.GoIdent{GoImportPath: protoPackage, GoName: "UnmarshalJSONEnum"}, "(", enum.GoIdent, `_value, data, "`, enum.GoIdent, `")`)
199 g.P("if err != nil {")
200 g.P("return err")
201 g.P("}")
202 g.P("*x = ", enum.GoIdent, "(value)")
203 g.P("return nil")
204 g.P("}")
205 g.P()
206 }
207
208 var indexes []string
209 for i := 1; i < len(enum.Path); i += 2 {
210 indexes = append(indexes, strconv.Itoa(int(enum.Path[i])))
211 }
212 g.P("func (", enum.GoIdent, ") EnumDescriptor() ([]byte, []int) {")
213 g.P("return ", f.descriptorVar, ", []int{", strings.Join(indexes, ","), "}")
214 g.P("}")
215 g.P()
216
217 genWellKnownType(g, enum.GoIdent, enum.Desc)
218
Damien Neil46abb572018-09-07 12:45:37 -0700219 f.init = append(f.init, fmt.Sprintf("%s(%q, %s, %s)",
220 g.QualifiedGoIdent(protogen.GoIdent{
221 GoImportPath: protoPackage,
222 GoName: "RegisterEnum",
223 }),
Damien Neil658051b2018-09-10 12:26:21 -0700224 enumRegistryName(enum), nameMap, valueMap,
Damien Neil46abb572018-09-07 12:45:37 -0700225 ))
226}
227
Damien Neil658051b2018-09-10 12:26:21 -0700228// enumRegistryName returns the name used to register an enum with the proto
229// package registry.
230//
231// Confusingly, this is <proto_package>.<go_ident>. This probably should have
232// been the full name of the proto enum type instead, but changing it at this
233// point would require thought.
234func enumRegistryName(enum *protogen.Enum) string {
235 // Find the FileDescriptor for this enum.
236 var desc protoreflect.Descriptor = enum.Desc
237 for {
238 p, ok := desc.Parent()
239 if !ok {
240 break
241 }
242 desc = p
243 }
244 fdesc := desc.(protoreflect.FileDescriptor)
245 return string(fdesc.Package()) + "." + enum.GoIdent.GoName
246}
247
Damien Neilcab8dfe2018-09-06 14:51:28 -0700248func genMessage(gen *protogen.Plugin, g *protogen.GeneratedFile, f *File, message *protogen.Message) {
Damien Neil0bd5a382018-09-13 15:07:10 -0700249 if message.Desc.IsMapEntry() {
250 return
251 }
252
Damien Neil658051b2018-09-10 12:26:21 -0700253 for _, e := range message.Enums {
254 genEnum(gen, g, f, e)
Damien Neil46abb572018-09-07 12:45:37 -0700255 }
256
Damien Neilcab8dfe2018-09-06 14:51:28 -0700257 genComment(g, f, message.Path)
Damien Neil658051b2018-09-10 12:26:21 -0700258 // TODO: deprecation
Damien Neilcab8dfe2018-09-06 14:51:28 -0700259 g.P("type ", message.GoIdent, " struct {")
Damien Neil658051b2018-09-10 12:26:21 -0700260 for _, field := range message.Fields {
261 if field.Desc.OneofType() != nil {
262 // TODO oneofs
263 continue
264 }
265 genComment(g, f, field.Path)
Damien Neil77f82fe2018-09-13 10:59:17 -0700266 goType, pointer := fieldGoType(g, field)
267 if pointer {
268 goType = "*" + goType
269 }
Damien Neil0bd5a382018-09-13 15:07:10 -0700270 tags := []string{
271 fmt.Sprintf("protobuf:%q", fieldProtobufTag(field)),
272 fmt.Sprintf("json:%q", fieldJSONTag(field)),
273 }
274 if field.Desc.IsMap() {
275 key := field.MessageType.Fields[0]
276 val := field.MessageType.Fields[1]
277 tags = append(tags,
278 fmt.Sprintf("protobuf_key:%q", fieldProtobufTag(key)),
279 fmt.Sprintf("protobuf_val:%q", fieldProtobufTag(val)),
280 )
281 }
282 g.P(field.GoIdent, " ", goType, " `", strings.Join(tags, " "), "`")
Damien Neil658051b2018-09-10 12:26:21 -0700283 }
284 g.P("XXX_NoUnkeyedLiteral struct{} `json:\"-\"`")
285 // TODO XXX_InternalExtensions
286 g.P("XXX_unrecognized []byte `json:\"-\"`")
287 g.P("XXX_sizecache int32 `json:\"-\"`")
Damien Neilc7d07d92018-08-22 13:46:02 -0700288 g.P("}")
289 g.P()
290
Damien Neila1c6abc2018-09-12 13:36:34 -0700291 // Reset
292 g.P("func (m *", message.GoIdent, ") Reset() { *m = ", message.GoIdent, "{} }")
293 // String
294 g.P("func (m *", message.GoIdent, ") String() string { return ", protogen.GoIdent{
295 GoImportPath: protoPackage,
296 GoName: "CompactTextString",
297 }, "(m) }")
298 // ProtoMessage
299 g.P("func (*", message.GoIdent, ") ProtoMessage() {}")
300 // Descriptor
301 var indexes []string
302 for i := 1; i < len(message.Path); i += 2 {
303 indexes = append(indexes, strconv.Itoa(int(message.Path[i])))
304 }
305 g.P("func (*", message.GoIdent, ") Descriptor() ([]byte, []int) {")
306 g.P("return ", f.descriptorVar, ", []int{", strings.Join(indexes, ","), "}")
307 g.P("}")
308 // TODO: extension support methods
309
310 // Table-driven proto support.
311 //
312 // TODO: It does not scale to keep adding another method for every
313 // operation on protos that we want to switch over to using the
314 // table-driven approach. Instead, we should only add a single method
315 // that allows getting access to the *InternalMessageInfo struct and then
316 // calling Unmarshal, Marshal, Merge, Size, and Discard directly on that.
317 messageInfoVar := "xxx_messageInfo_" + message.GoIdent.GoName
318 // XXX_Unmarshal
319 g.P("func (m *", message.GoIdent, ") XXX_Unmarshal(b []byte) error {")
320 g.P("return ", messageInfoVar, ".Unmarshal(m, b)")
321 g.P("}")
322 // XXX_Marshal
323 g.P("func (m *", message.GoIdent, ") XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {")
324 g.P("return ", messageInfoVar, ".Marshal(b, m, deterministic)")
325 g.P("}")
326 // XXX_Merge
327 g.P("func (m *", message.GoIdent, ") XXX_Merge(src proto.Message) {")
328 g.P(messageInfoVar, ".Merge(m, src)")
329 g.P("}")
330 // XXX_Size
331 g.P("func (m *", message.GoIdent, ") XXX_Size() int {")
332 g.P("return ", messageInfoVar, ".Size(m)")
333 g.P("}")
334 // XXX_DiscardUnknown
335 g.P("func (m *", message.GoIdent, ") XXX_DiscardUnknown() {")
336 g.P(messageInfoVar, ".DiscardUnknown(m)")
337 g.P("}")
338 g.P()
339 g.P("var ", messageInfoVar, " ", protogen.GoIdent{
340 GoImportPath: protoPackage,
341 GoName: "InternalMessageInfo",
342 })
343 g.P()
344
Damien Neilebc699d2018-09-13 08:50:13 -0700345 // Constants and vars holding the default values of fields.
346 for _, field := range message.Fields {
347 if !field.Desc.HasDefault() {
348 continue
349 }
350 defVarName := "Default_" + message.GoIdent.GoName + "_" + field.GoIdent.GoName
351 def := field.Desc.Default()
352 switch field.Desc.Kind() {
353 case protoreflect.StringKind:
354 g.P("const ", defVarName, " string = ", strconv.Quote(def.String()))
355 case protoreflect.BytesKind:
356 g.P("var ", defVarName, " []byte = []byte(", strconv.Quote(string(def.Bytes())), ")")
357 case protoreflect.EnumKind:
358 enum := field.EnumType
359 evalue := enum.Values[enum.Desc.Values().ByNumber(def.Enum()).Index()]
360 g.P("const ", defVarName, " ", field.EnumType.GoIdent, " = ", evalue.GoIdent)
361 case protoreflect.FloatKind, protoreflect.DoubleKind:
362 // Floating point numbers need extra handling for -Inf/Inf/NaN.
363 f := field.Desc.Default().Float()
364 goType := "float64"
365 if field.Desc.Kind() == protoreflect.FloatKind {
366 goType = "float32"
367 }
368 // funcCall returns a call to a function in the math package,
369 // possibly converting the result to float32.
370 funcCall := func(fn, param string) string {
371 s := g.QualifiedGoIdent(protogen.GoIdent{
372 GoImportPath: "math",
373 GoName: fn,
374 }) + param
375 if goType != "float64" {
376 s = goType + "(" + s + ")"
377 }
378 return s
379 }
380 switch {
381 case math.IsInf(f, -1):
382 g.P("var ", defVarName, " ", goType, " = ", funcCall("Inf", "(-1)"))
383 case math.IsInf(f, 1):
384 g.P("var ", defVarName, " ", goType, " = ", funcCall("Inf", "(1)"))
385 case math.IsNaN(f):
386 g.P("var ", defVarName, " ", goType, " = ", funcCall("NaN", "()"))
387 default:
388 g.P("const ", defVarName, " ", goType, " = ", f)
389 }
390 default:
Damien Neil77f82fe2018-09-13 10:59:17 -0700391 goType, _ := fieldGoType(g, field)
Damien Neilebc699d2018-09-13 08:50:13 -0700392 g.P("const ", defVarName, " ", goType, " = ", def.Interface())
393 }
394 }
395 g.P()
396
Damien Neil77f82fe2018-09-13 10:59:17 -0700397 // Getters.
398 for _, field := range message.Fields {
399 goType, pointer := fieldGoType(g, field)
400 defaultValue := fieldDefaultValue(g, message, field)
401 g.P("func (m *", message.GoIdent, ") Get", field.GoIdent, "() ", goType, " {")
402 if field.Desc.Syntax() == protoreflect.Proto3 || defaultValue == "nil" {
403 g.P("if m != nil {")
404 } else {
405 g.P("if m != nil && m.", field.GoIdent, " != nil {")
406 }
407 star := ""
408 if pointer {
409 star = "*"
410 }
411 g.P("return ", star, " m.", field.GoIdent)
412 g.P("}")
413 g.P("return ", defaultValue)
414 g.P("}")
415 g.P()
416 }
Damien Neila1c6abc2018-09-12 13:36:34 -0700417
Damien Neilcab8dfe2018-09-06 14:51:28 -0700418 for _, nested := range message.Messages {
419 genMessage(gen, g, f, nested)
Damien Neilc7d07d92018-08-22 13:46:02 -0700420 }
421}
Damien Neilcab8dfe2018-09-06 14:51:28 -0700422
Damien Neil77f82fe2018-09-13 10:59:17 -0700423// fieldGoType returns the Go type used for a field.
424//
425// If it returns pointer=true, the struct field is a pointer to the type.
426func fieldGoType(g *protogen.GeneratedFile, field *protogen.Field) (goType string, pointer bool) {
Damien Neil77f82fe2018-09-13 10:59:17 -0700427 pointer = true
Damien Neil658051b2018-09-10 12:26:21 -0700428 switch field.Desc.Kind() {
429 case protoreflect.BoolKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700430 goType = "bool"
Damien Neil658051b2018-09-10 12:26:21 -0700431 case protoreflect.EnumKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700432 goType = g.QualifiedGoIdent(field.EnumType.GoIdent)
Damien Neil658051b2018-09-10 12:26:21 -0700433 case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700434 goType = "int32"
Damien Neil658051b2018-09-10 12:26:21 -0700435 case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700436 goType = "uint32"
Damien Neil658051b2018-09-10 12:26:21 -0700437 case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700438 goType = "int64"
Damien Neil658051b2018-09-10 12:26:21 -0700439 case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700440 goType = "uint64"
Damien Neil658051b2018-09-10 12:26:21 -0700441 case protoreflect.FloatKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700442 goType = "float32"
Damien Neil658051b2018-09-10 12:26:21 -0700443 case protoreflect.DoubleKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700444 goType = "float64"
Damien Neil658051b2018-09-10 12:26:21 -0700445 case protoreflect.StringKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700446 goType = "string"
Damien Neil658051b2018-09-10 12:26:21 -0700447 case protoreflect.BytesKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700448 goType = "[]byte"
449 pointer = false
Damien Neil658051b2018-09-10 12:26:21 -0700450 case protoreflect.MessageKind, protoreflect.GroupKind:
Damien Neil0bd5a382018-09-13 15:07:10 -0700451 if field.Desc.IsMap() {
452 keyType, _ := fieldGoType(g, field.MessageType.Fields[0])
453 valType, _ := fieldGoType(g, field.MessageType.Fields[1])
454 return fmt.Sprintf("map[%v]%v", keyType, valType), false
455 }
Damien Neil77f82fe2018-09-13 10:59:17 -0700456 goType = "*" + g.QualifiedGoIdent(field.MessageType.GoIdent)
457 pointer = false
Damien Neil658051b2018-09-10 12:26:21 -0700458 }
459 if field.Desc.Cardinality() == protoreflect.Repeated {
Damien Neil77f82fe2018-09-13 10:59:17 -0700460 goType = "[]" + goType
461 pointer = false
Damien Neil658051b2018-09-10 12:26:21 -0700462 }
463 if field.Desc.Syntax() == protoreflect.Proto3 {
Damien Neil77f82fe2018-09-13 10:59:17 -0700464 pointer = false
Damien Neil658051b2018-09-10 12:26:21 -0700465 }
Damien Neil77f82fe2018-09-13 10:59:17 -0700466 return goType, pointer
Damien Neil658051b2018-09-10 12:26:21 -0700467}
468
469func fieldProtobufTag(field *protogen.Field) string {
470 var tag []string
471 // wire type
472 tag = append(tag, wireTypes[field.Desc.Kind()])
473 // field number
474 tag = append(tag, strconv.Itoa(int(field.Desc.Number())))
475 // cardinality
476 switch field.Desc.Cardinality() {
477 case protoreflect.Optional:
478 tag = append(tag, "opt")
479 case protoreflect.Required:
480 tag = append(tag, "req")
481 case protoreflect.Repeated:
482 tag = append(tag, "rep")
483 }
Damien Neil658051b2018-09-10 12:26:21 -0700484 // TODO: packed
485 // name
486 name := string(field.Desc.Name())
487 if field.Desc.Kind() == protoreflect.GroupKind {
488 // The name of the FieldDescriptor for a group field is
489 // lowercased. To find the original capitalization, we
490 // look in the field's MessageType.
491 name = string(field.MessageType.Desc.Name())
492 }
493 tag = append(tag, "name="+name)
494 // JSON name
495 if jsonName := field.Desc.JSONName(); jsonName != "" && jsonName != name {
496 tag = append(tag, "json="+jsonName)
497 }
498 // proto3
499 if field.Desc.Syntax() == protoreflect.Proto3 {
500 tag = append(tag, "proto3")
501 }
502 // enum
503 if field.Desc.Kind() == protoreflect.EnumKind {
504 tag = append(tag, "enum="+enumRegistryName(field.EnumType))
505 }
506 // oneof
507 if field.Desc.OneofType() != nil {
508 tag = append(tag, "oneof")
509 }
Damien Neilebc699d2018-09-13 08:50:13 -0700510 // default value
511 // This must appear last in the tag, since commas in strings aren't escaped.
512 if field.Desc.HasDefault() {
513 var def string
514 switch field.Desc.Kind() {
515 case protoreflect.BoolKind:
516 if field.Desc.Default().Bool() {
517 def = "1"
518 } else {
519 def = "0"
520 }
521 case protoreflect.BytesKind:
522 def = string(field.Desc.Default().Bytes())
523 case protoreflect.FloatKind, protoreflect.DoubleKind:
524 f := field.Desc.Default().Float()
525 switch {
526 case math.IsInf(f, -1):
527 def = "-inf"
528 case math.IsInf(f, 1):
529 def = "inf"
530 case math.IsNaN(f):
531 def = "nan"
532 default:
533 def = fmt.Sprint(f)
534 }
535 default:
536 def = fmt.Sprint(field.Desc.Default().Interface())
537 }
538 tag = append(tag, "def="+def)
539 }
Damien Neil658051b2018-09-10 12:26:21 -0700540 return strings.Join(tag, ",")
541}
542
Damien Neil77f82fe2018-09-13 10:59:17 -0700543func fieldDefaultValue(g *protogen.GeneratedFile, message *protogen.Message, field *protogen.Field) string {
544 if field.Desc.Cardinality() == protoreflect.Repeated {
545 return "nil"
546 }
547 if field.Desc.HasDefault() {
548 defVarName := "Default_" + message.GoIdent.GoName + "_" + field.GoIdent.GoName
549 if field.Desc.Kind() == protoreflect.BytesKind {
550 return "append([]byte(nil), " + defVarName + "...)"
551 }
552 return defVarName
553 }
554 switch field.Desc.Kind() {
555 case protoreflect.BoolKind:
556 return "false"
557 case protoreflect.StringKind:
558 return `""`
559 case protoreflect.MessageKind, protoreflect.GroupKind, protoreflect.BytesKind:
560 return "nil"
561 case protoreflect.EnumKind:
562 return g.QualifiedGoIdent(field.EnumType.Values[0].GoIdent)
563 default:
564 return "0"
565 }
566}
567
Damien Neil658051b2018-09-10 12:26:21 -0700568var wireTypes = map[protoreflect.Kind]string{
569 protoreflect.BoolKind: "varint",
570 protoreflect.EnumKind: "varint",
571 protoreflect.Int32Kind: "varint",
572 protoreflect.Sint32Kind: "zigzag32",
573 protoreflect.Uint32Kind: "varint",
574 protoreflect.Int64Kind: "varint",
575 protoreflect.Sint64Kind: "zigzag64",
576 protoreflect.Uint64Kind: "varint",
577 protoreflect.Sfixed32Kind: "fixed32",
578 protoreflect.Fixed32Kind: "fixed32",
579 protoreflect.FloatKind: "fixed32",
580 protoreflect.Sfixed64Kind: "fixed64",
581 protoreflect.Fixed64Kind: "fixed64",
582 protoreflect.DoubleKind: "fixed64",
583 protoreflect.StringKind: "bytes",
584 protoreflect.BytesKind: "bytes",
585 protoreflect.MessageKind: "bytes",
586 protoreflect.GroupKind: "group",
587}
588
589func fieldJSONTag(field *protogen.Field) string {
590 return string(field.Desc.Name()) + ",omitempty"
591}
592
Damien Neilcab8dfe2018-09-06 14:51:28 -0700593func genComment(g *protogen.GeneratedFile, f *File, path []int32) {
594 for _, loc := range f.locationMap[pathKey(path)] {
595 if loc.LeadingComments == nil {
596 continue
597 }
598 for _, line := range strings.Split(strings.TrimSuffix(loc.GetLeadingComments(), "\n"), "\n") {
599 g.P("//", line)
600 }
601 return
602 }
603}
604
605// pathKey converts a location path to a string suitable for use as a map key.
606func pathKey(path []int32) string {
607 var buf []byte
608 for i, x := range path {
609 if i != 0 {
610 buf = append(buf, ',')
611 }
612 buf = strconv.AppendInt(buf, int64(x), 10)
613 }
614 return string(buf)
615}
Damien Neil46abb572018-09-07 12:45:37 -0700616
617func genWellKnownType(g *protogen.GeneratedFile, ident protogen.GoIdent, desc protoreflect.Descriptor) {
618 if wellKnownTypes[desc.FullName()] {
619 g.P("func (", ident, `) XXX_WellKnownType() string { return "`, desc.Name(), `" }`)
620 g.P()
621 }
622}
623
624// Names of messages and enums for which we will generate XXX_WellKnownType methods.
625var wellKnownTypes = map[protoreflect.FullName]bool{
626 "google.protobuf.NullValue": true,
627}