blob: 3a062b46880fe038439016a966886ea9f7aef74e [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 Neil658051b2018-09-10 12:26:21 -0700249 for _, e := range message.Enums {
250 genEnum(gen, g, f, e)
Damien Neil46abb572018-09-07 12:45:37 -0700251 }
252
Damien Neilcab8dfe2018-09-06 14:51:28 -0700253 genComment(g, f, message.Path)
Damien Neil658051b2018-09-10 12:26:21 -0700254 // TODO: deprecation
Damien Neilcab8dfe2018-09-06 14:51:28 -0700255 g.P("type ", message.GoIdent, " struct {")
Damien Neil658051b2018-09-10 12:26:21 -0700256 for _, field := range message.Fields {
257 if field.Desc.OneofType() != nil {
258 // TODO oneofs
259 continue
260 }
261 genComment(g, f, field.Path)
Damien Neil77f82fe2018-09-13 10:59:17 -0700262 goType, pointer := fieldGoType(g, field)
263 if pointer {
264 goType = "*" + goType
265 }
266 g.P(field.GoIdent, " ", goType, fmt.Sprintf(" `protobuf:%q json:%q`", fieldProtobufTag(field), fieldJSONTag(field)))
Damien Neil658051b2018-09-10 12:26:21 -0700267 }
268 g.P("XXX_NoUnkeyedLiteral struct{} `json:\"-\"`")
269 // TODO XXX_InternalExtensions
270 g.P("XXX_unrecognized []byte `json:\"-\"`")
271 g.P("XXX_sizecache int32 `json:\"-\"`")
Damien Neilc7d07d92018-08-22 13:46:02 -0700272 g.P("}")
273 g.P()
274
Damien Neila1c6abc2018-09-12 13:36:34 -0700275 // Reset
276 g.P("func (m *", message.GoIdent, ") Reset() { *m = ", message.GoIdent, "{} }")
277 // String
278 g.P("func (m *", message.GoIdent, ") String() string { return ", protogen.GoIdent{
279 GoImportPath: protoPackage,
280 GoName: "CompactTextString",
281 }, "(m) }")
282 // ProtoMessage
283 g.P("func (*", message.GoIdent, ") ProtoMessage() {}")
284 // Descriptor
285 var indexes []string
286 for i := 1; i < len(message.Path); i += 2 {
287 indexes = append(indexes, strconv.Itoa(int(message.Path[i])))
288 }
289 g.P("func (*", message.GoIdent, ") Descriptor() ([]byte, []int) {")
290 g.P("return ", f.descriptorVar, ", []int{", strings.Join(indexes, ","), "}")
291 g.P("}")
292 // TODO: extension support methods
293
294 // Table-driven proto support.
295 //
296 // TODO: It does not scale to keep adding another method for every
297 // operation on protos that we want to switch over to using the
298 // table-driven approach. Instead, we should only add a single method
299 // that allows getting access to the *InternalMessageInfo struct and then
300 // calling Unmarshal, Marshal, Merge, Size, and Discard directly on that.
301 messageInfoVar := "xxx_messageInfo_" + message.GoIdent.GoName
302 // XXX_Unmarshal
303 g.P("func (m *", message.GoIdent, ") XXX_Unmarshal(b []byte) error {")
304 g.P("return ", messageInfoVar, ".Unmarshal(m, b)")
305 g.P("}")
306 // XXX_Marshal
307 g.P("func (m *", message.GoIdent, ") XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {")
308 g.P("return ", messageInfoVar, ".Marshal(b, m, deterministic)")
309 g.P("}")
310 // XXX_Merge
311 g.P("func (m *", message.GoIdent, ") XXX_Merge(src proto.Message) {")
312 g.P(messageInfoVar, ".Merge(m, src)")
313 g.P("}")
314 // XXX_Size
315 g.P("func (m *", message.GoIdent, ") XXX_Size() int {")
316 g.P("return ", messageInfoVar, ".Size(m)")
317 g.P("}")
318 // XXX_DiscardUnknown
319 g.P("func (m *", message.GoIdent, ") XXX_DiscardUnknown() {")
320 g.P(messageInfoVar, ".DiscardUnknown(m)")
321 g.P("}")
322 g.P()
323 g.P("var ", messageInfoVar, " ", protogen.GoIdent{
324 GoImportPath: protoPackage,
325 GoName: "InternalMessageInfo",
326 })
327 g.P()
328
Damien Neilebc699d2018-09-13 08:50:13 -0700329 // Constants and vars holding the default values of fields.
330 for _, field := range message.Fields {
331 if !field.Desc.HasDefault() {
332 continue
333 }
334 defVarName := "Default_" + message.GoIdent.GoName + "_" + field.GoIdent.GoName
335 def := field.Desc.Default()
336 switch field.Desc.Kind() {
337 case protoreflect.StringKind:
338 g.P("const ", defVarName, " string = ", strconv.Quote(def.String()))
339 case protoreflect.BytesKind:
340 g.P("var ", defVarName, " []byte = []byte(", strconv.Quote(string(def.Bytes())), ")")
341 case protoreflect.EnumKind:
342 enum := field.EnumType
343 evalue := enum.Values[enum.Desc.Values().ByNumber(def.Enum()).Index()]
344 g.P("const ", defVarName, " ", field.EnumType.GoIdent, " = ", evalue.GoIdent)
345 case protoreflect.FloatKind, protoreflect.DoubleKind:
346 // Floating point numbers need extra handling for -Inf/Inf/NaN.
347 f := field.Desc.Default().Float()
348 goType := "float64"
349 if field.Desc.Kind() == protoreflect.FloatKind {
350 goType = "float32"
351 }
352 // funcCall returns a call to a function in the math package,
353 // possibly converting the result to float32.
354 funcCall := func(fn, param string) string {
355 s := g.QualifiedGoIdent(protogen.GoIdent{
356 GoImportPath: "math",
357 GoName: fn,
358 }) + param
359 if goType != "float64" {
360 s = goType + "(" + s + ")"
361 }
362 return s
363 }
364 switch {
365 case math.IsInf(f, -1):
366 g.P("var ", defVarName, " ", goType, " = ", funcCall("Inf", "(-1)"))
367 case math.IsInf(f, 1):
368 g.P("var ", defVarName, " ", goType, " = ", funcCall("Inf", "(1)"))
369 case math.IsNaN(f):
370 g.P("var ", defVarName, " ", goType, " = ", funcCall("NaN", "()"))
371 default:
372 g.P("const ", defVarName, " ", goType, " = ", f)
373 }
374 default:
Damien Neil77f82fe2018-09-13 10:59:17 -0700375 goType, _ := fieldGoType(g, field)
Damien Neilebc699d2018-09-13 08:50:13 -0700376 g.P("const ", defVarName, " ", goType, " = ", def.Interface())
377 }
378 }
379 g.P()
380
Damien Neil77f82fe2018-09-13 10:59:17 -0700381 // Getters.
382 for _, field := range message.Fields {
383 goType, pointer := fieldGoType(g, field)
384 defaultValue := fieldDefaultValue(g, message, field)
385 g.P("func (m *", message.GoIdent, ") Get", field.GoIdent, "() ", goType, " {")
386 if field.Desc.Syntax() == protoreflect.Proto3 || defaultValue == "nil" {
387 g.P("if m != nil {")
388 } else {
389 g.P("if m != nil && m.", field.GoIdent, " != nil {")
390 }
391 star := ""
392 if pointer {
393 star = "*"
394 }
395 g.P("return ", star, " m.", field.GoIdent)
396 g.P("}")
397 g.P("return ", defaultValue)
398 g.P("}")
399 g.P()
400 }
Damien Neila1c6abc2018-09-12 13:36:34 -0700401
Damien Neilcab8dfe2018-09-06 14:51:28 -0700402 for _, nested := range message.Messages {
403 genMessage(gen, g, f, nested)
Damien Neilc7d07d92018-08-22 13:46:02 -0700404 }
405}
Damien Neilcab8dfe2018-09-06 14:51:28 -0700406
Damien Neil77f82fe2018-09-13 10:59:17 -0700407// fieldGoType returns the Go type used for a field.
408//
409// If it returns pointer=true, the struct field is a pointer to the type.
410func fieldGoType(g *protogen.GeneratedFile, field *protogen.Field) (goType string, pointer bool) {
Damien Neil658051b2018-09-10 12:26:21 -0700411 // TODO: map types
Damien Neil77f82fe2018-09-13 10:59:17 -0700412 pointer = true
Damien Neil658051b2018-09-10 12:26:21 -0700413 switch field.Desc.Kind() {
414 case protoreflect.BoolKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700415 goType = "bool"
Damien Neil658051b2018-09-10 12:26:21 -0700416 case protoreflect.EnumKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700417 goType = g.QualifiedGoIdent(field.EnumType.GoIdent)
Damien Neil658051b2018-09-10 12:26:21 -0700418 case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700419 goType = "int32"
Damien Neil658051b2018-09-10 12:26:21 -0700420 case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700421 goType = "uint32"
Damien Neil658051b2018-09-10 12:26:21 -0700422 case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700423 goType = "int64"
Damien Neil658051b2018-09-10 12:26:21 -0700424 case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700425 goType = "uint64"
Damien Neil658051b2018-09-10 12:26:21 -0700426 case protoreflect.FloatKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700427 goType = "float32"
Damien Neil658051b2018-09-10 12:26:21 -0700428 case protoreflect.DoubleKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700429 goType = "float64"
Damien Neil658051b2018-09-10 12:26:21 -0700430 case protoreflect.StringKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700431 goType = "string"
Damien Neil658051b2018-09-10 12:26:21 -0700432 case protoreflect.BytesKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700433 goType = "[]byte"
434 pointer = false
Damien Neil658051b2018-09-10 12:26:21 -0700435 case protoreflect.MessageKind, protoreflect.GroupKind:
Damien Neil77f82fe2018-09-13 10:59:17 -0700436 goType = "*" + g.QualifiedGoIdent(field.MessageType.GoIdent)
437 pointer = false
Damien Neil658051b2018-09-10 12:26:21 -0700438 }
439 if field.Desc.Cardinality() == protoreflect.Repeated {
Damien Neil77f82fe2018-09-13 10:59:17 -0700440 goType = "[]" + goType
441 pointer = false
Damien Neil658051b2018-09-10 12:26:21 -0700442 }
443 if field.Desc.Syntax() == protoreflect.Proto3 {
Damien Neil77f82fe2018-09-13 10:59:17 -0700444 pointer = false
Damien Neil658051b2018-09-10 12:26:21 -0700445 }
Damien Neil77f82fe2018-09-13 10:59:17 -0700446 return goType, pointer
Damien Neil658051b2018-09-10 12:26:21 -0700447}
448
449func fieldProtobufTag(field *protogen.Field) string {
450 var tag []string
451 // wire type
452 tag = append(tag, wireTypes[field.Desc.Kind()])
453 // field number
454 tag = append(tag, strconv.Itoa(int(field.Desc.Number())))
455 // cardinality
456 switch field.Desc.Cardinality() {
457 case protoreflect.Optional:
458 tag = append(tag, "opt")
459 case protoreflect.Required:
460 tag = append(tag, "req")
461 case protoreflect.Repeated:
462 tag = append(tag, "rep")
463 }
Damien Neil658051b2018-09-10 12:26:21 -0700464 // TODO: packed
465 // name
466 name := string(field.Desc.Name())
467 if field.Desc.Kind() == protoreflect.GroupKind {
468 // The name of the FieldDescriptor for a group field is
469 // lowercased. To find the original capitalization, we
470 // look in the field's MessageType.
471 name = string(field.MessageType.Desc.Name())
472 }
473 tag = append(tag, "name="+name)
474 // JSON name
475 if jsonName := field.Desc.JSONName(); jsonName != "" && jsonName != name {
476 tag = append(tag, "json="+jsonName)
477 }
478 // proto3
479 if field.Desc.Syntax() == protoreflect.Proto3 {
480 tag = append(tag, "proto3")
481 }
482 // enum
483 if field.Desc.Kind() == protoreflect.EnumKind {
484 tag = append(tag, "enum="+enumRegistryName(field.EnumType))
485 }
486 // oneof
487 if field.Desc.OneofType() != nil {
488 tag = append(tag, "oneof")
489 }
Damien Neilebc699d2018-09-13 08:50:13 -0700490 // default value
491 // This must appear last in the tag, since commas in strings aren't escaped.
492 if field.Desc.HasDefault() {
493 var def string
494 switch field.Desc.Kind() {
495 case protoreflect.BoolKind:
496 if field.Desc.Default().Bool() {
497 def = "1"
498 } else {
499 def = "0"
500 }
501 case protoreflect.BytesKind:
502 def = string(field.Desc.Default().Bytes())
503 case protoreflect.FloatKind, protoreflect.DoubleKind:
504 f := field.Desc.Default().Float()
505 switch {
506 case math.IsInf(f, -1):
507 def = "-inf"
508 case math.IsInf(f, 1):
509 def = "inf"
510 case math.IsNaN(f):
511 def = "nan"
512 default:
513 def = fmt.Sprint(f)
514 }
515 default:
516 def = fmt.Sprint(field.Desc.Default().Interface())
517 }
518 tag = append(tag, "def="+def)
519 }
Damien Neil658051b2018-09-10 12:26:21 -0700520 return strings.Join(tag, ",")
521}
522
Damien Neil77f82fe2018-09-13 10:59:17 -0700523func fieldDefaultValue(g *protogen.GeneratedFile, message *protogen.Message, field *protogen.Field) string {
524 if field.Desc.Cardinality() == protoreflect.Repeated {
525 return "nil"
526 }
527 if field.Desc.HasDefault() {
528 defVarName := "Default_" + message.GoIdent.GoName + "_" + field.GoIdent.GoName
529 if field.Desc.Kind() == protoreflect.BytesKind {
530 return "append([]byte(nil), " + defVarName + "...)"
531 }
532 return defVarName
533 }
534 switch field.Desc.Kind() {
535 case protoreflect.BoolKind:
536 return "false"
537 case protoreflect.StringKind:
538 return `""`
539 case protoreflect.MessageKind, protoreflect.GroupKind, protoreflect.BytesKind:
540 return "nil"
541 case protoreflect.EnumKind:
542 return g.QualifiedGoIdent(field.EnumType.Values[0].GoIdent)
543 default:
544 return "0"
545 }
546}
547
Damien Neil658051b2018-09-10 12:26:21 -0700548var wireTypes = map[protoreflect.Kind]string{
549 protoreflect.BoolKind: "varint",
550 protoreflect.EnumKind: "varint",
551 protoreflect.Int32Kind: "varint",
552 protoreflect.Sint32Kind: "zigzag32",
553 protoreflect.Uint32Kind: "varint",
554 protoreflect.Int64Kind: "varint",
555 protoreflect.Sint64Kind: "zigzag64",
556 protoreflect.Uint64Kind: "varint",
557 protoreflect.Sfixed32Kind: "fixed32",
558 protoreflect.Fixed32Kind: "fixed32",
559 protoreflect.FloatKind: "fixed32",
560 protoreflect.Sfixed64Kind: "fixed64",
561 protoreflect.Fixed64Kind: "fixed64",
562 protoreflect.DoubleKind: "fixed64",
563 protoreflect.StringKind: "bytes",
564 protoreflect.BytesKind: "bytes",
565 protoreflect.MessageKind: "bytes",
566 protoreflect.GroupKind: "group",
567}
568
569func fieldJSONTag(field *protogen.Field) string {
570 return string(field.Desc.Name()) + ",omitempty"
571}
572
Damien Neilcab8dfe2018-09-06 14:51:28 -0700573func genComment(g *protogen.GeneratedFile, f *File, path []int32) {
574 for _, loc := range f.locationMap[pathKey(path)] {
575 if loc.LeadingComments == nil {
576 continue
577 }
578 for _, line := range strings.Split(strings.TrimSuffix(loc.GetLeadingComments(), "\n"), "\n") {
579 g.P("//", line)
580 }
581 return
582 }
583}
584
585// pathKey converts a location path to a string suitable for use as a map key.
586func pathKey(path []int32) string {
587 var buf []byte
588 for i, x := range path {
589 if i != 0 {
590 buf = append(buf, ',')
591 }
592 buf = strconv.AppendInt(buf, int64(x), 10)
593 }
594 return string(buf)
595}
Damien Neil46abb572018-09-07 12:45:37 -0700596
597func genWellKnownType(g *protogen.GeneratedFile, ident protogen.GoIdent, desc protoreflect.Descriptor) {
598 if wellKnownTypes[desc.FullName()] {
599 g.P("func (", ident, `) XXX_WellKnownType() string { return "`, desc.Name(), `" }`)
600 g.P()
601 }
602}
603
604// Names of messages and enums for which we will generate XXX_WellKnownType methods.
605var wellKnownTypes = map[protoreflect.FullName]bool{
606 "google.protobuf.NullValue": true,
607}