forked from lyft/protoc-gen-star
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplugin.go
More file actions
207 lines (172 loc) · 6.91 KB
/
Copy pathplugin.go
File metadata and controls
207 lines (172 loc) · 6.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
package pgs
import (
"io"
"log"
"os"
"strconv"
"text/template"
"github.com/golang/protobuf/protoc-gen-go/generator"
)
// Plugin describes an official protoc-gen-go plugin that will also be passed a
// pre-configured debugger for use. The plugin must be registered via
// Generator.RegisterPlugin for it to be properly initialized.
type Plugin interface {
generator.Plugin
// InitContext is called before the Plugin's Init method and is passed a
// pre-configured BuildContext instance.
InitContext(c BuildContext)
}
// Template describes a template used to render content. Both the text/template
// and html/template packages satisfy this interface.
type Template interface {
Name() string
Execute(wr io.Writer, data interface{}) error
}
// PluginBase provides utility methods and a base implementation for the
// protoc-gen-go sub-plugin workflow.
type PluginBase struct {
BuildContext
Generator ProtocGenGo
seenImports map[string]string
Imports map[string]string
buildTargets map[string]struct{}
}
// Name satisfies the protoc-gen-go plugin interface, however this method will
// fail and must be overridden by a parent struct. PluginBase should be used as
// an anonymously embedded field of an actual Plugin implementation. The only
// methods that need to be overridden are Name and Generate.
func (p *PluginBase) Name() string {
p.Fail("Name method is not implemented for this plugin")
return "unimplemented"
}
// InitContext populates this Plugin with the BuildContext from the parent
// Generator, allowing for easy debug logging, and error checking. This
// method is called prior to Init for plugins registered directly with the
// generator.
func (p *PluginBase) InitContext(c BuildContext) { p.BuildContext = c }
// Init sets up the plugin with a reference to the generator. This method
// satisfies the Init method for the protoc-gen-go plugin.
func (p *PluginBase) Init(g *generator.Generator) {
if p.BuildContext == nil {
d := initDebugger(
&Generator{pgg: Wrap(g)},
log.New(os.Stderr, "", 0)).Push("unregistered plugin")
p.BuildContext = Context(d, Parameters{}, ".")
}
p.Debug("Initializing")
p.Generator = Wrap(g)
}
// Generate satisfies the protoc-gen-go plugin interface, however this method
// will fail and must be overridden by a parent struct.
func (p *PluginBase) Generate(file *generator.FileDescriptor) {
p.Fail("Generate method is not implemented for this plugin")
}
var importsTmpl = template.Must(template.New("imports").Parse(`import({{ range $path, $pkg := . }}
{{ $pkg }} "{{ $path }}"
{{- end }}
)`))
// GenerateImports adds the imported packages to the top of the file to be
// generated, using the packages included in b.Imports. This method satisfies
// the GenerateImports method for the protoc-gen-go plugin, and is called after
// Generate for each particular FileDescriptor. The added Imports are cleared
// after this call is completed.
func (p *PluginBase) GenerateImports(file *generator.FileDescriptor) {
if p == nil || len(p.Imports) == 0 {
return
}
p.T(importsTmpl, p.Imports)
p.Imports = nil
}
// AddImport safely registers an import at path with the target pkg name. The
// returned uniquePkg should be used within the code to avoid naming collisions.
// If referencing an entity from a protocol buffer, provide its FileDescriptor
// fd, otherwise leave it as nil. The Imports are cleared after GenerateImports
// is called.
func (p *PluginBase) AddImport(pkg, path string, fd *generator.FileDescriptor) (uniquePkg string) {
if p.seenImports == nil {
p.seenImports = map[string]string{}
}
if p.Imports == nil {
p.Imports = map[string]string{}
}
if existing, ok := p.seenImports[path]; ok {
p.Imports[path] = existing
return existing
}
uniquePkg = generator.RegisterUniquePackageName(pkg, fd)
p.seenImports[path] = uniquePkg
p.Imports[path] = uniquePkg
return
}
// P wraps the generator's P method, printing the arguments to the generated
// output. It handles strings and int32s, plus handling indirections because
// they may be *string, etc.
func (p *PluginBase) P(args ...interface{}) { p.Generator.P(args...) }
// In wraps the generator's In command, indenting the output by one tab.
func (p *PluginBase) In() { p.Generator.In() }
// Out wraps the generator's Out command, outdenting the output by one tab.
func (p *PluginBase) Out() { p.Generator.Out() }
// C behaves like the P method, but prints a comment block.
// The wrap parameter indicates what width to wrap the comment at.
func (p *PluginBase) C(wrap int, args ...interface{}) {
s := commentScanner(wrap, args...)
for s.Scan() {
p.P("// ", s.Text())
}
}
// C80 curries the C method with the traditional width of 80 characters,
// calling p.C(80, args...).
func (p *PluginBase) C80(args ...interface{}) { p.C(80, args...) }
// T renders tpl into the target file, using data. The plugin is terminated if
// there is an error executing the template. Both text/template and
// html/template packages are compatible with this method.
func (p *PluginBase) T(tpl Template, data interface{}) {
p.CheckErr(
tpl.Execute(p.Generator, data),
"unable to render template: ",
strconv.Quote(tpl.Name()))
}
// Push adds a prefix to the plugin's BuildContext. Pop should be called when
// that context is complete.
func (p *PluginBase) Push(prefix string) BuildContext {
p.BuildContext = p.BuildContext.Push(prefix)
return p.BuildContext
}
// PushDir changes the OutputPath of the plugin's BuildContext. Pop (or PopDir)
// should be called when that context is complete.
func (p *PluginBase) PushDir(dir string) BuildContext {
p.BuildContext = p.BuildContext.PushDir(dir)
return p.BuildContext
}
// Pop removes the last push from the plugin's BuildContext. This method should
// only be called after a paired Push or PushDir.
func (p *PluginBase) Pop() BuildContext {
p.BuildContext = p.BuildContext.Pop()
return p.BuildContext
}
// PopDir removes the last PushDir from the plugin's BuildContext. This method
// should only be called after a paired PushDir.
func (p *PluginBase) PopDir() BuildContext {
p.BuildContext = p.BuildContext.PopDir()
return p.BuildContext
}
// BuildTarget returns true if the specified proto filename was an input to
// protoc. This method is useful to determine if generation logic should be
// executed against it or if it is only loaded as a dependency. This method
// expects the value returned by generator.FileDescriptor.GetName or
// descriptor.FileDescriptorProto.GetName methods.
func (p *PluginBase) BuildTarget(proto string) bool {
if p.buildTargets == nil {
files := p.Generator.request().GetFileToGenerate()
p.buildTargets = make(map[string]struct{}, len(files))
for _, f := range files {
p.buildTargets[f] = struct{}{}
}
}
_, ok := p.buildTargets[proto]
return ok
}
// BuildTargetObj returns whether or not a generator.Object was loaded from a
// BuildTarget file.
func (p *PluginBase) BuildTargetObj(o generator.Object) bool { return p.BuildTarget(o.File().GetName()) }
var _ Plugin = (*PluginBase)(nil)