256 lines
7.1 KiB
Go
256 lines
7.1 KiB
Go
package bun
|
|
|
|
import (
|
|
"sort"
|
|
"strings"
|
|
|
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
|
)
|
|
|
|
// TemplateData represents the data passed to the template for code generation
|
|
type TemplateData struct {
|
|
PackageName string
|
|
Imports []string
|
|
Models []*ModelData
|
|
Config *MethodConfig
|
|
}
|
|
|
|
// ModelData represents a single model/struct in the template
|
|
type ModelData struct {
|
|
Name string
|
|
TableName string // schema.table format
|
|
SchemaName string
|
|
TableNameOnly string // just table name without schema
|
|
Comment string
|
|
Fields []*FieldData
|
|
Config *MethodConfig
|
|
PrimaryKeyField string // Name of the primary key field
|
|
PrimaryKeyIsSQL bool // Whether PK uses SQL type (needs .Int64() call)
|
|
IDColumnName string // Name of the ID column in database
|
|
Prefix string // 3-letter prefix
|
|
}
|
|
|
|
// FieldData represents a single field in a struct
|
|
type FieldData struct {
|
|
Name string // Go field name (PascalCase)
|
|
Type string // Go type
|
|
BunTag string // Complete bun tag
|
|
JSONTag string // JSON tag
|
|
Comment string // Field comment
|
|
}
|
|
|
|
// MethodConfig controls which helper methods to generate
|
|
type MethodConfig struct {
|
|
GenerateTableName bool
|
|
GenerateSchemaName bool
|
|
GenerateTableNameOnly bool
|
|
GenerateGetID bool
|
|
GenerateGetIDStr bool
|
|
GenerateSetID bool
|
|
GenerateUpdateID bool
|
|
GenerateGetIDName bool
|
|
GenerateGetPrefix bool
|
|
}
|
|
|
|
// DefaultMethodConfig returns a MethodConfig with all methods enabled
|
|
func DefaultMethodConfig() *MethodConfig {
|
|
return &MethodConfig{
|
|
GenerateTableName: true,
|
|
GenerateSchemaName: true,
|
|
GenerateTableNameOnly: true,
|
|
GenerateGetID: true,
|
|
GenerateGetIDStr: true,
|
|
GenerateSetID: true,
|
|
GenerateUpdateID: true,
|
|
GenerateGetIDName: true,
|
|
GenerateGetPrefix: true,
|
|
}
|
|
}
|
|
|
|
// NewTemplateData creates a new TemplateData with the given package name and config
|
|
func NewTemplateData(packageName string, config *MethodConfig) *TemplateData {
|
|
if config == nil {
|
|
config = DefaultMethodConfig()
|
|
}
|
|
|
|
return &TemplateData{
|
|
PackageName: packageName,
|
|
Imports: make([]string, 0),
|
|
Models: make([]*ModelData, 0),
|
|
Config: config,
|
|
}
|
|
}
|
|
|
|
// AddModel adds a model to the template data
|
|
func (td *TemplateData) AddModel(model *ModelData) {
|
|
model.Config = td.Config
|
|
td.Models = append(td.Models, model)
|
|
}
|
|
|
|
// AddImport adds an import to the template data (deduplicates automatically)
|
|
func (td *TemplateData) AddImport(importPath string) {
|
|
// Check if already exists
|
|
for _, imp := range td.Imports {
|
|
if imp == importPath {
|
|
return
|
|
}
|
|
}
|
|
td.Imports = append(td.Imports, importPath)
|
|
}
|
|
|
|
// FinalizeImports sorts and organizes imports
|
|
func (td *TemplateData) FinalizeImports() {
|
|
// Sort imports alphabetically
|
|
sort.Strings(td.Imports)
|
|
}
|
|
|
|
// NewModelData creates a new ModelData from a models.Table
|
|
func NewModelData(table *models.Table, schema string, typeMapper *TypeMapper) *ModelData {
|
|
tableName := table.Name
|
|
if schema != "" {
|
|
tableName = schema + "." + table.Name
|
|
}
|
|
|
|
// Generate model name: singularize and convert to PascalCase
|
|
singularTable := Singularize(table.Name)
|
|
modelName := SnakeCaseToPascalCase(singularTable)
|
|
|
|
// Add "Model" prefix if not already present
|
|
if !hasModelPrefix(modelName) {
|
|
modelName = "Model" + modelName
|
|
}
|
|
|
|
model := &ModelData{
|
|
Name: modelName,
|
|
TableName: tableName,
|
|
SchemaName: schema,
|
|
TableNameOnly: table.Name,
|
|
Comment: formatComment(table.Description, table.Comment),
|
|
Fields: make([]*FieldData, 0),
|
|
Prefix: GeneratePrefix(table.Name),
|
|
}
|
|
|
|
// Find primary key
|
|
for _, col := range table.Columns {
|
|
if col.IsPrimaryKey {
|
|
model.PrimaryKeyField = SnakeCaseToPascalCase(col.Name)
|
|
model.IDColumnName = col.Name
|
|
// Check if PK type is a SQL type (contains resolvespec_common or sql_types)
|
|
goType := typeMapper.SQLTypeToGoType(col.Type, col.NotNull)
|
|
model.PrimaryKeyIsSQL = strings.Contains(goType, "resolvespec_common") || strings.Contains(goType, "sql_types")
|
|
break
|
|
}
|
|
}
|
|
|
|
// Convert columns to fields (sorted by sequence or name)
|
|
columns := sortColumns(table.Columns)
|
|
for _, col := range columns {
|
|
field := columnToField(col, table, typeMapper)
|
|
model.Fields = append(model.Fields, field)
|
|
}
|
|
|
|
return model
|
|
}
|
|
|
|
// columnToField converts a models.Column to FieldData
|
|
func columnToField(col *models.Column, table *models.Table, typeMapper *TypeMapper) *FieldData {
|
|
fieldName := SnakeCaseToPascalCase(col.Name)
|
|
goType := typeMapper.SQLTypeToGoType(col.Type, col.NotNull)
|
|
bunTag := typeMapper.BuildBunTag(col, table)
|
|
jsonTag := col.Name // Use column name for JSON tag
|
|
|
|
return &FieldData{
|
|
Name: fieldName,
|
|
Type: goType,
|
|
BunTag: bunTag,
|
|
JSONTag: jsonTag,
|
|
Comment: formatComment(col.Description, col.Comment),
|
|
}
|
|
}
|
|
|
|
// AddRelationshipField adds a relationship field to the model
|
|
func (md *ModelData) AddRelationshipField(field *FieldData) {
|
|
md.Fields = append(md.Fields, field)
|
|
}
|
|
|
|
// formatComment combines description and comment into a single comment string
|
|
func formatComment(description, comment string) string {
|
|
if description != "" && comment != "" {
|
|
return description + " - " + comment
|
|
}
|
|
if description != "" {
|
|
return description
|
|
}
|
|
return comment
|
|
}
|
|
|
|
// hasModelPrefix checks if a name already has "Model" prefix
|
|
func hasModelPrefix(name string) bool {
|
|
return len(name) >= 5 && name[:5] == "Model"
|
|
}
|
|
|
|
// sortColumns sorts columns by sequence, then by name
|
|
func sortColumns(columns map[string]*models.Column) []*models.Column {
|
|
result := make([]*models.Column, 0, len(columns))
|
|
for _, col := range columns {
|
|
result = append(result, col)
|
|
}
|
|
|
|
sort.Slice(result, func(i, j int) bool {
|
|
// Sort by sequence if both have it
|
|
if result[i].Sequence > 0 && result[j].Sequence > 0 {
|
|
return result[i].Sequence < result[j].Sequence
|
|
}
|
|
|
|
// Put primary keys first
|
|
if result[i].IsPrimaryKey != result[j].IsPrimaryKey {
|
|
return result[i].IsPrimaryKey
|
|
}
|
|
|
|
// Otherwise sort alphabetically
|
|
return result[i].Name < result[j].Name
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
// LoadMethodConfigFromMetadata loads method configuration from metadata map
|
|
func LoadMethodConfigFromMetadata(metadata map[string]interface{}) *MethodConfig {
|
|
config := DefaultMethodConfig()
|
|
|
|
if metadata == nil {
|
|
return config
|
|
}
|
|
|
|
// Load each setting from metadata if present
|
|
if val, ok := metadata["generate_table_name"].(bool); ok {
|
|
config.GenerateTableName = val
|
|
}
|
|
if val, ok := metadata["generate_schema_name"].(bool); ok {
|
|
config.GenerateSchemaName = val
|
|
}
|
|
if val, ok := metadata["generate_table_name_only"].(bool); ok {
|
|
config.GenerateTableNameOnly = val
|
|
}
|
|
if val, ok := metadata["generate_get_id"].(bool); ok {
|
|
config.GenerateGetID = val
|
|
}
|
|
if val, ok := metadata["generate_get_id_str"].(bool); ok {
|
|
config.GenerateGetIDStr = val
|
|
}
|
|
if val, ok := metadata["generate_set_id"].(bool); ok {
|
|
config.GenerateSetID = val
|
|
}
|
|
if val, ok := metadata["generate_update_id"].(bool); ok {
|
|
config.GenerateUpdateID = val
|
|
}
|
|
if val, ok := metadata["generate_get_id_name"].(bool); ok {
|
|
config.GenerateGetIDName = val
|
|
}
|
|
if val, ok := metadata["generate_get_prefix"].(bool); ok {
|
|
config.GenerateGetPrefix = val
|
|
}
|
|
|
|
return config
|
|
}
|