2016-06-12 20:19:23 +02:00
|
|
|
package main
|
2016-03-28 10:17:41 +02:00
|
|
|
|
|
|
|
import (
|
2016-06-26 05:18:03 +02:00
|
|
|
"bufio"
|
2016-03-28 10:17:41 +02:00
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"go/format"
|
|
|
|
"io"
|
|
|
|
"os"
|
2016-04-06 22:10:12 +02:00
|
|
|
"path/filepath"
|
2016-06-20 07:20:38 +02:00
|
|
|
"regexp"
|
2016-06-26 05:18:03 +02:00
|
|
|
"strconv"
|
2016-03-28 10:17:41 +02:00
|
|
|
"text/template"
|
2016-06-20 07:20:38 +02:00
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
2016-03-28 10:17:41 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
var testHarnessStdout io.Writer = os.Stdout
|
|
|
|
var testHarnessFileOpen = func(filename string) (io.WriteCloser, error) {
|
|
|
|
file, err := os.Create(filename)
|
|
|
|
return file, err
|
|
|
|
}
|
|
|
|
|
2016-04-06 22:10:12 +02:00
|
|
|
// generateOutput builds the file output and sends it to outHandler for saving
|
2016-06-12 03:25:00 +02:00
|
|
|
func generateOutput(state *State, data *templateData) error {
|
2016-06-12 09:32:46 +02:00
|
|
|
return executeTemplates(executeTemplateData{
|
|
|
|
state: state,
|
|
|
|
data: data,
|
|
|
|
templates: state.Templates,
|
|
|
|
importSet: defaultTemplateImports,
|
|
|
|
combineImportsOnType: true,
|
|
|
|
fileSuffix: ".go",
|
|
|
|
})
|
|
|
|
}
|
2016-04-06 22:10:12 +02:00
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
// generateTestOutput builds the test file output and sends it to outHandler for saving
|
|
|
|
func generateTestOutput(state *State, data *templateData) error {
|
|
|
|
return executeTemplates(executeTemplateData{
|
|
|
|
state: state,
|
|
|
|
data: data,
|
|
|
|
templates: state.TestTemplates,
|
|
|
|
importSet: defaultTestTemplateImports,
|
|
|
|
combineImportsOnType: false,
|
|
|
|
fileSuffix: "_test.go",
|
|
|
|
})
|
|
|
|
}
|
2016-03-28 10:17:41 +02:00
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
// generateSingletonOutput processes the templates that should only be run
|
|
|
|
// one time.
|
|
|
|
func generateSingletonOutput(state *State, data *templateData) error {
|
|
|
|
return executeSingletonTemplates(executeTemplateData{
|
|
|
|
state: state,
|
|
|
|
data: data,
|
|
|
|
templates: state.SingletonTemplates,
|
|
|
|
importNamedSet: defaultSingletonTemplateImports,
|
|
|
|
fileSuffix: ".go",
|
|
|
|
})
|
|
|
|
}
|
2016-03-28 10:17:41 +02:00
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
// generateSingletonTestOutput processes the templates that should only be run
|
|
|
|
// one time.
|
|
|
|
func generateSingletonTestOutput(state *State, data *templateData) error {
|
|
|
|
return executeSingletonTemplates(executeTemplateData{
|
|
|
|
state: state,
|
|
|
|
data: data,
|
|
|
|
templates: state.SingletonTestTemplates,
|
|
|
|
importNamedSet: defaultSingletonTestTemplateImports,
|
|
|
|
fileSuffix: "_test.go",
|
|
|
|
})
|
2016-03-28 10:17:41 +02:00
|
|
|
}
|
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
type executeTemplateData struct {
|
|
|
|
state *State
|
|
|
|
data *templateData
|
2016-03-28 10:17:41 +02:00
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
templates templateList
|
|
|
|
|
|
|
|
importSet imports
|
|
|
|
importNamedSet map[string]imports
|
|
|
|
|
|
|
|
combineImportsOnType bool
|
|
|
|
|
|
|
|
fileSuffix string
|
|
|
|
}
|
|
|
|
|
|
|
|
func executeTemplates(e executeTemplateData) error {
|
2016-06-27 08:54:23 +02:00
|
|
|
if e.data.Table.IsJoinTable {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-06 22:10:12 +02:00
|
|
|
var out [][]byte
|
|
|
|
var imps imports
|
2016-03-28 10:17:41 +02:00
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
imps.standard = e.importSet.standard
|
|
|
|
imps.thirdParty = e.importSet.thirdParty
|
|
|
|
|
|
|
|
for _, template := range e.templates {
|
|
|
|
if e.combineImportsOnType {
|
|
|
|
imps = combineTypeImports(imps, importsBasedOnType, e.data.Table.Columns)
|
|
|
|
}
|
2016-03-28 10:17:41 +02:00
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
resp, err := executeTemplate(template, e.data)
|
2016-03-28 10:17:41 +02:00
|
|
|
if err != nil {
|
2016-06-12 09:32:46 +02:00
|
|
|
return fmt.Errorf("Error generating template %s: %s", template.Name(), err)
|
2016-03-28 10:17:41 +02:00
|
|
|
}
|
2016-04-06 22:10:12 +02:00
|
|
|
out = append(out, resp)
|
|
|
|
}
|
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
fName := e.data.Table.Name + e.fileSuffix
|
|
|
|
err := outHandler(e.state.Config.OutFolder, fName, e.state.Config.PkgName, imps, out)
|
2016-04-06 22:10:12 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
func executeSingletonTemplates(e executeTemplateData) error {
|
2016-06-27 08:54:23 +02:00
|
|
|
if e.data.Table.IsJoinTable {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-06-20 07:20:38 +02:00
|
|
|
rgxRemove := regexp.MustCompile(`[0-9]+_`)
|
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
for _, template := range e.templates {
|
|
|
|
resp, err := executeTemplate(template, e.data)
|
2016-04-19 04:02:32 +02:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Error generating template %s: %s", template.Name(), err)
|
|
|
|
}
|
|
|
|
|
|
|
|
fName := template.Name()
|
|
|
|
ext := filepath.Ext(fName)
|
2016-06-20 07:57:46 +02:00
|
|
|
fName = rgxRemove.ReplaceAllString(fName[:len(fName)-len(ext)], "")
|
2016-04-19 04:02:32 +02:00
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
imps := imports{
|
|
|
|
standard: e.importNamedSet[fName].standard,
|
|
|
|
thirdParty: e.importNamedSet[fName].thirdParty,
|
2016-04-19 04:02:32 +02:00
|
|
|
}
|
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
err = outHandler(
|
|
|
|
e.state.Config.OutFolder,
|
2016-06-20 07:57:46 +02:00
|
|
|
fName+e.fileSuffix,
|
2016-06-12 09:32:46 +02:00
|
|
|
e.state.Config.PkgName,
|
|
|
|
imps,
|
|
|
|
[][]byte{resp},
|
|
|
|
)
|
2016-06-02 23:07:51 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
func generateTestMainOutput(state *State, data *templateData) error {
|
2016-06-12 03:25:00 +02:00
|
|
|
if state.TestMainTemplate == nil {
|
2016-04-06 22:10:12 +02:00
|
|
|
return errors.New("No TestMain template located for generation")
|
|
|
|
}
|
|
|
|
|
|
|
|
var out [][]byte
|
|
|
|
var imps imports
|
|
|
|
|
2016-06-12 03:25:00 +02:00
|
|
|
imps.standard = defaultTestMainImports[state.Config.DriverName].standard
|
|
|
|
imps.thirdParty = defaultTestMainImports[state.Config.DriverName].thirdParty
|
2016-04-06 22:10:12 +02:00
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
resp, err := executeTemplate(state.TestMainTemplate, data)
|
2016-04-06 22:10:12 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
out = append(out, resp)
|
|
|
|
|
2016-06-12 03:25:00 +02:00
|
|
|
err = outHandler(state.Config.OutFolder, "main_test.go", state.Config.PkgName, imps, out)
|
2016-04-06 22:10:12 +02:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func outHandler(outFolder string, fileName string, pkgName string, imps imports, contents [][]byte) error {
|
|
|
|
out := testHarnessStdout
|
|
|
|
|
|
|
|
path := filepath.Join(outFolder, fileName)
|
|
|
|
|
|
|
|
outFile, err := testHarnessFileOpen(path)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("Unable to create output file %s: %s", path, err)
|
2016-03-28 10:17:41 +02:00
|
|
|
}
|
2016-04-06 22:10:12 +02:00
|
|
|
defer outFile.Close()
|
|
|
|
out = outFile
|
2016-03-28 10:17:41 +02:00
|
|
|
|
2016-04-06 22:10:12 +02:00
|
|
|
if _, err := fmt.Fprintf(out, "package %s\n\n", pkgName); err != nil {
|
|
|
|
return fmt.Errorf("Unable to write package name %s to file: %s", pkgName, path)
|
2016-03-28 10:17:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impStr := buildImportString(imps)
|
|
|
|
if len(impStr) > 0 {
|
|
|
|
if _, err := fmt.Fprintf(out, "%s\n", impStr); err != nil {
|
|
|
|
return fmt.Errorf("Unable to write imports to file handle: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-06 22:10:12 +02:00
|
|
|
for _, templateOutput := range contents {
|
2016-03-28 10:17:41 +02:00
|
|
|
if _, err := fmt.Fprintf(out, "%s\n", templateOutput); err != nil {
|
|
|
|
return fmt.Errorf("Unable to write template output to file handle: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-06-26 05:18:03 +02:00
|
|
|
var rgxSyntaxError = regexp.MustCompile(`(\d+):\d+: `)
|
|
|
|
|
2016-06-12 09:32:46 +02:00
|
|
|
// executeTemplate takes a template and returns the output of the template
|
|
|
|
// execution.
|
|
|
|
func executeTemplate(t *template.Template, data *templateData) ([]byte, error) {
|
2016-03-28 10:17:41 +02:00
|
|
|
var buf bytes.Buffer
|
|
|
|
if err := t.Execute(&buf, data); err != nil {
|
2016-06-20 07:20:38 +02:00
|
|
|
return nil, errors.Wrap(err, "failed to execute template")
|
2016-03-28 10:17:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
output, err := format.Source(buf.Bytes())
|
|
|
|
if err != nil {
|
2016-06-26 05:18:03 +02:00
|
|
|
matches := rgxSyntaxError.FindStringSubmatch(err.Error())
|
|
|
|
if matches == nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to format template")
|
|
|
|
}
|
|
|
|
|
|
|
|
lineNum, _ := strconv.Atoi(matches[1])
|
|
|
|
scanner := bufio.NewScanner(&buf)
|
|
|
|
errBuf := &bytes.Buffer{}
|
|
|
|
line := 0
|
|
|
|
for ; scanner.Scan(); line++ {
|
|
|
|
if delta := line - lineNum; delta < -5 || delta > 5 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if line == lineNum {
|
|
|
|
errBuf.WriteString(">>> ")
|
|
|
|
} else {
|
|
|
|
fmt.Fprintf(errBuf, "% 3d ", line)
|
|
|
|
}
|
|
|
|
errBuf.Write(scanner.Bytes())
|
|
|
|
errBuf.WriteByte('\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, errors.Wrapf(err, "failed to format template\n\n%s\n", errBuf.Bytes())
|
2016-03-28 10:17:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return output, nil
|
|
|
|
}
|