1 package descriptor
2
3 import (
4 "fmt"
5 "path"
6 "path/filepath"
7 "strings"
8
9 "github.com/golang/glog"
10 descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
11 plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
12 "google.golang.org/genproto/googleapis/api/annotations"
13 )
14
15
16 type Registry struct {
17
18 msgs map[string]*Message
19
20
21 enums map[string]*Enum
22
23
24 files map[string]*File
25
26
27 prefix string
28
29
30 importPath string
31
32
33 pkgMap map[string]string
34
35
36 pkgAliases map[string]string
37
38
39 allowDeleteBody bool
40
41
42 externalHTTPRules map[string][]*annotations.HttpRule
43
44
45 allowMerge bool
46
47
48 mergeFileName string
49
50
51 allowRepeatedFieldsInBody bool
52
53
54
55 includePackageInTags bool
56
57
58 repeatedPathParamSeparator repeatedFieldSeparator
59
60
61
62
63 useJSONNamesForFields bool
64
65
66
67
68
69 useFQNForSwaggerName bool
70
71
72
73 allowColonFinalSegments bool
74
75
76
77 useGoTemplate bool
78
79
80 enumsAsInts bool
81
82
83
84 disableDefaultErrors bool
85
86
87
88 simpleOperationIDs bool
89
90
91
92 warnOnUnboundMethods bool
93
94
95
96 generateUnboundMethods bool
97
98
99 omitPackageDoc bool
100 }
101
102 type repeatedFieldSeparator struct {
103 name string
104 sep rune
105 }
106
107
108 func NewRegistry() *Registry {
109 return &Registry{
110 msgs: make(map[string]*Message),
111 enums: make(map[string]*Enum),
112 files: make(map[string]*File),
113 pkgMap: make(map[string]string),
114 pkgAliases: make(map[string]string),
115 externalHTTPRules: make(map[string][]*annotations.HttpRule),
116 repeatedPathParamSeparator: repeatedFieldSeparator{
117 name: "csv",
118 sep: ',',
119 },
120 }
121 }
122
123
124 func (r *Registry) Load(req *plugin.CodeGeneratorRequest) error {
125 for _, file := range req.GetProtoFile() {
126 r.loadFile(file)
127 }
128
129 var targetPkg string
130 for _, name := range req.FileToGenerate {
131 target := r.files[name]
132 if target == nil {
133 return fmt.Errorf("no such file: %s", name)
134 }
135 name := r.packageIdentityName(target.FileDescriptorProto)
136 if targetPkg == "" {
137 targetPkg = name
138 } else {
139 if targetPkg != name {
140 return fmt.Errorf("inconsistent package names: %s %s", targetPkg, name)
141 }
142 }
143
144 if err := r.loadServices(target); err != nil {
145 return err
146 }
147 }
148 return nil
149 }
150
151
152
153
154 func (r *Registry) loadFile(file *descriptor.FileDescriptorProto) {
155 pkg := GoPackage{
156 Path: r.goPackagePath(file),
157 Name: r.defaultGoPackageName(file),
158 }
159 if err := r.ReserveGoPackageAlias(pkg.Name, pkg.Path); err != nil {
160 for i := 0; ; i++ {
161 alias := fmt.Sprintf("%s_%d", pkg.Name, i)
162 if err := r.ReserveGoPackageAlias(alias, pkg.Path); err == nil {
163 pkg.Alias = alias
164 break
165 }
166 }
167 }
168 f := &File{
169 FileDescriptorProto: file,
170 GoPkg: pkg,
171 }
172
173 r.files[file.GetName()] = f
174 r.registerMsg(f, nil, file.GetMessageType())
175 r.registerEnum(f, nil, file.GetEnumType())
176 }
177
178 func (r *Registry) registerMsg(file *File, outerPath []string, msgs []*descriptor.DescriptorProto) {
179 for i, md := range msgs {
180 m := &Message{
181 File: file,
182 Outers: outerPath,
183 DescriptorProto: md,
184 Index: i,
185 }
186 for _, fd := range md.GetField() {
187 m.Fields = append(m.Fields, &Field{
188 Message: m,
189 FieldDescriptorProto: fd,
190 })
191 }
192 file.Messages = append(file.Messages, m)
193 r.msgs[m.FQMN()] = m
194 glog.V(1).Infof("register name: %s", m.FQMN())
195
196 var outers []string
197 outers = append(outers, outerPath...)
198 outers = append(outers, m.GetName())
199 r.registerMsg(file, outers, m.GetNestedType())
200 r.registerEnum(file, outers, m.GetEnumType())
201 }
202 }
203
204 func (r *Registry) registerEnum(file *File, outerPath []string, enums []*descriptor.EnumDescriptorProto) {
205 for i, ed := range enums {
206 e := &Enum{
207 File: file,
208 Outers: outerPath,
209 EnumDescriptorProto: ed,
210 Index: i,
211 }
212 file.Enums = append(file.Enums, e)
213 r.enums[e.FQEN()] = e
214 glog.V(1).Infof("register enum name: %s", e.FQEN())
215 }
216 }
217
218
219
220 func (r *Registry) LookupMsg(location, name string) (*Message, error) {
221 glog.V(1).Infof("lookup %s from %s", name, location)
222 if strings.HasPrefix(name, ".") {
223 m, ok := r.msgs[name]
224 if !ok {
225 return nil, fmt.Errorf("no message found: %s", name)
226 }
227 return m, nil
228 }
229
230 if !strings.HasPrefix(location, ".") {
231 location = fmt.Sprintf(".%s", location)
232 }
233 components := strings.Split(location, ".")
234 for len(components) > 0 {
235 fqmn := strings.Join(append(components, name), ".")
236 if m, ok := r.msgs[fqmn]; ok {
237 return m, nil
238 }
239 components = components[:len(components)-1]
240 }
241 return nil, fmt.Errorf("no message found: %s", name)
242 }
243
244
245
246 func (r *Registry) LookupEnum(location, name string) (*Enum, error) {
247 glog.V(1).Infof("lookup enum %s from %s", name, location)
248 if strings.HasPrefix(name, ".") {
249 e, ok := r.enums[name]
250 if !ok {
251 return nil, fmt.Errorf("no enum found: %s", name)
252 }
253 return e, nil
254 }
255
256 if !strings.HasPrefix(location, ".") {
257 location = fmt.Sprintf(".%s", location)
258 }
259 components := strings.Split(location, ".")
260 for len(components) > 0 {
261 fqen := strings.Join(append(components, name), ".")
262 if e, ok := r.enums[fqen]; ok {
263 return e, nil
264 }
265 components = components[:len(components)-1]
266 }
267 return nil, fmt.Errorf("no enum found: %s", name)
268 }
269
270
271 func (r *Registry) LookupFile(name string) (*File, error) {
272 f, ok := r.files[name]
273 if !ok {
274 return nil, fmt.Errorf("no such file given: %s", name)
275 }
276 return f, nil
277 }
278
279
280 func (r *Registry) LookupExternalHTTPRules(qualifiedMethodName string) []*annotations.HttpRule {
281 return r.externalHTTPRules[qualifiedMethodName]
282 }
283
284
285 func (r *Registry) AddExternalHTTPRule(qualifiedMethodName string, rule *annotations.HttpRule) {
286 r.externalHTTPRules[qualifiedMethodName] = append(r.externalHTTPRules[qualifiedMethodName], rule)
287 }
288
289
290
291 func (r *Registry) UnboundExternalHTTPRules() []string {
292 allServiceMethods := make(map[string]struct{})
293 for _, f := range r.files {
294 for _, s := range f.GetService() {
295 svc := &Service{File: f, ServiceDescriptorProto: s}
296 for _, m := range s.GetMethod() {
297 method := &Method{Service: svc, MethodDescriptorProto: m}
298 allServiceMethods[method.FQMN()] = struct{}{}
299 }
300 }
301 }
302
303 var missingMethods []string
304 for httpRuleMethod := range r.externalHTTPRules {
305 if _, ok := allServiceMethods[httpRuleMethod]; !ok {
306 missingMethods = append(missingMethods, httpRuleMethod)
307 }
308 }
309 return missingMethods
310 }
311
312
313 func (r *Registry) AddPkgMap(file, protoPkg string) {
314 r.pkgMap[file] = protoPkg
315 }
316
317
318 func (r *Registry) SetPrefix(prefix string) {
319 r.prefix = prefix
320 }
321
322
323
324
325 func (r *Registry) SetImportPath(importPath string) {
326 r.importPath = importPath
327 }
328
329
330
331
332
333 func (r *Registry) ReserveGoPackageAlias(alias, pkgpath string) error {
334 if taken, ok := r.pkgAliases[alias]; ok {
335 if taken == pkgpath {
336 return nil
337 }
338 return fmt.Errorf("package name %s is already taken. Use another alias", alias)
339 }
340 r.pkgAliases[alias] = pkgpath
341 return nil
342 }
343
344
345
346
347 func (r *Registry) goPackagePath(f *descriptor.FileDescriptorProto) string {
348 name := f.GetName()
349 if pkg, ok := r.pkgMap[name]; ok {
350 return path.Join(r.prefix, pkg)
351 }
352
353 gopkg := f.Options.GetGoPackage()
354 idx := strings.LastIndex(gopkg, "/")
355 if idx >= 0 {
356 if sc := strings.LastIndex(gopkg, ";"); sc > 0 {
357 gopkg = gopkg[:sc+1-1]
358 }
359 return gopkg
360 }
361
362 return path.Join(r.prefix, path.Dir(name))
363 }
364
365
366 func (r *Registry) GetAllFQMNs() []string {
367 var keys []string
368 for k := range r.msgs {
369 keys = append(keys, k)
370 }
371 return keys
372 }
373
374
375 func (r *Registry) GetAllFQENs() []string {
376 var keys []string
377 for k := range r.enums {
378 keys = append(keys, k)
379 }
380 return keys
381 }
382
383
384
385 func (r *Registry) SetAllowDeleteBody(allow bool) {
386 r.allowDeleteBody = allow
387 }
388
389
390 func (r *Registry) SetAllowMerge(allow bool) {
391 r.allowMerge = allow
392 }
393
394
395 func (r *Registry) IsAllowMerge() bool {
396 return r.allowMerge
397 }
398
399
400 func (r *Registry) SetMergeFileName(mergeFileName string) {
401 r.mergeFileName = mergeFileName
402 }
403
404
405
406 func (r *Registry) SetAllowRepeatedFieldsInBody(allow bool) {
407 r.allowRepeatedFieldsInBody = allow
408 }
409
410
411
412 func (r *Registry) IsAllowRepeatedFieldsInBody() bool {
413 return r.allowRepeatedFieldsInBody
414 }
415
416
417
418 func (r *Registry) SetIncludePackageInTags(allow bool) {
419 r.includePackageInTags = allow
420 }
421
422
423
424 func (r *Registry) IsIncludePackageInTags() bool {
425 return r.includePackageInTags
426 }
427
428
429
430 func (r *Registry) GetRepeatedPathParamSeparator() rune {
431 return r.repeatedPathParamSeparator.sep
432 }
433
434
435
436 func (r *Registry) GetRepeatedPathParamSeparatorName() string {
437 return r.repeatedPathParamSeparator.name
438 }
439
440
441
442 func (r *Registry) SetRepeatedPathParamSeparator(name string) error {
443 var sep rune
444 switch name {
445 case "csv":
446 sep = ','
447 case "pipes":
448 sep = '|'
449 case "ssv":
450 sep = ' '
451 case "tsv":
452 sep = '\t'
453 default:
454 return fmt.Errorf("unknown repeated path parameter separator: %s", name)
455 }
456 r.repeatedPathParamSeparator = repeatedFieldSeparator{
457 name: name,
458 sep: sep,
459 }
460 return nil
461 }
462
463
464 func (r *Registry) SetUseJSONNamesForFields(use bool) {
465 r.useJSONNamesForFields = use
466 }
467
468
469 func (r *Registry) GetUseJSONNamesForFields() bool {
470 return r.useJSONNamesForFields
471 }
472
473
474 func (r *Registry) SetUseFQNForSwaggerName(use bool) {
475 r.useFQNForSwaggerName = use
476 }
477
478
479 func (r *Registry) GetAllowColonFinalSegments() bool {
480 return r.allowColonFinalSegments
481 }
482
483
484 func (r *Registry) SetAllowColonFinalSegments(use bool) {
485 r.allowColonFinalSegments = use
486 }
487
488
489 func (r *Registry) GetUseFQNForSwaggerName() bool {
490 return r.useFQNForSwaggerName
491 }
492
493
494 func (r *Registry) GetMergeFileName() string {
495 return r.mergeFileName
496 }
497
498
499 func (r *Registry) SetUseGoTemplate(use bool) {
500 r.useGoTemplate = use
501 }
502
503
504 func (r *Registry) GetUseGoTemplate() bool {
505 return r.useGoTemplate
506 }
507
508
509 func (r *Registry) SetEnumsAsInts(enumsAsInts bool) {
510 r.enumsAsInts = enumsAsInts
511 }
512
513
514 func (r *Registry) GetEnumsAsInts() bool {
515 return r.enumsAsInts
516 }
517
518
519 func (r *Registry) SetDisableDefaultErrors(use bool) {
520 r.disableDefaultErrors = use
521 }
522
523
524 func (r *Registry) GetDisableDefaultErrors() bool {
525 return r.disableDefaultErrors
526 }
527
528
529 func (r *Registry) SetSimpleOperationIDs(use bool) {
530 r.simpleOperationIDs = use
531 }
532
533
534 func (r *Registry) GetSimpleOperationIDs() bool {
535 return r.simpleOperationIDs
536 }
537
538
539 func (r *Registry) SetWarnOnUnboundMethods(warn bool) {
540 r.warnOnUnboundMethods = warn
541 }
542
543
544 func (r *Registry) SetGenerateUnboundMethods(generate bool) {
545 r.generateUnboundMethods = generate
546 }
547
548
549 func (r *Registry) SetOmitPackageDoc(omit bool) {
550 r.omitPackageDoc = omit
551 }
552
553
554 func (r *Registry) GetOmitPackageDoc() bool {
555 return r.omitPackageDoc
556 }
557
558
559
560 func sanitizePackageName(pkgName string) string {
561 pkgName = strings.Replace(pkgName, ".", "_", -1)
562 pkgName = strings.Replace(pkgName, "-", "_", -1)
563 return pkgName
564 }
565
566
567
568 func (r *Registry) defaultGoPackageName(f *descriptor.FileDescriptorProto) string {
569 name := r.packageIdentityName(f)
570 return sanitizePackageName(name)
571 }
572
573
574
575
576 func (r *Registry) packageIdentityName(f *descriptor.FileDescriptorProto) string {
577 if f.Options != nil && f.Options.GoPackage != nil {
578 gopkg := f.Options.GetGoPackage()
579 idx := strings.LastIndex(gopkg, "/")
580 if idx < 0 {
581 gopkg = gopkg[idx+1:]
582 }
583
584 gopkg = gopkg[idx+1:]
585
586
587 sc := strings.IndexByte(gopkg, ';')
588 if sc < 0 {
589 return sanitizePackageName(gopkg)
590
591 }
592 return sanitizePackageName(gopkg[sc+1:])
593 }
594 if p := r.importPath; len(p) != 0 {
595 if i := strings.LastIndex(p, "/"); i >= 0 {
596 p = p[i+1:]
597 }
598 return p
599 }
600
601 if f.Package == nil {
602 base := filepath.Base(f.GetName())
603 ext := filepath.Ext(base)
604 return strings.TrimSuffix(base, ext)
605 }
606 return f.GetPackage()
607 }
608
View as plain text