-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgenerator.go
275 lines (239 loc) · 9.59 KB
/
generator.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
266
267
268
269
270
271
272
273
274
275
package generate
import (
"bytes"
"fmt"
"go/format"
"io"
"log"
"os"
"path/filepath"
"strings"
"text/template"
"github.com/paloaltonetworks/pan-os-codegen/pkg/naming"
"github.com/paloaltonetworks/pan-os-codegen/pkg/properties"
"github.com/paloaltonetworks/pan-os-codegen/pkg/translate"
"github.com/paloaltonetworks/pan-os-codegen/pkg/translate/terraform_provider"
)
type Creator struct {
GoOutputDir string
TemplatesDir string
Spec *properties.Normalization
}
// NewCreator initializes a Creator instance.
func NewCreator(goOutputDir, templatesDir string, spec *properties.Normalization) *Creator {
return &Creator{
GoOutputDir: goOutputDir,
TemplatesDir: templatesDir,
Spec: spec,
}
}
// RenderTemplate loops through all templates, parses them, and renders content, which is saved to the output file.
func (c *Creator) RenderTemplate() error {
log.Println("Start rendering templates")
templates, err := c.listOfTemplates()
if err != nil {
return fmt.Errorf("error listing templates: %w", err)
}
for _, templateName := range templates {
filePath := c.createFullFilePath(templateName)
log.Printf("Creating file: %s\n", filePath)
if err := c.makeAllDirs(filePath); err != nil {
return fmt.Errorf("error creating directories for %s: %w", filePath, err)
}
if err := c.processTemplate(templateName, filePath); err != nil {
return err
}
}
return nil
}
// RenderTerraformProviderFile generates a Go file for a Terraform provider based on the provided TerraformProviderFile and Normalization arguments.
func (c *Creator) RenderTerraformProviderFile(spec *properties.Normalization, typ properties.ResourceType) ([]string, []string, error) {
var name string
switch typ {
case properties.ResourceUuidPlural:
name = fmt.Sprintf("%s_%s", spec.TerraformProviderConfig.Suffix, spec.TerraformProviderConfig.PluralName)
case properties.ResourceEntryPlural:
name = spec.TerraformProviderConfig.PluralSuffix
case properties.ResourceEntry, properties.ResourceUuid:
name = spec.Name
}
terraformProvider := properties.NewTerraformProviderFile(name)
tfp := terraform_provider.GenerateTerraformProvider{}
if err := tfp.GenerateTerraformDataSource(typ, spec, terraformProvider); err != nil {
return nil, nil, err
}
if err := tfp.GenerateTerraformResource(typ, spec, terraformProvider); err != nil {
return nil, nil, err
}
if err := tfp.GenerateCommonCode(typ, spec, terraformProvider); err != nil {
return nil, nil, err
}
if err := tfp.GenerateTerraformProviderFile(spec, terraformProvider); err != nil {
return nil, nil, err
}
var filePath string
switch typ {
case properties.ResourceUuidPlural:
name = fmt.Sprintf("%s_%s", spec.TerraformProviderConfig.Suffix, spec.TerraformProviderConfig.PluralName)
filePath = c.createTerraformProviderFilePath(name)
case properties.ResourceEntryPlural:
name = spec.TerraformProviderConfig.PluralSuffix
filePath = c.createTerraformProviderFilePath(name)
case properties.ResourceEntry, properties.ResourceUuid:
filePath = c.createTerraformProviderFilePath(spec.TerraformProviderConfig.Suffix)
}
if err := c.writeFormattedContentToFile(filePath, terraformProvider.Code.String()); err != nil {
return nil, nil, err
}
return terraformProvider.DataSources, terraformProvider.Resources, nil
}
// RenderTerraformProvider generates and writes a Terraform provider file.
func (c *Creator) RenderTerraformProvider(terraformProvider *properties.TerraformProviderFile, spec *properties.Normalization, providerConfig properties.TerraformProvider) error {
tfp := terraform_provider.GenerateTerraformProvider{}
if err := tfp.GenerateTerraformProvider(terraformProvider, spec, providerConfig); err != nil {
return err
}
filePath := c.createTerraformProviderFilePath(spec.Name)
return c.writeFormattedContentToFile(filePath, terraformProvider.Code.String())
}
// processTemplate processes a single template and writes the rendered content to a file.
func (c *Creator) processTemplate(templateName, filePath string) error {
tmpl, err := c.parseTemplate(templateName)
if err != nil {
return fmt.Errorf("error parsing template %s: %w", templateName, err)
}
var data bytes.Buffer
if err := tmpl.Execute(&data, c.Spec); err != nil {
return fmt.Errorf("error executing template %s: %w", templateName, err)
}
// If no data was rendered from the template, skip creating an empty file.
dataLength := len(bytes.TrimSpace(data.Bytes()))
if dataLength > 0 {
var formattedCode []byte
formattedCode, err = format.Source(data.Bytes())
if err != nil {
log.Printf("Failed to format source code: %s", err.Error())
formattedCode = data.Bytes()
}
formattedBuf := bytes.NewBuffer(formattedCode)
if writeErr := c.createAndWriteFile(filePath, formattedBuf); writeErr != nil {
return fmt.Errorf("error creating and writing to file %s: %w", filePath, err)
}
}
return err
}
// writeFormattedContentToFile formats the content and writes it to a file.
func (c *Creator) writeFormattedContentToFile(filePath, content string) error {
formattedCode, err := format.Source([]byte(content))
if err != nil {
log.Printf("provided content: %s", content)
return fmt.Errorf("error formatting code: %w", err)
}
formattedBuf := bytes.NewBuffer(formattedCode)
return c.createFileAndWriteContent(filePath, formattedBuf)
}
// createTerraformProviderFilePath returns a file path for a Terraform provider based on the provided suffix.
func (c *Creator) createTerraformProviderFilePath(terraformProviderFileName string) string {
fileName := fmt.Sprintf("%s.go", terraformProviderFileName)
return filepath.Join(c.GoOutputDir, "internal/provider", fileName)
}
// createFileAndWriteContent creates a new file at the specified filePath and writes the content from the content buffer to the file.
func (c *Creator) createFileAndWriteContent(filePath string, content *bytes.Buffer) error {
if err := c.makeAllDirs(filePath); err != nil {
return fmt.Errorf("error creating directories for %s: %w", filePath, err)
}
if err := c.createAndWriteFile(filePath, content); err != nil {
return err
}
return nil
}
// createAndWriteFile creates a new file at the specified filePath and writes the content from the content buffer to the file.
// If an error occurs during file creation or content writing, it returns an error. The file is automatically closed after writing.
func (c *Creator) createAndWriteFile(filePath string, content *bytes.Buffer) error {
outputFile, err := c.createFile(filePath)
if err != nil {
return err
}
defer func(outputFile *os.File) {
_ = outputFile.Close()
}(outputFile)
return writeContentToFile(content, outputFile)
}
// createFullFilePath returns a full path for the output file generated from the template passed as an argument.
func (c *Creator) createFullFilePath(templateName string) string {
fileBaseName := strings.TrimSuffix(templateName, filepath.Ext(templateName))
return filepath.Join(c.GoOutputDir, filepath.Join(c.Spec.GoSdkPath...), fmt.Sprintf("%s.go", fileBaseName))
}
// listOfTemplates returns a list of templates defined in TemplatesDir.
func (c *Creator) listOfTemplates() ([]string, error) {
var files []string
err := filepath.WalkDir(c.TemplatesDir, func(path string, entry os.DirEntry, err error) error {
if err != nil {
return err
}
if entry.IsDir() {
return nil
}
if strings.HasSuffix(entry.Name(), ".tmpl") {
files = append(files, entry.Name())
}
return nil
})
if err != nil {
return nil, err
}
return files, nil
}
// makeAllDirs creates all required directories in the file path.
func (c *Creator) makeAllDirs(filePath string) error {
dirPath := filepath.Dir(filePath)
return os.MkdirAll(dirPath, os.ModePerm)
}
// createFile creates a file and returns it.
func (c *Creator) createFile(filePath string) (*os.File, error) {
outputFile, err := os.Create(filePath)
if err != nil {
return nil, fmt.Errorf("error creating file %s: %w", filePath, err)
}
return outputFile, nil
}
func writeContentToFile(content *bytes.Buffer, file *os.File) error {
_, err := io.Copy(file, content)
if err != nil {
return fmt.Errorf("error writing to file: %w", err)
}
return nil
}
// parseTemplate parses the template passed as an argument with the function map defined below.
func (c *Creator) parseTemplate(templateName string) (*template.Template, error) {
templatePath := filepath.Join(c.TemplatesDir, templateName)
funcMap := template.FuncMap{
"renderImports": translate.RenderImports,
"packageName": translate.PackageName,
"locationType": translate.LocationType,
"specParamType": translate.SpecParamType,
"xmlParamType": translate.XmlParamType,
"xmlName": translate.XmlName,
"xmlTag": translate.XmlTag,
"specifyEntryAssignment": translate.SpecifyEntryAssignment,
"normalizeAssignment": translate.NormalizeAssignment,
"specMatchesFunction": translate.SpecMatchesFunction,
"nestedSpecMatchesFunction": translate.NestedSpecMatchesFunction,
"omitEmpty": translate.OmitEmpty,
"contains": strings.Contains,
"add": func(a, b int) int {
return a + b
},
"subtract": func(a, b int) int {
return a - b
},
"generateEntryXpath": translate.GenerateEntryXpath,
"nestedSpecs": translate.NestedSpecs,
"createGoSuffixFromVersion": translate.CreateGoSuffixFromVersion,
"paramSupportedInVersion": translate.ParamSupportedInVersion,
"xmlPathSuffixes": translate.XmlPathSuffixes,
"underscore": naming.Underscore,
"camelCase": naming.CamelCase,
}
return template.New(templateName).Funcs(funcMap).ParseFiles(templatePath)
}