Add the ability to fully customize jet generated files.
This commit is contained in:
parent
caa81930dc
commit
8864667f47
40 changed files with 2274 additions and 882 deletions
223
generator/template/file_templates.go
Normal file
223
generator/template/file_templates.go
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
package template
|
||||
|
||||
var autoGenWarningTemplate = `
|
||||
//
|
||||
// Code generated by go-jet DO NOT EDIT.
|
||||
//
|
||||
// WARNING: Changes to this file may cause incorrect behavior
|
||||
// and will be lost if the code is regenerated
|
||||
//
|
||||
|
||||
`
|
||||
|
||||
var tableSQLBuilderTemplate = `
|
||||
{{define "column-list" -}}
|
||||
{{- range $i, $c := . }}
|
||||
{{- $field := columnField $c}}
|
||||
{{- if gt $i 0 }}, {{end}}{{$field.Name}}Column
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
|
||||
package {{package}}
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/{{dialect.PackageName}}"
|
||||
)
|
||||
|
||||
var {{tableTemplate.InstanceName}} = new{{tableTemplate.TypeName}}("{{schemaName}}", "{{.Name}}", "")
|
||||
|
||||
type {{tableTemplate.TypeName}} struct {
|
||||
{{dialect.PackageName}}.Table
|
||||
|
||||
//Columns
|
||||
{{- range $i, $c := .Columns}}
|
||||
{{- $field := columnField $c}}
|
||||
{{$field.Name}} {{dialect.PackageName}}.Column{{$field.Type}}
|
||||
{{- end}}
|
||||
|
||||
AllColumns {{dialect.PackageName}}.ColumnList
|
||||
MutableColumns {{dialect.PackageName}}.ColumnList
|
||||
}
|
||||
|
||||
// AS creates new {{tableTemplate.TypeName}} with assigned alias
|
||||
func (a {{tableTemplate.TypeName}}) AS(alias string) {{tableTemplate.TypeName}} {
|
||||
return new{{tableTemplate.TypeName}}(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new {{tableTemplate.TypeName}} with assigned schema name
|
||||
func (a {{tableTemplate.TypeName}}) FromSchema(schemaName string) {{tableTemplate.TypeName}} {
|
||||
return new{{tableTemplate.TypeName}}(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
func new{{tableTemplate.TypeName}}(schemaName, tableName, alias string) {{tableTemplate.TypeName}} {
|
||||
var (
|
||||
{{- range $i, $c := .Columns}}
|
||||
{{- $field := columnField $c}}
|
||||
{{$field.Name}}Column = {{dialect.PackageName}}.{{$field.Type}}Column("{{$c.Name}}")
|
||||
{{- end}}
|
||||
allColumns = {{dialect.PackageName}}.ColumnList{ {{template "column-list" .Columns}} }
|
||||
mutableColumns = {{dialect.PackageName}}.ColumnList{ {{template "column-list" .MutableColumns}} }
|
||||
)
|
||||
|
||||
return {{tableTemplate.TypeName}}{
|
||||
Table: {{dialect.PackageName}}.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
{{- range $i, $c := .Columns}}
|
||||
{{- $field := columnField $c}}
|
||||
{{$field.Name}}: {{$field.Name}}Column,
|
||||
{{- end}}
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var tablePostgreSQLBuilderTemplate = `
|
||||
{{define "column-list" -}}
|
||||
{{- range $i, $c := . }}
|
||||
{{- $field := columnField $c}}
|
||||
{{- if gt $i 0 }}, {{end}}{{$field.Name}}Column
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
|
||||
package {{package}}
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/{{dialect.PackageName}}"
|
||||
)
|
||||
|
||||
var {{tableTemplate.InstanceName}} = new{{tableTemplate.TypeName}}("{{schemaName}}", "{{.Name}}", "")
|
||||
|
||||
type {{structImplName}} struct {
|
||||
{{dialect.PackageName}}.Table
|
||||
|
||||
//Columns
|
||||
{{- range $i, $c := .Columns}}
|
||||
{{- $field := columnField $c}}
|
||||
{{$field.Name}} {{dialect.PackageName}}.Column{{$field.Type}}
|
||||
{{- end}}
|
||||
|
||||
AllColumns {{dialect.PackageName}}.ColumnList
|
||||
MutableColumns {{dialect.PackageName}}.ColumnList
|
||||
}
|
||||
|
||||
type {{tableTemplate.TypeName}} struct {
|
||||
{{structImplName}}
|
||||
|
||||
EXCLUDED {{structImplName}}
|
||||
}
|
||||
|
||||
// AS creates new {{tableTemplate.TypeName}} with assigned alias
|
||||
func (a {{tableTemplate.TypeName}}) AS(alias string) *{{tableTemplate.TypeName}} {
|
||||
return new{{tableTemplate.TypeName}}(a.SchemaName(), a.TableName(), alias)
|
||||
}
|
||||
|
||||
// Schema creates new {{tableTemplate.TypeName}} with assigned schema name
|
||||
func (a {{tableTemplate.TypeName}}) FromSchema(schemaName string) *{{tableTemplate.TypeName}} {
|
||||
return new{{tableTemplate.TypeName}}(schemaName, a.TableName(), a.Alias())
|
||||
}
|
||||
|
||||
func new{{tableTemplate.TypeName}}(schemaName, tableName, alias string) *{{tableTemplate.TypeName}} {
|
||||
return &{{tableTemplate.TypeName}}{
|
||||
{{structImplName}}: new{{tableTemplate.TypeName}}Impl(schemaName, tableName, alias),
|
||||
EXCLUDED: new{{tableTemplate.TypeName}}Impl("", "excluded", ""),
|
||||
}
|
||||
}
|
||||
|
||||
func new{{tableTemplate.TypeName}}Impl(schemaName, tableName, alias string) {{structImplName}} {
|
||||
var (
|
||||
{{- range $i, $c := .Columns}}
|
||||
{{- $field := columnField $c}}
|
||||
{{$field.Name}}Column = {{dialect.PackageName}}.{{$field.Type}}Column("{{$c.Name}}")
|
||||
{{- end}}
|
||||
allColumns = {{dialect.PackageName}}.ColumnList{ {{template "column-list" .Columns}} }
|
||||
mutableColumns = {{dialect.PackageName}}.ColumnList{ {{template "column-list" .MutableColumns}} }
|
||||
)
|
||||
|
||||
return {{structImplName}}{
|
||||
Table: {{dialect.PackageName}}.NewTable(schemaName, tableName, alias, allColumns...),
|
||||
|
||||
//Columns
|
||||
{{- range $i, $c := .Columns}}
|
||||
{{- $field := columnField $c}}
|
||||
{{$field.Name}}: {{$field.Name}}Column,
|
||||
{{- end}}
|
||||
|
||||
AllColumns: allColumns,
|
||||
MutableColumns: mutableColumns,
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var tableModelFileTemplate = `package {{package}}
|
||||
|
||||
{{ with modelImports }}
|
||||
import (
|
||||
{{- range .}}
|
||||
"{{.}}"
|
||||
{{- end}}
|
||||
)
|
||||
{{end}}
|
||||
|
||||
{{$modelTableTemplate := tableTemplate}}
|
||||
type {{$modelTableTemplate.TypeName}} struct {
|
||||
{{- range .Columns}}
|
||||
{{- $field := structField .}}
|
||||
{{$field.Name}} {{$field.Type.Name}} ` + "{{$field.TagsString}}" + `
|
||||
{{- end}}
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
var enumSQLBuilderTemplate = `package {{package}}
|
||||
|
||||
import "github.com/go-jet/jet/v2/{{dialect.PackageName}}"
|
||||
|
||||
var {{enumTemplate.InstanceName}} = &struct {
|
||||
{{- range $index, $value := .Values}}
|
||||
{{enumValueName $value}} {{dialect.PackageName}}.StringExpression
|
||||
{{- end}}
|
||||
} {
|
||||
{{- range $index, $value := .Values}}
|
||||
{{enumValueName $value}}: {{dialect.PackageName}}.NewEnumValue("{{$value}}"),
|
||||
{{- end}}
|
||||
}
|
||||
`
|
||||
|
||||
var enumModelTemplate = `package {{package}}
|
||||
{{- $enumTemplate := enumTemplate}}
|
||||
|
||||
import "errors"
|
||||
|
||||
type {{$enumTemplate.TypeName}} string
|
||||
|
||||
const (
|
||||
{{- range $_, $value := .Values}}
|
||||
{{valueName $value}} {{$enumTemplate.TypeName}} = "{{$value}}"
|
||||
{{- end}}
|
||||
)
|
||||
|
||||
func (e *{{$enumTemplate.TypeName}}) Scan(value interface{}) error {
|
||||
if v, ok := value.(string); !ok {
|
||||
return errors.New("jet: Invalid scan value for {{$enumTemplate.TypeName}} enum. Enum value has to be of type string")
|
||||
} else {
|
||||
switch string(v) {
|
||||
{{- range $_, $value := .Values}}
|
||||
case "{{$value}}":
|
||||
*e = {{valueName $value}}
|
||||
{{- end}}
|
||||
default:
|
||||
return errors.New("jet: Invalid scan value '" + string(v) + "' for {{$enumTemplate.TypeName}} enum")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (e {{$enumTemplate.TypeName}}) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
`
|
||||
60
generator/template/generator_template.go
Normal file
60
generator/template/generator_template.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
package template
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/internal/jet"
|
||||
)
|
||||
|
||||
// Template is generator template used for file generation
|
||||
type Template struct {
|
||||
Dialect jet.Dialect
|
||||
Schema func(schemaMetaData metadata.Schema) Schema
|
||||
}
|
||||
|
||||
// Default is default generator template implementation
|
||||
func Default(dialect jet.Dialect) Template {
|
||||
return Template{
|
||||
Dialect: dialect,
|
||||
Schema: DefaultSchema,
|
||||
}
|
||||
}
|
||||
|
||||
// UseSchema replaces current schema generate function with a new implementation and returns new generator template
|
||||
func (t Template) UseSchema(schemaFunc func(schemaMetaData metadata.Schema) Schema) Template {
|
||||
t.Schema = schemaFunc
|
||||
return t
|
||||
}
|
||||
|
||||
// Schema is schema generator template used to generate schema(model and sql builder) files
|
||||
type Schema struct {
|
||||
Path string
|
||||
Model Model
|
||||
SQLBuilder SQLBuilder
|
||||
}
|
||||
|
||||
// UsePath replaces path and returns new schema template
|
||||
func (s Schema) UsePath(path string) Schema {
|
||||
s.Path = path
|
||||
return s
|
||||
}
|
||||
|
||||
// UseModel returns new schema template with replaced template for model files generation
|
||||
func (s Schema) UseModel(model Model) Schema {
|
||||
s.Model = model
|
||||
return s
|
||||
}
|
||||
|
||||
// UseSQLBuilder returns new schema with replaced template for sql builder files generation
|
||||
func (s Schema) UseSQLBuilder(sqlBuilder SQLBuilder) Schema {
|
||||
s.SQLBuilder = sqlBuilder
|
||||
return s
|
||||
}
|
||||
|
||||
// DefaultSchema returns default schema template implementation
|
||||
func DefaultSchema(schemaMetaData metadata.Schema) Schema {
|
||||
return Schema{
|
||||
Path: schemaMetaData.Name,
|
||||
Model: DefaultModel(),
|
||||
SQLBuilder: DefaultSQLBuilder(),
|
||||
}
|
||||
}
|
||||
327
generator/template/model_template.go
Normal file
327
generator/template/model_template.go
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/internal/utils"
|
||||
"github.com/google/uuid"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Model is template for model files generation
|
||||
type Model struct {
|
||||
Skip bool
|
||||
Path string
|
||||
Table func(table metadata.Table) TableModel
|
||||
View func(table metadata.Table) ViewModel
|
||||
Enum func(enum metadata.Enum) EnumModel
|
||||
}
|
||||
|
||||
// PackageName returns package name of model types
|
||||
func (m Model) PackageName() string {
|
||||
return path.Base(m.Path)
|
||||
}
|
||||
|
||||
// UsePath returns new Model template with replaced file path
|
||||
func (m Model) UsePath(path string) Model {
|
||||
m.Path = path
|
||||
return m
|
||||
}
|
||||
|
||||
// UseTable returns new Model template with replaced template for table model files generation
|
||||
func (m Model) UseTable(tableModelFunc func(table metadata.Table) TableModel) Model {
|
||||
m.Table = tableModelFunc
|
||||
return m
|
||||
}
|
||||
|
||||
// UseView returns new Model template with replaced template for view model files generation
|
||||
func (m Model) UseView(tableModelFunc func(table metadata.Table) TableModel) Model {
|
||||
m.View = tableModelFunc
|
||||
return m
|
||||
}
|
||||
|
||||
// UseEnum returns new Model template with replaced template for enum model files generation
|
||||
func (m Model) UseEnum(enumFunc func(enumMetaData metadata.Enum) EnumModel) Model {
|
||||
m.Enum = enumFunc
|
||||
return m
|
||||
}
|
||||
|
||||
// DefaultModel returns default Model template implementation
|
||||
func DefaultModel() Model {
|
||||
return Model{
|
||||
Skip: false,
|
||||
Path: "/model",
|
||||
Table: DefaultTableModel,
|
||||
View: DefaultViewModel,
|
||||
Enum: DefaultEnumModel,
|
||||
}
|
||||
}
|
||||
|
||||
// TableModel is template for table model files generation
|
||||
type TableModel struct {
|
||||
Skip bool
|
||||
FileName string
|
||||
TypeName string
|
||||
Field func(columnMetaData metadata.Column) TableModelField
|
||||
}
|
||||
|
||||
// ViewModel is template for view model files generation
|
||||
type ViewModel = TableModel
|
||||
|
||||
// DefaultViewModel is default view template implementation
|
||||
var DefaultViewModel = DefaultTableModel
|
||||
|
||||
// DefaultTableModel is default table template implementation
|
||||
func DefaultTableModel(tableMetaData metadata.Table) TableModel {
|
||||
return TableModel{
|
||||
FileName: utils.ToGoFileName(tableMetaData.Name),
|
||||
TypeName: utils.ToGoIdentifier(tableMetaData.Name),
|
||||
Field: DefaultTableModelField,
|
||||
}
|
||||
}
|
||||
|
||||
// UseFileName returns new TableModel with new file name set
|
||||
func (t TableModel) UseFileName(fileName string) TableModel {
|
||||
t.FileName = fileName
|
||||
return t
|
||||
}
|
||||
|
||||
// UseTypeName returns new TableModel with new type name set
|
||||
func (t TableModel) UseTypeName(typeName string) TableModel {
|
||||
t.TypeName = typeName
|
||||
return t
|
||||
}
|
||||
|
||||
// UseField returns new TableModel with new TableModelField template function
|
||||
func (t TableModel) UseField(structFieldFunc func(columnMetaData metadata.Column) TableModelField) TableModel {
|
||||
t.Field = structFieldFunc
|
||||
return t
|
||||
}
|
||||
|
||||
func getTableModelImports(modelType TableModel, tableMetaData metadata.Table) []string {
|
||||
importPaths := map[string]bool{}
|
||||
for _, columnMetaData := range tableMetaData.Columns {
|
||||
field := modelType.Field(columnMetaData)
|
||||
importPath := field.Type.ImportPath
|
||||
|
||||
if importPath != "" {
|
||||
importPaths[importPath] = true
|
||||
}
|
||||
}
|
||||
|
||||
var ret []string
|
||||
for importPath := range importPaths {
|
||||
ret = append(ret, importPath)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// EnumModel is template for enum model files generation
|
||||
type EnumModel struct {
|
||||
Skip bool
|
||||
FileName string
|
||||
TypeName string
|
||||
ValueName func(value string) string
|
||||
}
|
||||
|
||||
// UseFileName returns new EnumModel with new file name set
|
||||
func (em EnumModel) UseFileName(fileName string) EnumModel {
|
||||
em.FileName = fileName
|
||||
return em
|
||||
}
|
||||
|
||||
// UseTypeName returns new EnumModel with new type name set
|
||||
func (em EnumModel) UseTypeName(typeName string) EnumModel {
|
||||
em.TypeName = typeName
|
||||
return em
|
||||
}
|
||||
|
||||
// DefaultEnumModel returns default implementation for EnumModel
|
||||
func DefaultEnumModel(enumMetaData metadata.Enum) EnumModel {
|
||||
typeName := utils.ToGoIdentifier(enumMetaData.Name)
|
||||
|
||||
return EnumModel{
|
||||
FileName: utils.ToGoFileName(enumMetaData.Name),
|
||||
TypeName: typeName,
|
||||
ValueName: func(value string) string {
|
||||
return typeName + "_" + utils.ToGoIdentifier(value)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TableModelField is template for table model field generation
|
||||
type TableModelField struct {
|
||||
Name string
|
||||
Type Type
|
||||
Tags []string
|
||||
}
|
||||
|
||||
// DefaultTableModelField returns default TableModelField implementation
|
||||
func DefaultTableModelField(columnMetaData metadata.Column) TableModelField {
|
||||
var tags []string
|
||||
|
||||
if columnMetaData.IsPrimaryKey {
|
||||
tags = append(tags, `sql:"primary_key"`)
|
||||
}
|
||||
|
||||
return TableModelField{
|
||||
Name: utils.ToGoIdentifier(columnMetaData.Name),
|
||||
Type: getType(columnMetaData),
|
||||
Tags: tags,
|
||||
}
|
||||
}
|
||||
|
||||
// UseType returns new TypeModelField with a new field type set
|
||||
func (f TableModelField) UseType(t Type) TableModelField {
|
||||
f.Type = t
|
||||
return f
|
||||
}
|
||||
|
||||
// UseName returns new TableModelField implementation with new field name set
|
||||
func (f TableModelField) UseName(name string) TableModelField {
|
||||
f.Name = name
|
||||
return f
|
||||
}
|
||||
|
||||
// UseTags returns new TableModelField implementation with additional tags added.
|
||||
func (f TableModelField) UseTags(tags ...string) TableModelField {
|
||||
f.Tags = append(f.Tags, tags...)
|
||||
return f
|
||||
}
|
||||
|
||||
// TagsString returns tags string representation
|
||||
func (f TableModelField) TagsString() string {
|
||||
if len(f.Tags) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("`%s`", strings.Join(f.Tags, " "))
|
||||
}
|
||||
|
||||
// Type represents type of the struct field
|
||||
type Type struct {
|
||||
ImportPath string
|
||||
Name string
|
||||
}
|
||||
|
||||
// NewType creates new type for dummy object
|
||||
func NewType(dummyObject interface{}) Type {
|
||||
return Type{
|
||||
ImportPath: getImportPath(dummyObject),
|
||||
Name: getTypeName(dummyObject),
|
||||
}
|
||||
}
|
||||
|
||||
func getTypeName(t interface{}) string {
|
||||
typeStr := reflect.TypeOf(t).String()
|
||||
typeStr = strings.Replace(typeStr, "[]uint8", "[]byte", -1)
|
||||
|
||||
return typeStr
|
||||
}
|
||||
|
||||
func getImportPath(dummyData interface{}) string {
|
||||
dataType := reflect.TypeOf(dummyData)
|
||||
if dataType.Kind() == reflect.Ptr {
|
||||
return dataType.Elem().PkgPath()
|
||||
}
|
||||
return dataType.PkgPath()
|
||||
}
|
||||
|
||||
func getType(columnMetadata metadata.Column) Type {
|
||||
userDefinedType := getUserDefinedType(columnMetadata)
|
||||
|
||||
if userDefinedType != "" {
|
||||
if columnMetadata.IsNullable {
|
||||
return Type{Name: "*" + userDefinedType}
|
||||
}
|
||||
return Type{Name: userDefinedType}
|
||||
}
|
||||
|
||||
return NewType(getGoType(columnMetadata))
|
||||
}
|
||||
|
||||
func getUserDefinedType(column metadata.Column) string {
|
||||
switch column.DataType.Kind {
|
||||
case metadata.EnumType:
|
||||
return utils.ToGoIdentifier(column.DataType.Name)
|
||||
case metadata.UserDefinedType, metadata.ArrayType:
|
||||
return "string"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func getGoType(column metadata.Column) interface{} {
|
||||
defaultGoType := toGoType(column)
|
||||
|
||||
if column.IsNullable {
|
||||
return reflect.New(reflect.TypeOf(defaultGoType)).Interface()
|
||||
}
|
||||
|
||||
return defaultGoType
|
||||
}
|
||||
|
||||
// toGoType returns model type for column info.
|
||||
func toGoType(column metadata.Column) interface{} {
|
||||
switch column.DataType.Name {
|
||||
case "USER-DEFINED", "enum":
|
||||
return ""
|
||||
case "boolean", "bool":
|
||||
return false
|
||||
case "tinyint":
|
||||
if column.DataType.IsUnsigned {
|
||||
return uint8(0)
|
||||
}
|
||||
return int8(0)
|
||||
case "smallint", "int2",
|
||||
"year":
|
||||
if column.DataType.IsUnsigned {
|
||||
return uint16(0)
|
||||
}
|
||||
return int16(0)
|
||||
case "integer", "int4",
|
||||
"mediumint", "int": //MySQL
|
||||
if column.DataType.IsUnsigned {
|
||||
return uint32(0)
|
||||
}
|
||||
return int32(0)
|
||||
case "bigint", "int8":
|
||||
if column.DataType.IsUnsigned {
|
||||
return uint64(0)
|
||||
}
|
||||
return int64(0)
|
||||
case "date",
|
||||
"timestamp without time zone", "timestamp",
|
||||
"timestamp with time zone", "timestamptz",
|
||||
"time without time zone", "time",
|
||||
"time with time zone", "timetz",
|
||||
"datetime": // MySQL
|
||||
return time.Time{}
|
||||
case "bytea",
|
||||
"binary", "varbinary", "tinyblob", "blob", "mediumblob", "longblob": //MySQL
|
||||
return []byte("")
|
||||
case "text",
|
||||
"character", "bpchar",
|
||||
"character varying", "varchar",
|
||||
"tsvector", "bit", "bit varying", "varbit",
|
||||
"money", "json", "jsonb",
|
||||
"xml", "point", "interval", "line", "ARRAY",
|
||||
"char", "tinytext", "mediumtext", "longtext": // MySQL
|
||||
return ""
|
||||
case "real", "float4":
|
||||
return float32(0.0)
|
||||
case "numeric", "decimal",
|
||||
"double precision", "float8", "float",
|
||||
"double": // MySQL
|
||||
return float64(0.0)
|
||||
case "uuid":
|
||||
return uuid.UUID{}
|
||||
default:
|
||||
fmt.Println("- [Model ] Unsupported sql column '" + column.Name + " " + column.DataType.Name + "', using string instead.")
|
||||
return ""
|
||||
}
|
||||
}
|
||||
45
generator/template/model_template_test.go
Normal file
45
generator/template/model_template_test.go
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package template
|
||||
|
||||
import (
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_TableModelField(t *testing.T) {
|
||||
require.Equal(t, DefaultTableModelField(metadata.Column{
|
||||
Name: "col_name",
|
||||
IsPrimaryKey: true,
|
||||
IsNullable: true,
|
||||
DataType: metadata.DataType{
|
||||
Name: "smallint",
|
||||
Kind: "base",
|
||||
IsUnsigned: true,
|
||||
},
|
||||
}), TableModelField{
|
||||
Name: "ColName",
|
||||
Type: Type{
|
||||
ImportPath: "",
|
||||
Name: "*uint16",
|
||||
},
|
||||
Tags: []string{"sql:\"primary_key\""},
|
||||
})
|
||||
|
||||
require.Equal(t, DefaultTableModelField(metadata.Column{
|
||||
Name: "time_column_1",
|
||||
IsPrimaryKey: false,
|
||||
IsNullable: true,
|
||||
DataType: metadata.DataType{
|
||||
Name: "timestamp with time zone",
|
||||
Kind: "base",
|
||||
IsUnsigned: false,
|
||||
},
|
||||
}), TableModelField{
|
||||
Name: "TimeColumn1",
|
||||
Type: Type{
|
||||
ImportPath: "time",
|
||||
Name: "*time.Time",
|
||||
},
|
||||
Tags: nil,
|
||||
})
|
||||
}
|
||||
269
generator/template/process.go
Normal file
269
generator/template/process.go
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/internal/jet"
|
||||
"github.com/go-jet/jet/v2/internal/utils"
|
||||
"github.com/go-jet/jet/v2/internal/utils/throw"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// ProcessSchema will process schema metadata and constructs go files using generator Template
|
||||
func ProcessSchema(dirPath string, schemaMetaData metadata.Schema, generatorTemplate Template) {
|
||||
if schemaMetaData.IsEmpty() {
|
||||
return
|
||||
}
|
||||
|
||||
schemaTemplate := generatorTemplate.Schema(schemaMetaData)
|
||||
schemaPath := path.Join(dirPath, schemaTemplate.Path)
|
||||
|
||||
fmt.Println("Destination directory:", schemaPath)
|
||||
fmt.Println("Cleaning up destination directory...")
|
||||
err := utils.CleanUpGeneratedFiles(schemaPath)
|
||||
throw.OnError(err)
|
||||
|
||||
processModel(schemaPath, schemaMetaData, schemaTemplate)
|
||||
processSQLBuilder(schemaPath, generatorTemplate.Dialect, schemaMetaData, schemaTemplate)
|
||||
}
|
||||
|
||||
func processModel(dirPath string, schemaMetaData metadata.Schema, schemaTemplate Schema) {
|
||||
modelTemplate := schemaTemplate.Model
|
||||
|
||||
if modelTemplate.Skip {
|
||||
fmt.Println("Skipping the generation of model types.")
|
||||
return
|
||||
}
|
||||
|
||||
modelDirPath := path.Join(dirPath, modelTemplate.Path)
|
||||
|
||||
err := utils.EnsureDirPath(modelDirPath)
|
||||
throw.OnError(err)
|
||||
|
||||
processTableModels("table", modelDirPath, schemaMetaData.TablesMetaData, modelTemplate)
|
||||
processTableModels("view", modelDirPath, schemaMetaData.ViewsMetaData, modelTemplate)
|
||||
processEnumModels(modelDirPath, schemaMetaData.EnumsMetaData, modelTemplate)
|
||||
}
|
||||
|
||||
func processSQLBuilder(dirPath string, dialect jet.Dialect, schemaMetaData metadata.Schema, schemaTemplate Schema) {
|
||||
sqlBuilderTemplate := schemaTemplate.SQLBuilder
|
||||
|
||||
if sqlBuilderTemplate.Skip {
|
||||
fmt.Println("Skipping the generation of SQL Builder types.")
|
||||
return
|
||||
}
|
||||
|
||||
sqlBuilderPath := path.Join(dirPath, sqlBuilderTemplate.Path)
|
||||
|
||||
processTableSQLBuilder("table", sqlBuilderPath, dialect, schemaMetaData, schemaMetaData.TablesMetaData, sqlBuilderTemplate)
|
||||
processTableSQLBuilder("view", sqlBuilderPath, dialect, schemaMetaData, schemaMetaData.ViewsMetaData, sqlBuilderTemplate)
|
||||
processEnumSQLBuilder(sqlBuilderPath, dialect, schemaMetaData.EnumsMetaData, sqlBuilderTemplate)
|
||||
}
|
||||
|
||||
func processEnumSQLBuilder(dirPath string, dialect jet.Dialect, enumsMetaData []metadata.Enum, sqlBuilder SQLBuilder) {
|
||||
if len(enumsMetaData) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Generating enum sql builder files\n")
|
||||
|
||||
for _, enumMetaData := range enumsMetaData {
|
||||
enumTemplate := sqlBuilder.Enum(enumMetaData)
|
||||
|
||||
if enumTemplate.Skip {
|
||||
continue
|
||||
}
|
||||
|
||||
enumSQLBuilderPath := path.Join(dirPath, enumTemplate.Path)
|
||||
|
||||
err := utils.EnsureDirPath(enumSQLBuilderPath)
|
||||
throw.OnError(err)
|
||||
|
||||
text, err := generateTemplate(
|
||||
autoGenWarningTemplate+enumSQLBuilderTemplate,
|
||||
enumMetaData,
|
||||
template.FuncMap{
|
||||
"package": func() string {
|
||||
return enumTemplate.PackageName()
|
||||
},
|
||||
"dialect": func() jet.Dialect {
|
||||
return dialect
|
||||
},
|
||||
"enumTemplate": func() EnumSQLBuilder {
|
||||
return enumTemplate
|
||||
},
|
||||
"enumValueName": func(enumValue string) string {
|
||||
return enumTemplate.ValueName(enumValue)
|
||||
},
|
||||
})
|
||||
throw.OnError(err)
|
||||
|
||||
err = utils.SaveGoFile(enumSQLBuilderPath, enumTemplate.FileName, text)
|
||||
throw.OnError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func processTableSQLBuilder(fileTypes, dirPath string,
|
||||
dialect jet.Dialect,
|
||||
schemaMetaData metadata.Schema,
|
||||
tablesMetaData []metadata.Table,
|
||||
sqlBuilderTemplate SQLBuilder) {
|
||||
|
||||
if len(tablesMetaData) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Generating %s sql builder files\n", fileTypes)
|
||||
|
||||
for _, tableMetaData := range tablesMetaData {
|
||||
|
||||
var tableSQLBuilderTemplate TableSQLBuilder
|
||||
|
||||
if fileTypes == "view" {
|
||||
tableSQLBuilderTemplate = sqlBuilderTemplate.View(tableMetaData)
|
||||
} else {
|
||||
tableSQLBuilderTemplate = sqlBuilderTemplate.Table(tableMetaData)
|
||||
}
|
||||
|
||||
if tableSQLBuilderTemplate.Skip {
|
||||
continue
|
||||
}
|
||||
|
||||
tableSQLBuilderPath := path.Join(dirPath, tableSQLBuilderTemplate.Path)
|
||||
|
||||
err := utils.EnsureDirPath(tableSQLBuilderPath)
|
||||
throw.OnError(err)
|
||||
|
||||
text, err := generateTemplate(
|
||||
autoGenWarningTemplate+getTableSQLBuilderTemplate(dialect),
|
||||
tableMetaData,
|
||||
template.FuncMap{
|
||||
"package": func() string {
|
||||
return tableSQLBuilderTemplate.PackageName()
|
||||
},
|
||||
"dialect": func() jet.Dialect {
|
||||
return dialect
|
||||
},
|
||||
"schemaName": func() string {
|
||||
return schemaMetaData.Name
|
||||
},
|
||||
"tableTemplate": func() TableSQLBuilder {
|
||||
return tableSQLBuilderTemplate
|
||||
},
|
||||
"structImplName": func() string { // postgres only
|
||||
structName := tableSQLBuilderTemplate.TypeName
|
||||
return string(strings.ToLower(structName)[0]) + structName[1:]
|
||||
},
|
||||
"columnField": func(columnMetaData metadata.Column) TableSQLBuilderColumn {
|
||||
return tableSQLBuilderTemplate.Column(columnMetaData)
|
||||
},
|
||||
})
|
||||
throw.OnError(err)
|
||||
|
||||
err = utils.SaveGoFile(tableSQLBuilderPath, tableSQLBuilderTemplate.FileName, text)
|
||||
throw.OnError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getTableSQLBuilderTemplate(dialect jet.Dialect) string {
|
||||
if dialect.Name() == "PostgreSQL" {
|
||||
return tablePostgreSQLBuilderTemplate
|
||||
}
|
||||
|
||||
return tableSQLBuilderTemplate
|
||||
}
|
||||
|
||||
func processTableModels(fileTypes, modelDirPath string, tablesMetaData []metadata.Table, modelTemplate Model) {
|
||||
if len(tablesMetaData) == 0 {
|
||||
return
|
||||
}
|
||||
fmt.Printf("Generating %s model files...\n", fileTypes)
|
||||
|
||||
for _, tableMetaData := range tablesMetaData {
|
||||
var tableTemplate TableModel
|
||||
|
||||
if fileTypes == "table" {
|
||||
tableTemplate = modelTemplate.Table(tableMetaData)
|
||||
} else {
|
||||
tableTemplate = modelTemplate.View(tableMetaData)
|
||||
}
|
||||
|
||||
if tableTemplate.Skip {
|
||||
continue
|
||||
}
|
||||
|
||||
text, err := generateTemplate(
|
||||
autoGenWarningTemplate+tableModelFileTemplate,
|
||||
tableMetaData,
|
||||
template.FuncMap{
|
||||
"package": func() string {
|
||||
return modelTemplate.PackageName()
|
||||
},
|
||||
"modelImports": func() []string {
|
||||
return getTableModelImports(tableTemplate, tableMetaData)
|
||||
},
|
||||
"tableTemplate": func() TableModel {
|
||||
return tableTemplate
|
||||
},
|
||||
"structField": func(columnMetaData metadata.Column) TableModelField {
|
||||
return tableTemplate.Field(columnMetaData)
|
||||
},
|
||||
})
|
||||
throw.OnError(err)
|
||||
|
||||
err = utils.SaveGoFile(modelDirPath, tableTemplate.FileName, text)
|
||||
throw.OnError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func processEnumModels(modelDir string, enumsMetaData []metadata.Enum, modelTemplate Model) {
|
||||
if len(enumsMetaData) == 0 {
|
||||
return
|
||||
}
|
||||
fmt.Print("Generating enum model files...\n")
|
||||
|
||||
for _, enumMetaData := range enumsMetaData {
|
||||
enumTemplate := modelTemplate.Enum(enumMetaData)
|
||||
|
||||
if enumTemplate.Skip {
|
||||
continue
|
||||
}
|
||||
|
||||
text, err := generateTemplate(
|
||||
autoGenWarningTemplate+enumModelTemplate,
|
||||
enumMetaData,
|
||||
template.FuncMap{
|
||||
"package": func() string {
|
||||
return modelTemplate.PackageName()
|
||||
},
|
||||
"enumTemplate": func() EnumModel {
|
||||
return enumTemplate
|
||||
},
|
||||
"valueName": func(value string) string {
|
||||
return enumTemplate.ValueName(value)
|
||||
},
|
||||
})
|
||||
throw.OnError(err)
|
||||
|
||||
err = utils.SaveGoFile(modelDir, enumTemplate.FileName, text)
|
||||
throw.OnError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func generateTemplate(templateText string, templateData interface{}, funcMap template.FuncMap) ([]byte, error) {
|
||||
t, err := template.New("sqlBuilderTableTemplate").Funcs(funcMap).Parse(templateText)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := t.Execute(&buf, templateData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
225
generator/template/sql_builder_template.go
Normal file
225
generator/template/sql_builder_template.go
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-jet/jet/v2/generator/metadata"
|
||||
"github.com/go-jet/jet/v2/internal/utils"
|
||||
"path"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// SQLBuilder is template for generating sql builder files
|
||||
type SQLBuilder struct {
|
||||
Skip bool
|
||||
Path string
|
||||
Table func(table metadata.Table) TableSQLBuilder
|
||||
View func(view metadata.Table) TableSQLBuilder
|
||||
Enum func(enum metadata.Enum) EnumSQLBuilder
|
||||
}
|
||||
|
||||
// DefaultSQLBuilder returns default SQLBuilder implementation
|
||||
func DefaultSQLBuilder() SQLBuilder {
|
||||
return SQLBuilder{
|
||||
Path: "",
|
||||
Table: DefaultTableSQLBuilder,
|
||||
View: DefaultViewSQLBuilder,
|
||||
Enum: DefaultEnumSQLBuilder,
|
||||
}
|
||||
}
|
||||
|
||||
// UsePath returns new SQLBuilder with new relative path set
|
||||
func (sb SQLBuilder) UsePath(path string) SQLBuilder {
|
||||
sb.Path = path
|
||||
return sb
|
||||
}
|
||||
|
||||
// UseTable returns new SQLBuilder with new TableSQLBuilder template function set
|
||||
func (sb SQLBuilder) UseTable(tableFunc func(table metadata.Table) TableSQLBuilder) SQLBuilder {
|
||||
sb.Table = tableFunc
|
||||
return sb
|
||||
}
|
||||
|
||||
// UseView returns new SQLBuilder with new ViewSQLBuilder template function set
|
||||
func (sb SQLBuilder) UseView(viewFunc func(table metadata.Table) ViewSQLBuilder) SQLBuilder {
|
||||
sb.View = viewFunc
|
||||
return sb
|
||||
}
|
||||
|
||||
// UseEnum returns new SQLBuilder with new EnumSQLBuilder template function set
|
||||
func (sb SQLBuilder) UseEnum(enumFunc func(enum metadata.Enum) EnumSQLBuilder) SQLBuilder {
|
||||
sb.Enum = enumFunc
|
||||
return sb
|
||||
}
|
||||
|
||||
// TableSQLBuilder is template for generating table SQLBuilder files
|
||||
type TableSQLBuilder struct {
|
||||
Skip bool
|
||||
Path string
|
||||
FileName string
|
||||
InstanceName string
|
||||
TypeName string
|
||||
Column func(columnMetaData metadata.Column) TableSQLBuilderColumn
|
||||
}
|
||||
|
||||
// ViewSQLBuilder is template for generating view SQLBuilder files
|
||||
type ViewSQLBuilder = TableSQLBuilder
|
||||
|
||||
// DefaultTableSQLBuilder returns default implementation for TableSQLBuilder
|
||||
func DefaultTableSQLBuilder(tableMetaData metadata.Table) TableSQLBuilder {
|
||||
return TableSQLBuilder{
|
||||
Path: "/table",
|
||||
FileName: utils.ToGoFileName(tableMetaData.Name),
|
||||
InstanceName: utils.ToGoIdentifier(tableMetaData.Name),
|
||||
TypeName: utils.ToGoIdentifier(tableMetaData.Name) + "Table",
|
||||
Column: DefaultTableSQLBuilderColumn,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultViewSQLBuilder returns default implementation for ViewSQLBuilder
|
||||
func DefaultViewSQLBuilder(viewMetaData metadata.Table) ViewSQLBuilder {
|
||||
tableSQLBuilder := DefaultTableSQLBuilder(viewMetaData)
|
||||
tableSQLBuilder.Path = "/view"
|
||||
return tableSQLBuilder
|
||||
}
|
||||
|
||||
// PackageName returns package name of table sql builder types
|
||||
func (tb TableSQLBuilder) PackageName() string {
|
||||
return path.Base(tb.Path)
|
||||
}
|
||||
|
||||
// UsePath returns new TableSQLBuilder with new relative path set
|
||||
func (tb TableSQLBuilder) UsePath(path string) TableSQLBuilder {
|
||||
tb.Path = path
|
||||
return tb
|
||||
}
|
||||
|
||||
// UseFileName returns new TableSQLBuilder with new file name set
|
||||
func (tb TableSQLBuilder) UseFileName(name string) TableSQLBuilder {
|
||||
tb.FileName = name
|
||||
return tb
|
||||
}
|
||||
|
||||
// UseInstanceName returns new TableSQLBuilder with new instance name set
|
||||
func (tb TableSQLBuilder) UseInstanceName(name string) TableSQLBuilder {
|
||||
tb.InstanceName = name
|
||||
return tb
|
||||
}
|
||||
|
||||
// UseTypeName returns new TableSQLBuilder with new type name set
|
||||
func (tb TableSQLBuilder) UseTypeName(name string) TableSQLBuilder {
|
||||
tb.TypeName = name
|
||||
return tb
|
||||
}
|
||||
|
||||
// UseColumn returns new TableSQLBuilder with new column template function set
|
||||
func (tb TableSQLBuilder) UseColumn(columnsFunc func(column metadata.Column) TableSQLBuilderColumn) TableSQLBuilder {
|
||||
tb.Column = columnsFunc
|
||||
return tb
|
||||
}
|
||||
|
||||
// TableSQLBuilderColumn is template for table sql builder column
|
||||
type TableSQLBuilderColumn struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
// DefaultTableSQLBuilderColumn returns default implementation of TableSQLBuilderColumn
|
||||
func DefaultTableSQLBuilderColumn(columnMetaData metadata.Column) TableSQLBuilderColumn {
|
||||
return TableSQLBuilderColumn{
|
||||
Name: utils.ToGoIdentifier(columnMetaData.Name),
|
||||
Type: getSqlBuilderColumnType(columnMetaData),
|
||||
}
|
||||
}
|
||||
|
||||
// getSqlBuilderColumnType returns type of jet sql builder column
|
||||
func getSqlBuilderColumnType(columnMetaData metadata.Column) string {
|
||||
if columnMetaData.DataType.Kind != metadata.BaseType {
|
||||
return "String"
|
||||
}
|
||||
|
||||
switch columnMetaData.DataType.Name {
|
||||
case "boolean":
|
||||
return "Bool"
|
||||
case "smallint", "integer", "bigint",
|
||||
"tinyint", "mediumint", "int", "year": //MySQL
|
||||
return "Integer"
|
||||
case "date":
|
||||
return "Date"
|
||||
case "timestamp without time zone",
|
||||
"timestamp", "datetime": //MySQL:
|
||||
return "Timestamp"
|
||||
case "timestamp with time zone":
|
||||
return "Timestampz"
|
||||
case "time without time zone",
|
||||
"time": //MySQL
|
||||
return "Time"
|
||||
case "time with time zone":
|
||||
return "Timez"
|
||||
case "interval":
|
||||
return "Interval"
|
||||
case "USER-DEFINED", "enum", "text", "character", "character varying", "bytea", "uuid",
|
||||
"tsvector", "bit", "bit varying", "money", "json", "jsonb", "xml", "point", "line", "ARRAY",
|
||||
"char", "varchar", "binary", "varbinary",
|
||||
"tinyblob", "blob", "mediumblob", "longblob", "tinytext", "mediumtext", "longtext": // MySQL
|
||||
return "String"
|
||||
case "real", "numeric", "decimal", "double precision", "float",
|
||||
"double": // MySQL
|
||||
return "Float"
|
||||
default:
|
||||
fmt.Println("- [SQL Builder] Unsupported sql column '" + columnMetaData.Name + " " + columnMetaData.DataType.Name + "', using StringColumn instead.")
|
||||
return "String"
|
||||
}
|
||||
}
|
||||
|
||||
// EnumSQLBuilder is template for generating enum SQLBuilder files
|
||||
type EnumSQLBuilder struct {
|
||||
Skip bool
|
||||
Path string
|
||||
FileName string
|
||||
InstanceName string
|
||||
ValueName func(enumValue string) string
|
||||
}
|
||||
|
||||
// DefaultEnumSQLBuilder returns default implementation of EnumSQLBuilder
|
||||
func DefaultEnumSQLBuilder(enumMetaData metadata.Enum) EnumSQLBuilder {
|
||||
return EnumSQLBuilder{
|
||||
Path: "/enum",
|
||||
FileName: utils.ToGoFileName(enumMetaData.Name),
|
||||
InstanceName: utils.ToGoIdentifier(enumMetaData.Name),
|
||||
ValueName: func(enumValue string) string {
|
||||
return defaultEnumValueName(enumMetaData.Name, enumValue)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// PackageName returns enum sql builder package name
|
||||
func (e EnumSQLBuilder) PackageName() string {
|
||||
return path.Base(e.Path)
|
||||
}
|
||||
|
||||
// UsePath returns new EnumSQLBuilder with new path set
|
||||
func (e EnumSQLBuilder) UsePath(path string) EnumSQLBuilder {
|
||||
e.Path = path
|
||||
return e
|
||||
}
|
||||
|
||||
// UseFileName returns new EnumSQLBuilder with new file name set
|
||||
func (e EnumSQLBuilder) UseFileName(name string) EnumSQLBuilder {
|
||||
e.FileName = name
|
||||
return e
|
||||
}
|
||||
|
||||
// UseInstanceName returns new EnumSQLBuilder with instance name set
|
||||
func (e EnumSQLBuilder) UseInstanceName(name string) EnumSQLBuilder {
|
||||
e.InstanceName = name
|
||||
return e
|
||||
}
|
||||
|
||||
func defaultEnumValueName(enumName, enumValue string) string {
|
||||
enumValueName := utils.ToGoIdentifier(enumValue)
|
||||
if !unicode.IsLetter([]rune(enumValueName)[0]) {
|
||||
return utils.ToGoIdentifier(enumName) + enumValueName
|
||||
}
|
||||
|
||||
return enumValueName
|
||||
}
|
||||
11
generator/template/sql_builder_template_test.go
Normal file
11
generator/template/sql_builder_template_test.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package template
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestToGoEnumValueIdentifier(t *testing.T) {
|
||||
require.Equal(t, defaultEnumValueName("enum_name", "enum_value"), "EnumValue")
|
||||
require.Equal(t, defaultEnumValueName("NumEnum", "100"), "NumEnum100")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue