1
0
mirror of https://github.com/ManyakRus/crud_generator.git synced 2024-12-22 00:36:41 +02:00
crud_generator/pkg/dbmeta/codegen.go

999 lines
27 KiB
Go
Raw Normal View History

2023-10-24 17:03:04 +02:00
package dbmeta
import (
"bytes"
"encoding/csv"
"encoding/json"
"fmt"
"path"
"time"
"github.com/ManyakRus/crud_generator/pkg/utils"
"go/format"
"io/ioutil"
"os"
"path/filepath"
"reflect"
"regexp"
"strings"
"text/template"
"github.com/davecgh/go-spew/spew"
"github.com/jinzhu/inflection"
"github.com/serenize/snaker"
"golang.org/x/tools/imports"
)
// GenTemplate template info struct
type GenTemplate struct {
Name string
Content string
}
// TemplateLoader loader function to retrieve a template contents
type TemplateLoader func(filename string) (tpl *GenTemplate, err error)
var replaceFuncMap = template.FuncMap{
"singular": inflection.Singular,
"pluralize": inflection.Plural,
"title": strings.Title,
"toLower": strings.ToLower,
"toUpper": strings.ToUpper,
"toLowerCamelCase": camelToLowerCamel,
"toUpperCamelCase": camelToUpperCamel,
"toSnakeCase": snaker.CamelToSnake,
"StringsJoin": strings.Join,
"replace": replace,
"stringifyFirstChar": stringifyFirstChar,
"FmtFieldName": FmtFieldName,
}
func replace(input, from, to string) string {
return strings.Replace(input, from, to, -1)
}
// Replace takes a template based name format and will render a name using it
func Replace(nameFormat, name string) string {
var tpl bytes.Buffer
//fmt.Printf("Replace: %s\n",nameFormat)
t := template.Must(template.New("t1").Funcs(replaceFuncMap).Parse(nameFormat))
//sanek start
if name[0:1] == "$" {
name = name[1:] + "$"
}
//sanek end
if err := t.Execute(&tpl, name); err != nil {
//fmt.Printf("Error creating name format: %s error: %v\n", nameFormat, err)
return name
}
result := tpl.String()
result = strings.Trim(result, " \t")
result = strings.Replace(result, " ", "_", -1)
result = strings.Replace(result, "\t", "_", -1)
//fmt.Printf("Replace( '%s' '%s')= %s\n",nameFormat, name, result)
return result
}
// ReplaceFileNamingTemplate use the FileNamingTemplate to format a table name
func (c *Config) ReplaceFileNamingTemplate(name string) string {
return Replace(c.FileNamingTemplate, name)
}
// ReplaceModelNamingTemplate use the ModelNamingTemplate to format a table name
func (c *Config) ReplaceModelNamingTemplate(name string) string {
return Replace(c.ModelNamingTemplate, name)
}
// ReplaceFieldNamingTemplate use the FieldNamingTemplate to format a table name
func (c *Config) ReplaceFieldNamingTemplate(name string) string {
return Replace(c.FieldNamingTemplate, name)
}
// GetTemplate return a Template based on a name and template contents
func (c *Config) GetTemplate(genTemplate *GenTemplate) (*template.Template, error) {
var s State
var funcMap = template.FuncMap{
"ReplaceFileNamingTemplate": c.ReplaceFileNamingTemplate,
"ReplaceModelNamingTemplate": c.ReplaceModelNamingTemplate,
"ReplaceFieldNamingTemplate": c.ReplaceFieldNamingTemplate,
"stringifyFirstChar": stringifyFirstChar,
"singular": inflection.Singular,
"pluralize": inflection.Plural,
"title": strings.Title,
"toLower": strings.ToLower,
"toUpper": strings.ToUpper,
"toLowerCamelCase": camelToLowerCamel,
"toUpperCamelCase": camelToUpperCamel,
"FormatSource": FormatSource,
"toSnakeCase": snaker.CamelToSnake,
"markdownCodeBlock": markdownCodeBlock,
"wrapBash": wrapBash,
"escape": escape,
"GenerateTableFile": c.GenerateTableFile,
"GenerateFile": c.GenerateFile,
"ToJSON": ToJSON,
"spew": Spew,
"set": s.Set,
"inc": s.Inc,
"StringsJoin": strings.Join,
"replace": replace,
"hasField": hasField,
"FmtFieldName": FmtFieldName,
"copy": c.FileSystemCopy,
"mkdir": c.Mkdir,
"touch": c.Touch,
"pwd": Pwd,
"config": c.DisplayConfig,
"insertFragment": c.insertFragment,
}
baseName := filepath.Base(genTemplate.Name)
tmpl, err := template.New(baseName).Option("missingkey=error").Funcs(funcMap).Parse(genTemplate.Content)
if err != nil {
return nil, err
}
if baseName == "api.go.tmpl" ||
baseName == "dao_gorm.go.tmpl" ||
baseName == "dao_sqlx.go.tmpl" ||
baseName == "code_dao_sqlx.md.tmpl" ||
baseName == "code_dao_gorm.md.tmpl" ||
baseName == "code_http.md.tmpl" {
operations := []string{"add", "delete", "get", "getall", "update"}
for _, op := range operations {
var filename string
if baseName == "api.go.tmpl" {
filename = fmt.Sprintf("api_%s.go.tmpl", op)
}
if baseName == "dao_gorm.go.tmpl" {
filename = fmt.Sprintf("dao_gorm_%s.go.tmpl", op)
}
if baseName == "dao_sqlx.go.tmpl" {
filename = fmt.Sprintf("dao_sqlx_%s.go.tmpl", op)
}
if baseName == "code_dao_sqlx.md.tmpl" {
filename = fmt.Sprintf("dao_sqlx_%s.go.tmpl", op)
}
if baseName == "code_dao_gorm.md.tmpl" {
filename = fmt.Sprintf("dao_gorm_%s.go.tmpl", op)
}
if baseName == "code_http.md.tmpl" {
filename = fmt.Sprintf("api_%s.go.tmpl", op)
}
var subTemplate *GenTemplate
if subTemplate, err = c.TemplateLoader(filename); err != nil {
fmt.Printf("Error loading template %v\n", err)
return nil, err
}
// fmt.Printf("loading sub template %v\n", filename)
tmpl.Parse(subTemplate.Content)
}
}
return tmpl, nil
}
func hasField(v interface{}, name string) bool {
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if rv.Kind() != reflect.Struct {
return false
}
return rv.FieldByName(name).IsValid()
}
// ToJSON func to return json string representation of struct
func ToJSON(val interface{}, indent int) string {
pad := fmt.Sprintf("%*s", indent, "")
strB, _ := json.MarshalIndent(val, "", pad)
response := string(strB)
response = strings.Replace(response, "\n", "", -1)
return response
}
// Spew func to return spewed string representation of struct
func Spew(val interface{}) string {
return spew.Sdump(val)
}
// State struct used for storing state in template parsing
type State struct {
n int
}
// Set set state value in template parsing
func (s *State) Set(n int) int {
s.n = n
return n
}
// Inc increment state value in template parsing
func (s *State) Inc() int {
s.n++
return s.n
}
func camelToLowerCamel(s string) string {
ss := strings.Split(s, "")
ss[0] = strings.ToLower(ss[0])
return strings.Join(ss, "")
}
func camelToUpperCamel(s string) string {
ss := strings.Split(s, "")
ss[0] = strings.ToUpper(ss[0])
return strings.Join(ss, "")
}
// FormatSource format source code contents
func FormatSource(s string) string {
formattedSource, err := format.Source([]byte(s))
if err != nil {
return fmt.Sprintf("Error in formatting source: %s\n", err.Error())
}
formattedSource, err = imports.Process("", formattedSource, nil)
if err != nil {
return fmt.Sprintf("Error in formatting source: %s\n", err.Error())
}
return string(formattedSource)
}
func markdownCodeBlock(contentType, content string) string {
// fmt.Printf("%s - %s\n", contentType, content)
return fmt.Sprintf("```%s\n%s\n```\n", contentType, content)
}
func wrapBash(content string) string {
r := csv.NewReader(strings.NewReader(content))
r.Comma = ' '
record, err := r.Read()
if err != nil {
return content
}
fmt.Printf("[%s]\n", content)
for i, j := range record {
fmt.Printf("wrapBash [%d] %s\n", i, j)
}
out := strings.Join(record, " \\\n ")
return out
//
//
//r := regexp.MustCompile(`[^\s"']+|"([^"]*)"|'([^']*)`)
//arr := r.FindAllString(content, -1)
//return strings.Join(arr, " \\\n ")
//
//
//splitter := "[^\\s\"']+|\"[^\"]*\"|'[^']*'"
//result := RegSplit(content, splitter)
//return strings.Join(result, " \\\n ")
//
//result, err := parseCommandLine(content)
//if err != nil {
// return content
//}
//return strings.Join(result, " \\\n ")
}
// RegSplit split text based on regex
func RegSplit(text string, delimeter string) []string {
reg := regexp.MustCompile(delimeter)
indexes := reg.FindAllStringIndex(text, -1)
laststart := 0
result := make([]string, len(indexes)+1)
for i, element := range indexes {
result[i] = text[laststart:element[0]]
laststart = element[1]
}
result[len(indexes)] = text[laststart:]
return result
}
func parseCommandLine(command string) ([]string, error) {
var args []string
state := "start"
current := ""
quote := "\""
escapeNext := true
for i := 0; i < len(command); i++ {
c := command[i]
if state == "quotes" {
if string(c) != quote {
current += string(c)
} else {
args = append(args, current)
current = ""
state = "start"
}
continue
}
if escapeNext {
current += string(c)
escapeNext = false
continue
}
if c == '\\' {
escapeNext = true
continue
}
if c == '"' || c == '\'' {
state = "quotes"
quote = string(c)
continue
}
if state == "arg" {
if c == ' ' || c == '\t' {
args = append(args, current)
current = ""
state = "start"
} else {
current += string(c)
}
continue
}
if c != ' ' && c != '\t' {
state = "arg"
current += string(c)
}
}
if state == "quotes" {
return []string{}, fmt.Errorf("unclosed quote in command line: %s", command)
}
if current != "" {
args = append(args, current)
}
return args, nil
}
func escape(content string) string {
content = strings.Replace(content, "\"", "\\\"", -1)
content = strings.Replace(content, "'", "\\'", -1)
return content
}
// LoadFragments read all filed inside dirname to `fragments`
func (c *Config) LoadFragments(dirname string) error {
c.FragmentsDir = dirname
files, err := ioutil.ReadDir(dirname)
if err != nil {
return err
}
c.fragments = &bytes.Buffer{}
for _, file := range files {
if !file.IsDir() {
filename := path.Join(dirname, file.Name())
content, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
c.fragments.Write(content)
c.fragments.WriteRune('\n')
}
}
return nil
}
// Insert code fragment from file.
// Within a single file, fragment can be marked as // fragment: <name> ... // end
func (c *Config) insertFragment(name, defValue string) (string, error) {
if c.fragments == nil || c.fragments.Len() == 0 {
return defValue, nil
}
// pointer to fragment content
content := c.fragments.Bytes()
// if fragment name is not defined,
// return whole content
if name == "" {
return string(content), nil
}
// Use regex to extract fragment name from given file.
// Below is an example of code fragment.
/*
//fragment: <name>
func (w *Model) MyCode() {
}
// end
*/
begExp := fmt.Sprintf(`//[\s]{0,}fragment[\s]{0,}:[\s]{0,}%s[\s]{1,}`, name)
reBeg, err := regexp.Compile(begExp)
if err != nil {
return defValue, nil
}
begLoc := reBeg.FindIndex(content)
if begLoc == nil || begLoc[1] == len(content) {
// not found or no content specified after // fragment: <name>
return defValue, nil
}
fromIdx := begLoc[1]
subContent := content[fromIdx:]
reEnd := regexp.MustCompile(`//[\s]end[\s]{0,}`)
endLoc := reEnd.FindIndex(subContent)
if endLoc == nil {
// not found, return remaining content
return string(subContent), nil
}
return string(subContent[:endLoc[0]]), nil
}
// JSONFieldName convert name to appropriate case
func (c *Config) JSONFieldName(name string) string {
return formatFieldName(c.JSONNameFormat, name)
}
// JSONTag converts name to `json:"name"` respecting json-fmt option
func (c *Config) JSONTag(name string) string {
return fmt.Sprintf("`json:\"%s\"`", c.JSONFieldName(name))
}
// JSONTagOmitEmpty converts name to JSON tag with omitempty
func (c *Config) JSONTagOmitEmpty(name string) string {
return fmt.Sprintf("`json:\"%s,omitempty\"`", c.JSONFieldName(name))
}
// GenerateTableFile generate file from template using specific table used within templates
func (c *Config) GenerateTableFile(tableName, templateFilename, outputDirectory, outputFileName string) string {
buf := bytes.Buffer{}
buf.WriteString(fmt.Sprintf("GenerateTableFile( %s, %s, %s, %s)\n", tableName, templateFilename, outputDirectory, outputFileName))
tableInfo, ok := c.TableInfos[tableName]
if !ok {
buf.WriteString(fmt.Sprintf("Table: %s - No tableInfo found\n", tableName))
return buf.String()
}
if len(tableInfo.Fields) == 0 {
buf.WriteString(fmt.Sprintf("able: %s - No Fields Available\n", tableName))
return buf.String()
}
data := c.CreateContextForTableFile(tableInfo)
fileOutDir := filepath.Join(c.OutDir, outputDirectory)
err := os.MkdirAll(fileOutDir, 0777)
if err != nil && !c.Overwrite {
buf.WriteString(fmt.Sprintf("unable to create fileOutDir: %s error: %v\n", fileOutDir, err))
return buf.String()
}
var tpl *GenTemplate
if tpl, err = c.TemplateLoader(templateFilename); err != nil {
buf.WriteString(fmt.Sprintf("Error loading template %v\n", err))
return buf.String()
}
outputFile := filepath.Join(fileOutDir, outputFileName)
buf.WriteString(fmt.Sprintf("Writing %s -> %s\n", templateFilename, outputFile))
err = c.WriteTemplate(tpl, data, outputFile)
return buf.String()
}
// CreateContextForTableFile create map context for a db table
func (c *Config) CreateContextForTableFile(tableInfo *ModelInfo) map[string]interface{} {
var modelInfo = map[string]interface{}{
"StructName": tableInfo.StructName,
"TableName": tableInfo.DBMeta.TableName(),
"ShortStructName": strings.ToLower(string(tableInfo.StructName[0])),
"TableInfo": tableInfo,
}
nonPrimaryKeys := NonPrimaryKeyNames(tableInfo.DBMeta)
modelInfo["NonPrimaryKeyNamesList"] = nonPrimaryKeys
modelInfo["NonPrimaryKeysJoined"] = strings.Join(nonPrimaryKeys, ",")
primaryKeys := PrimaryKeyNames(tableInfo.DBMeta)
modelInfo["PrimaryKeyNamesList"] = primaryKeys
modelInfo["PrimaryKeysJoined"] = strings.Join(primaryKeys, ",")
delSQL, err := GenerateDeleteSQL(tableInfo.DBMeta)
if err == nil {
modelInfo["delSql"] = delSQL
}
updateSQL, err := GenerateUpdateSQL(tableInfo.DBMeta)
if err == nil {
modelInfo["updateSql"] = updateSQL
}
insertSQL, err := GenerateInsertSQL(tableInfo.DBMeta)
if err == nil {
modelInfo["insertSql"] = insertSQL
}
selectOneSQL, err := GenerateSelectOneSQL(tableInfo.DBMeta)
if err == nil {
modelInfo["selectOneSql"] = selectOneSQL
}
selectMultiSQL, err := GenerateSelectMultiSQL(tableInfo.DBMeta)
if err == nil {
modelInfo["selectMultiSql"] = selectMultiSQL
}
return modelInfo
}
// WriteTemplate write a template out
func (c *Config) WriteTemplate(genTemplate *GenTemplate, data map[string]interface{}, outputFile string) error {
//fmt.Printf("WriteTemplate %s\n", outputFile)
if !c.Overwrite && Exists(outputFile) {
fmt.Printf("not overwriting %s\n", outputFile)
return nil
}
for key, value := range c.ContextMap {
data[key] = value
}
dir := filepath.Dir(outputFile)
parent := filepath.Base(dir)
data["File"] = outputFile
data["Dir"] = dir
data["Parent"] = parent
data["DatabaseName"] = c.SQLDatabase
data["module"] = c.Module
data["modelFQPN"] = c.ModelFQPN
data["modelPackageName"] = c.ModelPackageName
data["daoFQPN"] = c.DaoFQPN
data["daoPackageName"] = c.DaoPackageName
data["UseGuregu"] = c.UseGureguTypes
data["apiFQPN"] = c.APIFQPN
data["apiPackageName"] = c.APIPackageName
data["sqlType"] = c.SQLType
data["sqlConnStr"] = c.SQLConnStr
data["serverPort"] = c.ServerPort
data["serverHost"] = c.ServerHost
data["serverScheme"] = c.ServerScheme
data["serverListen"] = c.ServerListen
data["SwaggerInfo"] = c.Swagger
data["outDir"] = c.OutDir
data["Config"] = c
rt, err := c.GetTemplate(genTemplate)
if err != nil {
return fmt.Errorf("error in loading %s template, error: %v", genTemplate.Name, err)
}
var buf bytes.Buffer
err = rt.Execute(&buf, data)
if err != nil {
return fmt.Errorf("error in rendering %s: %s", genTemplate.Name, err.Error())
}
fileContents, err := c.format(genTemplate, buf.Bytes(), outputFile)
if err != nil {
return fmt.Errorf("error writing %s - error: %v", outputFile, err)
}
err = ioutil.WriteFile(outputFile, fileContents, 0777)
if err != nil {
return fmt.Errorf("error writing %s - error: %v", outputFile, err)
}
if c.Verbose {
fmt.Printf("writing %s\n", outputFile)
}
return nil
}
func (c *Config) format(genTemplate *GenTemplate, content []byte, outputFile string) ([]byte, error) {
extension := filepath.Ext(outputFile)
if extension == ".go" {
formattedSource, err := format.Source([]byte(content))
if err != nil {
return nil, fmt.Errorf("error in formatting template: %s outputfile: %s source: %s", genTemplate.Name, outputFile, err.Error())
}
fileContents := NormalizeNewlines(formattedSource)
if c.LineEndingCRLF {
fileContents = CRLFNewlines(formattedSource)
}
return fileContents, nil
}
fileContents := NormalizeNewlines([]byte(content))
if c.LineEndingCRLF {
fileContents = CRLFNewlines(fileContents)
}
return fileContents, nil
}
// NormalizeNewlines normalizes \r\n (windows) and \r (mac)
// into \n (unix)
func NormalizeNewlines(d []byte) []byte {
// replace CR LF \r\n (windows) with LF \n (unix)
d = bytes.Replace(d, []byte{13, 10}, []byte{10}, -1)
// replace CF \r (mac) with LF \n (unix)
d = bytes.Replace(d, []byte{13}, []byte{10}, -1)
return d
}
// CRLFNewlines transforms \n to \r\n (windows)
func CRLFNewlines(d []byte) []byte {
// replace LF (unix) with CR LF \r\n (windows)
d = bytes.Replace(d, []byte{10}, []byte{13, 10}, -1)
return d
}
// Exists reports whether the named file or directory exists.
func Exists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// GenerateFile generate file from template, non table used within templates
func (c *Config) GenerateFile(templateFilename, outputDirectory, outputFileName string, overwrite bool) string {
buf := bytes.Buffer{}
buf.WriteString(fmt.Sprintf("GenerateFile( %s, %s, %s)\n", templateFilename, outputDirectory, outputFileName))
fileOutDir := outputDirectory
err := os.MkdirAll(fileOutDir, 0777)
if err != nil && !overwrite {
buf.WriteString(fmt.Sprintf("unable to create fileOutDir: %s error: %v\n", fileOutDir, err))
return buf.String()
}
data := map[string]interface{}{}
var tpl *GenTemplate
if tpl, err = c.TemplateLoader(templateFilename); err != nil {
buf.WriteString(fmt.Sprintf("Error loading template %v\n", err))
return buf.String()
}
outputFile := filepath.Join(fileOutDir, outputFileName)
buf.WriteString(fmt.Sprintf("Writing %s -> %s\n", templateFilename, outputFile))
err = c.WriteTemplate(tpl, data, outputFile)
if err != nil {
buf.WriteString(fmt.Sprintf("Error calling WriteTemplate %s -> %v\n", templateFilename, err))
}
return buf.String()
}
// DisplayConfig display config info
func (c *Config) DisplayConfig() string {
info := fmt.Sprintf(
`DisplayConfig
SQLType : %s
SQLConnStr : %s
SQLDatabase : %s
Module : %s
OutDir : %s
`, c.SQLType, c.SQLConnStr, c.SQLDatabase, c.Module, c.OutDir)
return info
}
type copyRules struct {
result bool
pattern string
r *regexp.Regexp
}
// FileSystemCopy template command to copy files, directories and to pass --include XXX and --exclude YYY regular expressions. Files ending in .tmpl will be processed as a template.
// Files ending in .table.tmpl will be processed as a template iterating through all the tables
func (c *Config) FileSystemCopy(src, dst string, options ...string) string {
dstDir := filepath.Join(c.OutDir, dst)
patterns := make([]*copyRules, 0)
for _, o := range options {
if strings.HasPrefix(o, "--exclude ") {
pattern := o[len("--exclude "):]
r, _ := regexp.Compile(pattern)
if r != nil {
patterns = append(patterns, &copyRules{result: false, r: r, pattern: pattern})
}
}
if strings.HasPrefix(o, "--include ") {
pattern := o[len("--include "):]
r, _ := regexp.Compile(pattern)
if r != nil {
patterns = append(patterns, &copyRules{result: true, r: r, pattern: pattern})
}
}
}
opt := utils.DefaultCopyOptions()
opt.ShouldCopy = func(info os.FileInfo) bool {
name := info.Name()
for _, r := range patterns {
if r.r.Match([]byte(name)) {
//fmt.Printf("copy ShouldCopy %s pattern: [%s] result: %t\n", name, r.pattern, r.result)
return r.result
}
}
return true
}
opt.FileHandler = func(src, dest string, info os.FileInfo) utils.FileHandlerFunc {
if !opt.ShouldCopy(info) {
return func(src, dest string, info os.FileInfo, opt utils.Options, results *utils.Results) (err error) {
results.Info.WriteString(fmt.Sprintf("CopyFile Skipping %s\n", src))
return nil
}
}
if strings.HasSuffix(src, ".table.tmpl") {
//fmt.Printf("@@ HandleTableTemplateFile: src: %s dest: %s Name: %s\n", src, dest, info.Name())
return c.tableFileHandlerFunc
}
if strings.HasSuffix(src, ".tmpl") {
//fmt.Printf("@@ HandleTemplateFile: src: %s dest: %s Name: %s\n", src, dest, info.Name())
return c.fileHandlerFunc
}
return func(src, dest string, info os.FileInfo, opt utils.Options, results *utils.Results) (err error) {
results.Info.WriteString(fmt.Sprintf("CopyFile %s\n", dest))
return utils.DefaultFileCopy(src, dest, info, opt, results)
}
}
result, err := utils.Copy(src, dstDir, opt)
if err != nil {
return fmt.Sprintf("copy returned an error %v", err)
}
return fmt.Sprintf("copy %s %s\n%s\n", src, dstDir, result.String())
}
// Mkdir template command to mkdir under the output directory
func (c *Config) Mkdir(dst string) string {
dstDir := filepath.Join(c.OutDir, dst)
err := os.MkdirAll(dstDir, os.ModePerm)
if err != nil {
return fmt.Sprintf("mkdir returned an error %v", err)
}
return fmt.Sprintf("mkdir %s", dstDir)
}
// Touch template command to touch a file under the output directory
func (c *Config) Touch(dst string) string {
dstDir := filepath.Join(c.OutDir, dst)
_, err := os.Stat(dstDir)
if os.IsNotExist(err) {
file, err := os.Create(dstDir)
if err != nil {
return fmt.Sprintf("touch returned an error %v", err)
}
defer file.Close()
} else {
currentTime := time.Now().Local()
err = os.Chtimes(dstDir, currentTime, currentTime)
if err != nil {
return fmt.Sprintf("touch returned an error %v", err)
}
}
return fmt.Sprintf("touch %s", dstDir)
}
// ".tmpl"
func (c *Config) fileHandlerFunc(src, dest string, info os.FileInfo, opt utils.Options, results *utils.Results) (err error) {
genTemplate := &GenTemplate{
Name: info.Name(),
Content: loadFile(src),
}
data := make(map[string]interface{})
outputFile := dest[0 : len(dest)-5]
results.Info.WriteString(fmt.Sprintf("WriteTemplate %s\n", outputFile))
return c.WriteTemplate(genTemplate, data, outputFile)
}
// ".table.tmpl"
func (c *Config) tableFileHandlerFunc(src, dest string, info os.FileInfo, opt utils.Options, results *utils.Results) (err error) {
genTemplate := &GenTemplate{
Name: info.Name(),
Content: loadFile(src),
}
outputFile := dest[0 : len(dest)-11]
dir := filepath.Dir(outputFile)
tmplateName := filepath.Base(outputFile)
// parent := filepath.Base(dir)
results.Info.WriteString(fmt.Sprintf("WriteTableTemplate %s\n", src))
for tableName, tableInfo := range c.TableInfos {
data := c.CreateContextForTableFile(tableInfo)
// fileName := filepath.Join(dir, tableName+name)
name := c.ReplaceFileNamingTemplate(tableName) + filepath.Ext(tmplateName)
fileName := filepath.Join(dir, name)
results.Info.WriteString(fmt.Sprintf(" table: %-25s %s\n", tableName, fileName))
c.WriteTemplate(genTemplate, data, fileName)
}
return nil
}
func loadFile(src string) string {
// Read entire file content, giving us little control but
// making it very simple. No need to close the file.
content, err := ioutil.ReadFile(src)
if err != nil {
return fmt.Sprintf("error loading %s error: %v", src, err)
}
// Convert []byte to string and print to screen
text := string(content)
return text
}
// SwaggerInfoDetails swagger details
type SwaggerInfoDetails struct {
Version string
Host string
BasePath string
Title string
Description string
TOS string
ContactName string
ContactURL string
ContactEmail string
}
// Config for generating code
type Config struct {
SQLType string
SQLConnStr string
SQLDatabase string
Module string
ModelPackageName string
ModelFQPN string
AddJSONAnnotation bool
AddGormAnnotation bool
AddProtobufAnnotation bool
AddXMLAnnotation bool
AddDBAnnotation bool
UseGureguTypes bool
JSONNameFormat string
XMLNameFormat string
ProtobufNameFormat string
DaoPackageName string
DaoFQPN string
APIPackageName string
APIFQPN string
GrpcPackageName string
GrpcFQPN string
Swagger *SwaggerInfoDetails
ServerPort int
ServerHost string
ServerScheme string
ServerListen string
Verbose bool
OutDir string
Overwrite bool
LineEndingCRLF bool
CmdLine string
CmdLineWrapped string
CmdLineArgs []string
FileNamingTemplate string
ModelNamingTemplate string
FieldNamingTemplate string
ContextMap map[string]interface{}
TemplateLoader TemplateLoader
TableInfos map[string]*ModelInfo
FragmentsDir string
fragments *bytes.Buffer
}
// NewConfig create a new code config
func NewConfig(templateLoader TemplateLoader) *Config {
conf := &Config{
Swagger: &SwaggerInfoDetails{
Version: "1.0",
BasePath: "/",
Title: "Swagger Example API",
Description: "This is a sample server Petstore server.",
TOS: "",
ContactName: "",
ContactURL: "",
ContactEmail: "",
},
TemplateLoader: templateLoader,
}
conf.CmdLineArgs = os.Args
conf.CmdLineWrapped = strings.Join(os.Args, " \\\n ")
conf.CmdLine = strings.Join(os.Args, " ")
conf.ContextMap = make(map[string]interface{})
conf.FileNamingTemplate = "{{.}}"
conf.ModelNamingTemplate = "{{FmtFieldName .}}"
conf.FieldNamingTemplate = "{{FmtFieldName (stringifyFirstChar .) }}"
outDir := "."
module := "github.com/alexj212/test"
modelPackageName := "model"
daoPackageName := "dao"
apiPackageName := "api"
conf.ModelPackageName = modelPackageName
conf.DaoPackageName = daoPackageName
conf.APIPackageName = apiPackageName
conf.AddJSONAnnotation = true
conf.AddXMLAnnotation = true
conf.AddGormAnnotation = true
conf.AddProtobufAnnotation = true
conf.AddDBAnnotation = true
conf.UseGureguTypes = false
conf.JSONNameFormat = "snake"
conf.XMLNameFormat = "snake"
conf.ProtobufNameFormat = "snake"
conf.Verbose = false
conf.OutDir = outDir
conf.Overwrite = true
conf.ServerPort = 8080
conf.ServerHost = "127.0.0.1"
conf.ServerScheme = "http"
conf.ServerListen = ":8080"
conf.Overwrite = true
conf.Module = module
conf.ModelFQPN = module + "/" + modelPackageName
conf.DaoFQPN = module + "/" + daoPackageName
conf.APIFQPN = module + "/" + apiPackageName
if conf.ServerPort == 80 {
conf.Swagger.Host = conf.ServerHost
} else {
conf.Swagger.Host = fmt.Sprintf("%s:%d", conf.ServerHost, conf.ServerPort)
}
return conf
}