-
Notifications
You must be signed in to change notification settings - Fork 77
/
module.go
265 lines (237 loc) · 9.19 KB
/
module.go
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
package pgs
import "os"
// Module describes the interface for a domain-specific code generation module
// that can be registered with the PG* generator.
type Module interface {
// The Name of the Module, used when establishing the build context and used
// as the base prefix for all debugger output.
Name() string
// InitContext is called on a Module with a pre-configured BuildContext that
// should be stored and used by the Module.
InitContext(c BuildContext)
// Execute is called on the module with the target Files as well as all
// loaded Packages from the gatherer. The module should return a slice of
// Artifacts that it would like to be generated.
Execute(targets map[string]File, packages map[string]Package) []Artifact
}
// ModuleBase provides utility methods and a base implementation for a
// protoc-gen-star Module. ModuleBase should be used as an anonymously embedded
// field of an actual Module implementation. The only methods that need to be
// overridden are Name and Execute.
//
// type MyModule {
// *pgs.ModuleBase
// }
//
// func InitMyModule() *MyModule { return &MyModule{ &pgs.ModuleBase{} } }
//
// func (m *MyModule) Name() string { return "MyModule" }
//
// func (m *MyModule) Execute(...) []pgs.Artifact { ... }
type ModuleBase struct {
BuildContext
artifacts []Artifact
}
// InitContext populates this Module with the BuildContext from the parent
// Generator, allowing for easy debug logging, error checking, and output path
// management. This method is called prior to Execute for modules registered
// with the generator.
func (m *ModuleBase) InitContext(c BuildContext) {
m.BuildContext = c
m.Debug("initializing")
}
// Name satisfies the Module interface, however this method will panic and must
// be overridden by a parent struct.
func (m *ModuleBase) Name() string {
panic("Name method is not implemented for this module")
}
// Execute satisfies the Module interface, however this method will fail and
// must be overridden by a parent struct.
func (m *ModuleBase) Execute(targets map[string]File, packages map[string]Package) []Artifact {
m.Fail("Execute method is not implemented for this module")
return m.Artifacts()
}
// Push adds a prefix to the Module's BuildContext. Pop should be called when
// the context is complete.
func (m *ModuleBase) Push(prefix string) BuildContext {
m.BuildContext = m.BuildContext.Push(prefix)
return m
}
// PushDir changes the OutputPath of the Module's BuildContext. Pop (or PopDir)
// should be called when that context is complete.
func (m *ModuleBase) PushDir(dir string) BuildContext {
m.BuildContext = m.BuildContext.PushDir(dir)
return m
}
// Pop removes the last push from the Module's BuildContext. This method should
// only be called after a paired Push or PushDir.
func (m *ModuleBase) Pop() BuildContext {
m.BuildContext = m.BuildContext.Pop()
return m
}
// PopDir removes the last PushDir from the Module's BuildContext. This method
// should only be called after a paired PushDir.
func (m *ModuleBase) PopDir() BuildContext {
m.BuildContext = m.BuildContext.PopDir()
return m
}
// Artifacts returns the slice of generation artifacts that have been captured
// by the Module. This method should/can be the return value of its Execute
// method. Subsequent calls will return a nil slice until more artifacts are
// added.
func (m *ModuleBase) Artifacts() []Artifact {
out := m.artifacts
m.artifacts = nil
return out
}
// AddArtifact adds an Artifact to this Module's collection of generation
// artifacts. This method is available as a convenience but the other Add &
// Overwrite methods should be used preferentially.
func (m *ModuleBase) AddArtifact(a ...Artifact) { m.artifacts = append(m.artifacts, a...) }
// AddGeneratorFile adds a file with the provided name and contents to the code
// generation response payload to protoc. Name must be a path relative to and
// within the protoc-plugin's output destination, which may differ from the
// BuildContext's OutputPath value. If another Module or Plugin has added a
// file with the same name, protoc will produce an error.
func (m *ModuleBase) AddGeneratorFile(name, content string) {
m.AddArtifact(GeneratorFile{
Name: name,
Contents: content,
})
}
// OverwriteGeneratorFile behaves the same as AddGeneratorFile, however if a
// previously executed Module has created a file with the same name, it will be
// overwritten with this one.
func (m *ModuleBase) OverwriteGeneratorFile(name, content string) {
m.AddArtifact(GeneratorFile{
Name: name,
Contents: content,
Overwrite: true,
})
}
// AddGeneratorTemplateFile behaves the same as AddGeneratorFile, however the
// contents are rendered from the provided tpl and data.
func (m *ModuleBase) AddGeneratorTemplateFile(name string, tpl Template, data interface{}) {
m.AddArtifact(GeneratorTemplateFile{
Name: name,
TemplateArtifact: TemplateArtifact{
Template: tpl,
Data: data,
},
})
}
// OverwriteGeneratorTemplateFile behaves the same as OverwriteGeneratorFile,
// however the contents are rendered from the provided tpl and data.
func (m *ModuleBase) OverwriteGeneratorTemplateFile(name string, tpl Template, data interface{}) {
m.AddArtifact(GeneratorTemplateFile{
Name: name,
Overwrite: true,
TemplateArtifact: TemplateArtifact{
Template: tpl,
Data: data,
},
})
}
// AddGeneratorAppend attempts to append content to the specified file name.
// Name must be a path relative to and within the protoc-plugin's output
// destination, which may differ from the BuildContext's OutputPath value. If
// the file is not generated by this protoc-plugin, execution will fail.
func (m *ModuleBase) AddGeneratorAppend(name, content string) {
m.AddArtifact(GeneratorAppend{
FileName: name,
Contents: content,
})
}
// AddGeneratorTemplateAppend behaves the same as AddGeneratorAppend, however
// the contents are rendered from the provided tpl and data.
func (m *ModuleBase) AddGeneratorTemplateAppend(name string, tpl Template, data interface{}) {
m.AddArtifact(GeneratorTemplateAppend{
FileName: name,
TemplateArtifact: TemplateArtifact{
Template: tpl,
Data: data,
},
})
}
// AddGeneratorInjection attempts to inject content into the file with name at
// the specified insertion point. Name must be a path relative to and within
// the protoc-plugin's output destination, which may differ from the
// BuildContext's OutputPath value. The file does not need to be generated by
// this protoc-plugin but the generating plugin must be called first in the
// protoc execution.
//
// See: https://godoc.org/github.com/golang/protobuf/protoc-gen-go/plugin#CodeGeneratorResponse_File
func (m *ModuleBase) AddGeneratorInjection(name, point, content string) {
m.AddArtifact(GeneratorInjection{
FileName: name,
InsertionPoint: point,
Contents: content,
})
}
// AddGeneratorTemplateInjection behaves the same as AddGeneratorInjection,
// however the contents are rendered from the provided tpl and data.
func (m *ModuleBase) AddGeneratorTemplateInjection(name, point string, tpl Template, data interface{}) {
m.AddArtifact(GeneratorTemplateInjection{
FileName: name,
InsertionPoint: point,
TemplateArtifact: TemplateArtifact{
Template: tpl,
Data: data,
},
})
}
// AddCustomFile creates a file directly on the file system with the provided
// content and perms. Unlike AddGeneratorFile, this method does not use protoc
// to generate the file. If name is a relative path, it is related to the
// directory in which protoc was executed; name can also be an absolute path.
// If a file already exists with the specified name, the file will not be
// created and there will be no generation error.
func (m *ModuleBase) AddCustomFile(name, content string, perms os.FileMode) {
m.AddArtifact(CustomFile{
Name: name,
Contents: content,
Perms: perms,
})
}
// OverwriteCustomFile behaves the same as AddCustomFile, however if the file
// already exists, it will be overwritten with this one.
func (m *ModuleBase) OverwriteCustomFile(name, content string, perms os.FileMode) {
m.AddArtifact(CustomFile{
Name: name,
Contents: content,
Perms: perms,
Overwrite: true,
})
}
// AddCustomTemplateFile behaves the same as AddCustomFile, however the
// contents are rendered from the provided tpl and data.
func (m *ModuleBase) AddCustomTemplateFile(name string, tpl Template, data interface{}, perms os.FileMode) {
m.AddArtifact(CustomTemplateFile{
Name: name,
Perms: perms,
TemplateArtifact: TemplateArtifact{
Template: tpl,
Data: data,
},
})
}
// OverwriteCustomTemplateFile behaves the same as OverwriteCustomFile, however
// the contents are rendered from the provided tpl and data.
func (m *ModuleBase) OverwriteCustomTemplateFile(name string, tpl Template, data interface{}, perms os.FileMode) {
m.AddArtifact(CustomTemplateFile{
Name: name,
Perms: perms,
Overwrite: true,
TemplateArtifact: TemplateArtifact{
Template: tpl,
Data: data,
},
})
}
// AddError adds a string to the `errors` field of the created
// CodeGeneratorResponse. Multiple calls to AddError will cause the errors to
// be concatenated (separated by "; ").
func (m *ModuleBase) AddError(message string) {
m.AddArtifact(GeneratorError{Message: message})
}
var _ Module = (*ModuleBase)(nil)