package services import ( "embed" "fmt" "go/format" "os" "path/filepath" "strings" "text/template" "time" ) type FieldConfig struct { Name string `json:"name"` Label string `json:"label"` GoType string `json:"go_type"` DBType string `json:"db_type"` Validators []string `json:"validators"` Required bool `json:"required"` Searchable bool `json:"searchable"` Sortable bool `json:"sortable"` SearchType string `json:"search_type"` SearchUIType string `json:"search_ui_type"` Dictionary string `json:"dictionary"` ApiUrl string `json:"api_url"` Relation *RelationConfig `json:"relation"` } type RelationConfig struct { Table string `json:"table"` // 关联表名 ForeignKey string `json:"foreign_key"` // 外键字段 DisplayField string `json:"display_field"` // 显示字段 RelationType string `json:"relation_type"` // 关联类型:hasOne, belongsTo, hasMany } type FieldType struct { Label string `json:"label"` Value string `json:"value"` GoType string `json:"go_type"` DBType string `json:"db_type"` Validators []string `json:"validators"` } type GeneratedFile struct { Path string `json:"path"` Content string `json:"content"` } type FilesExistError struct { Files []string } func (e *FilesExistError) Error() string { return fmt.Sprintf("files already exist: %v", e.Files) } type CodeGeneratorService interface { Preview(moduleName, tableName string, fields []FieldConfig, fileType string, options map[string]bool) (string, error) Save(moduleName, tableName string, fields []FieldConfig, selectedFiles []string, options map[string]bool) ([]string, error) ForceSave(moduleName, tableName string, fields []FieldConfig, selectedFiles []string, options map[string]bool) ([]string, error) GetFieldTypes() []FieldType Generate(moduleName, tableName string, fields []FieldConfig, selectedFiles []string, options map[string]bool) ([]GeneratedFile, error) } type CodeGeneratorServiceImpl struct{} //go:embed templates/* var templates embed.FS func NewCodeGeneratorService() CodeGeneratorService { return &CodeGeneratorServiceImpl{} } func (s *CodeGeneratorServiceImpl) Generate(moduleName, tableName string, fields []FieldConfig, selectedFiles []string, options map[string]bool) ([]GeneratedFile, error) { var files []GeneratedFile generators := []struct { fileType string generate func(string, string, []FieldConfig, map[string]bool) (GeneratedFile, error) }{ {"model", s.generateModel}, {"controller", s.generateController}, {"service", s.generateService}, {"request_create", s.generateRequestCreate}, {"request_update", s.generateRequestUpdate}, {"migration", s.generateMigration}, {"api", s.generateFrontendAPI}, {"list_page", s.generateFrontendListPage}, {"form_page", s.generateFrontendFormPage}, } // Create a map for faster lookup of selected files selectedMap := make(map[string]bool) if len(selectedFiles) > 0 { for _, f := range selectedFiles { selectedMap[f] = true } } for _, gen := range generators { // If selectedFiles is provided, only generate selected files if len(selectedFiles) > 0 && !selectedMap[gen.fileType] { continue } file, err := gen.generate(moduleName, tableName, fields, options) if err != nil { return nil, fmt.Errorf("failed to generate %s: %w", gen.fileType, err) } if strings.HasSuffix(file.Path, ".go") { formatted, err := format.Source([]byte(file.Content)) if err == nil { file.Content = string(formatted) } } files = append(files, file) } return files, nil } func (s *CodeGeneratorServiceImpl) Preview(moduleName, tableName string, fields []FieldConfig, fileType string, options map[string]bool) (string, error) { templateName, err := s.getTemplateName(fileType) if err != nil { return "", err } templateContent, err := templates.ReadFile(templateName) if err != nil { return "", fmt.Errorf("failed to read template: %w", err) } tmpl, err := template.New("preview").Delims("<<", ">>").Parse(string(templateContent)) if err != nil { return "", fmt.Errorf("failed to parse template: %w", err) } data := s.buildTemplateData(moduleName, tableName, fields, fileType, options) var builder strings.Builder if err := tmpl.Execute(&builder, data); err != nil { return "", fmt.Errorf("failed to execute template: %w", err) } content := builder.String() if strings.Contains(templateName, ".go") || fileType == "model" || fileType == "controller" || fileType == "service" || fileType == "request_create" || fileType == "request_update" || fileType == "migration" { formatted, err := format.Source([]byte(content)) if err == nil { return string(formatted), nil } } return content, nil } func (s *CodeGeneratorServiceImpl) Save(moduleName, tableName string, fields []FieldConfig, selectedFiles []string, options map[string]bool) ([]string, error) { files, err := s.Generate(moduleName, tableName, fields, selectedFiles, options) if err != nil { return nil, err } var existingFiles []string for _, file := range files { if _, err := os.Stat(file.Path); err == nil { existingFiles = append(existingFiles, file.Path) } } if len(existingFiles) > 0 { return nil, &FilesExistError{ Files: existingFiles, } } var savedFiles []string for _, file := range files { dir := filepath.Dir(file.Path) if err := os.MkdirAll(dir, 0755); err != nil { return nil, fmt.Errorf("failed to create directory %s: %w", dir, err) } if err := os.WriteFile(file.Path, []byte(file.Content), 0644); err != nil { return nil, fmt.Errorf("failed to write file %s: %w", file.Path, err) } savedFiles = append(savedFiles, file.Path) } return savedFiles, nil } func (s *CodeGeneratorServiceImpl) ForceSave(moduleName, tableName string, fields []FieldConfig, selectedFiles []string, options map[string]bool) ([]string, error) { files, err := s.Generate(moduleName, tableName, fields, selectedFiles, options) if err != nil { return nil, err } var savedFiles []string for _, file := range files { dir := filepath.Dir(file.Path) if err := os.MkdirAll(dir, 0755); err != nil { return nil, fmt.Errorf("failed to create directory %s: %w", dir, err) } if err := os.WriteFile(file.Path, []byte(file.Content), 0644); err != nil { return nil, fmt.Errorf("failed to write file %s: %w", file.Path, err) } savedFiles = append(savedFiles, file.Path) } return savedFiles, nil } func (s *CodeGeneratorServiceImpl) GetFieldTypes() []FieldType { return []FieldType{ { Label: "字符串", Value: "string", GoType: "string", DBType: "string", Validators: []string{"string", "max:255"}, }, { Label: "文本", Value: "text", GoType: "string", DBType: "text", Validators: []string{"string"}, }, { Label: "整数", Value: "integer", GoType: "int", DBType: "integer", Validators: []string{"integer"}, }, { Label: "小数", Value: "decimal", GoType: "float64", DBType: "decimal", Validators: []string{"numeric"}, }, { Label: "布尔值", Value: "boolean", GoType: "bool", DBType: "boolean", Validators: []string{"boolean"}, }, { Label: "日期", Value: "date", GoType: "time.Time", DBType: "date", Validators: []string{"date"}, }, { Label: "日期时间", Value: "datetime", GoType: "time.Time", DBType: "datetime", Validators: []string{"date"}, }, { Label: "时间戳", Value: "timestamp", GoType: "int64", DBType: "timestamp", Validators: []string{"integer"}, }, { Label: "JSON", Value: "json", GoType: "string", DBType: "json", Validators: []string{"json"}, }, } } func (s *CodeGeneratorServiceImpl) getTemplateName(fileType string) (string, error) { switch fileType { case "model": return "templates/model.tpl", nil case "controller": return "templates/controller.tpl", nil case "service": return "templates/service.tpl", nil case "request_create": return "templates/request_create.tpl", nil case "request_update": return "templates/request_update.tpl", nil case "migration": return "templates/migration.tpl", nil case "api": return "templates/api.js.tpl", nil case "list_page": return "templates/list_page.vue.tpl", nil case "form_page": return "templates/form_page.vue.tpl", nil default: return "", fmt.Errorf("unknown file type: %s", fileType) } } func (s *CodeGeneratorServiceImpl) buildTemplateData(moduleName, tableName string, fields []FieldConfig, fileType string, options map[string]bool) interface{} { templateFields := s.convertFieldsToTemplateFields(fields) // Default options hasCreate := true hasEdit := true hasDelete := true if options != nil { if val, ok := options["has_create"]; ok { hasCreate = val } if val, ok := options["has_edit"]; ok { hasEdit = val } if val, ok := options["has_delete"]; ok { hasDelete = val } } switch fileType { case "model": return struct { ModelName string TableName string Fields []TemplateFieldConfig }{ ModelName: toPascalCase(moduleName), TableName: tableName, Fields: templateFields, } case "controller": var searchableFields []TemplateFieldConfig var listFields []TemplateFieldConfig for _, field := range templateFields { if field.Searchable { searchableFields = append(searchableFields, field) } if field.Relation == nil { listFields = append(listFields, field) } } return struct { ControllerName string ServiceName string ModelName string ModuleName string SearchableFields []TemplateFieldConfig RequestCreateName string RequestUpdateName string HasCreate bool HasEdit bool HasDelete bool }{ ControllerName: toPascalCase(moduleName) + "Controller", ServiceName: toPascalCase(moduleName) + "Service", ModelName: toPascalCase(moduleName), ModuleName: moduleName, SearchableFields: searchableFields, RequestCreateName: toPascalCase(moduleName) + "Create", RequestUpdateName: toPascalCase(moduleName) + "Update", HasCreate: hasCreate, HasEdit: hasEdit, HasDelete: hasDelete, } case "service": var searchableFields []TemplateFieldConfig for _, field := range templateFields { if field.Searchable { searchableFields = append(searchableFields, field) } } return struct { ServiceName string ModelName string ModuleName string SearchableFields []TemplateFieldConfig RequestCreateName string RequestUpdateName string FormFields []TemplateFieldConfig HasCreate bool HasEdit bool HasDelete bool }{ ServiceName: toPascalCase(moduleName) + "Service", ModelName: toPascalCase(moduleName), ModuleName: moduleName, SearchableFields: searchableFields, RequestCreateName: toPascalCase(moduleName) + "Create", RequestUpdateName: toPascalCase(moduleName) + "Update", FormFields: templateFields, HasCreate: hasCreate, HasEdit: hasEdit, HasDelete: hasDelete, } case "request_create": return struct { ModuleName string TableName string FormFields []TemplateFieldConfig RequestCreateName string }{ ModuleName: moduleName, TableName: tableName, FormFields: templateFields, RequestCreateName: toPascalCase(moduleName) + "Create", } case "request_update": return struct { ModuleName string TableName string FormFields []TemplateFieldConfig RequestUpdateName string }{ ModuleName: moduleName, TableName: tableName, FormFields: templateFields, RequestUpdateName: toPascalCase(moduleName) + "Update", } case "migration": for i := range templateFields { templateFields[i].MigrationMethod = getMigrationMethod(templateFields[i].DBType) } return struct { ModelName string TableName string Fields []TemplateFieldConfig }{ ModelName: toPascalCase(moduleName), TableName: tableName, Fields: templateFields, } case "api": return struct { ModelName string ModuleName string ModuleNameK string FormFields []TemplateFieldConfig HasCreate bool HasEdit bool HasDelete bool }{ ModelName: toPascalCase(moduleName), ModuleName: moduleName, ModuleNameK: toKebabCase(moduleName), FormFields: templateFields, HasCreate: hasCreate, HasEdit: hasEdit, HasDelete: hasDelete, } case "list_page": var searchableFields []TemplateFieldConfig var listFields []TemplateFieldConfig for _, field := range templateFields { if field.Searchable { searchableFields = append(searchableFields, field) } if field.Relation == nil { listFields = append(listFields, field) } } return struct { ModelName string ModuleName string ModuleNameK string SearchableFields []TemplateFieldConfig ListFields []TemplateFieldConfig FormFields []TemplateFieldConfig HasCreate bool HasEdit bool HasDelete bool }{ ModelName: toPascalCase(moduleName), ModuleName: moduleName, ModuleNameK: toKebabCase(moduleName), SearchableFields: searchableFields, ListFields: listFields, FormFields: templateFields, HasCreate: hasCreate, HasEdit: hasEdit, HasDelete: hasDelete, } case "form_page": return struct { ModelName string ModuleName string ModuleNameK string FormFields []TemplateFieldConfig HasCreate bool HasEdit bool }{ ModelName: toPascalCase(moduleName), ModuleName: moduleName, ModuleNameK: toKebabCase(moduleName), FormFields: templateFields, HasCreate: hasCreate, HasEdit: hasEdit, } default: return struct { ModuleName string TableName string FormFields []TemplateFieldConfig RequestCreateName string RequestUpdateName string }{ ModuleName: moduleName, TableName: tableName, FormFields: templateFields, RequestCreateName: toPascalCase(moduleName) + "Create", RequestUpdateName: toPascalCase(moduleName) + "Update", } } } type TemplateFieldConfig struct { Name string Label string FieldName string JsonName string GoType string DBType string Validators []string Required bool MigrationMethod string PascalName string Comment string FormType string SearchType string SearchUIType string Dictionary string ApiUrl string Sortable bool Searchable bool Relation *RelationConfig } func (s *CodeGeneratorServiceImpl) convertFieldsToTemplateFields(fields []FieldConfig) []TemplateFieldConfig { fieldTypes := s.GetFieldTypes() fieldTypeMap := make(map[string]FieldType) for _, ft := range fieldTypes { fieldTypeMap[ft.Value] = ft } templateFields := make([]TemplateFieldConfig, len(fields)) for i, field := range fields { fieldType, exists := fieldTypeMap[field.DBType] if !exists { fieldType = fieldTypes[0] } templateFields[i] = TemplateFieldConfig{ Name: field.Name, Label: field.Label, FieldName: toPascalCase(field.Name), JsonName: field.Name, GoType: fieldType.GoType, DBType: field.DBType, Validators: field.Validators, Required: field.Required, PascalName: toPascalCase(field.Name), Comment: field.Label, FormType: getFormType(field.DBType), SearchType: getSearchType(field.SearchType), SearchUIType: getSearchUIType(field.SearchUIType, field.DBType), Dictionary: field.Dictionary, ApiUrl: getApiUrl(field.Dictionary, field.ApiUrl), Sortable: getSortable(field.DBType), Searchable: field.Searchable, Relation: field.Relation, } } return templateFields } func (s *CodeGeneratorServiceImpl) generateModel(moduleName, tableName string, fields []FieldConfig, options map[string]bool) (GeneratedFile, error) { templateContent, err := templates.ReadFile("templates/model.tpl") if err != nil { return GeneratedFile{}, fmt.Errorf("failed to read model template: %w", err) } templateFields := s.convertFieldsToTemplateFields(fields) data := struct { ModelName string TableName string Fields []TemplateFieldConfig }{ ModelName: toPascalCase(moduleName), TableName: tableName, Fields: templateFields, } content, err := s.executeTemplate(string(templateContent), data) if err != nil { return GeneratedFile{}, err } return GeneratedFile{ Path: fmt.Sprintf("app/models/%s.go", toSnakeCase(moduleName)), Content: content, }, nil } func (s *CodeGeneratorServiceImpl) generateController(moduleName, tableName string, fields []FieldConfig, options map[string]bool) (GeneratedFile, error) { templateContent, err := templates.ReadFile("templates/controller.tpl") if err != nil { return GeneratedFile{}, fmt.Errorf("failed to read controller template: %w", err) } // Default options hasCreate := true hasEdit := true hasDelete := true if options != nil { if val, ok := options["has_create"]; ok { hasCreate = val } if val, ok := options["has_edit"]; ok { hasEdit = val } if val, ok := options["has_delete"]; ok { hasDelete = val } } templateFields := s.convertFieldsToTemplateFields(fields) data := struct { ControllerName string ServiceName string ModelName string ModuleName string SearchableFields []TemplateFieldConfig RequestCreateName string RequestUpdateName string HasCreate bool HasEdit bool HasDelete bool }{ ControllerName: toPascalCase(moduleName) + "Controller", ServiceName: toPascalCase(moduleName) + "Service", ModelName: toPascalCase(moduleName), ModuleName: moduleName, SearchableFields: templateFields, RequestCreateName: toPascalCase(moduleName) + "Create", RequestUpdateName: toPascalCase(moduleName) + "Update", HasCreate: hasCreate, HasEdit: hasEdit, HasDelete: hasDelete, } content, err := s.executeTemplate(string(templateContent), data) if err != nil { return GeneratedFile{}, err } return GeneratedFile{ Path: fmt.Sprintf("app/http/controllers/admin/%s_controller.go", toSnakeCase(moduleName)), Content: content, }, nil } func (s *CodeGeneratorServiceImpl) generateService(moduleName, tableName string, fields []FieldConfig, options map[string]bool) (GeneratedFile, error) { templateContent, err := templates.ReadFile("templates/service.tpl") if err != nil { return GeneratedFile{}, fmt.Errorf("failed to read service template: %w", err) } // Default options hasCreate := true hasEdit := true hasDelete := true if options != nil { if val, ok := options["has_create"]; ok { hasCreate = val } if val, ok := options["has_edit"]; ok { hasEdit = val } if val, ok := options["has_delete"]; ok { hasDelete = val } } templateFields := s.convertFieldsToTemplateFields(fields) data := struct { ServiceName string ModelName string ModuleName string SearchableFields []TemplateFieldConfig RequestCreateName string RequestUpdateName string FormFields []TemplateFieldConfig HasCreate bool HasEdit bool HasDelete bool }{ ServiceName: toPascalCase(moduleName) + "Service", ModelName: toPascalCase(moduleName), ModuleName: moduleName, SearchableFields: templateFields, RequestCreateName: toPascalCase(moduleName) + "Create", RequestUpdateName: toPascalCase(moduleName) + "Update", FormFields: templateFields, HasCreate: hasCreate, HasEdit: hasEdit, HasDelete: hasDelete, } content, err := s.executeTemplate(string(templateContent), data) if err != nil { return GeneratedFile{}, err } return GeneratedFile{ Path: fmt.Sprintf("app/services/%s_service.go", toSnakeCase(moduleName)), Content: content, }, nil } func (s *CodeGeneratorServiceImpl) generateRequestCreate(moduleName, tableName string, fields []FieldConfig, options map[string]bool) (GeneratedFile, error) { templateContent, err := templates.ReadFile("templates/request_create.tpl") if err != nil { return GeneratedFile{}, fmt.Errorf("failed to read request_create template: %w", err) } templateFields := s.convertFieldsToTemplateFields(fields) data := struct { ModuleName string TableName string FormFields []TemplateFieldConfig RequestCreateName string }{ ModuleName: moduleName, TableName: tableName, FormFields: templateFields, RequestCreateName: toPascalCase(moduleName) + "Create", } content, err := s.executeTemplate(string(templateContent), data) if err != nil { return GeneratedFile{}, err } return GeneratedFile{ Path: fmt.Sprintf("app/http/requests/admin/%s_create.go", toSnakeCase(moduleName)), Content: content, }, nil } func (s *CodeGeneratorServiceImpl) generateRequestUpdate(moduleName, tableName string, fields []FieldConfig, options map[string]bool) (GeneratedFile, error) { templateContent, err := templates.ReadFile("templates/request_update.tpl") if err != nil { return GeneratedFile{}, fmt.Errorf("failed to read request_update template: %w", err) } templateFields := s.convertFieldsToTemplateFields(fields) data := struct { ModuleName string TableName string FormFields []TemplateFieldConfig RequestUpdateName string }{ ModuleName: moduleName, TableName: tableName, FormFields: templateFields, RequestUpdateName: toPascalCase(moduleName) + "Update", } content, err := s.executeTemplate(string(templateContent), data) if err != nil { return GeneratedFile{}, err } return GeneratedFile{ Path: fmt.Sprintf("app/http/requests/admin/%s_update.go", toSnakeCase(moduleName)), Content: content, }, nil } func (s *CodeGeneratorServiceImpl) generateMigration(moduleName, tableName string, fields []FieldConfig, options map[string]bool) (GeneratedFile, error) { templateContent, err := templates.ReadFile("templates/migration.tpl") if err != nil { return GeneratedFile{}, fmt.Errorf("failed to read migration template: %w", err) } templateFields := s.convertFieldsToTemplateFields(fields) for i := range templateFields { templateFields[i].MigrationMethod = getMigrationMethod(templateFields[i].DBType) } timestamp := time.Now().Format("20060102150405") data := struct { ModelName string TableName string Fields []TemplateFieldConfig Timestamp string }{ ModelName: toPascalCase(moduleName), TableName: tableName, Fields: templateFields, Timestamp: timestamp, } content, err := s.executeTemplate(string(templateContent), data) if err != nil { return GeneratedFile{}, err } return GeneratedFile{ Path: fmt.Sprintf("database/migrations/%s_create_%s_table.go", timestamp, toSnakeCase(tableName)), Content: content, }, nil } func (s *CodeGeneratorServiceImpl) generateFrontendAPI(moduleName, tableName string, fields []FieldConfig, options map[string]bool) (GeneratedFile, error) { templateContent, err := templates.ReadFile("templates/api.js.tpl") if err != nil { return GeneratedFile{}, fmt.Errorf("failed to read api template: %w", err) } // Default options hasCreate := true hasEdit := true hasDelete := true if options != nil { if val, ok := options["has_create"]; ok { hasCreate = val } if val, ok := options["has_edit"]; ok { hasEdit = val } if val, ok := options["has_delete"]; ok { hasDelete = val } } templateFields := s.convertFieldsToTemplateFields(fields) data := struct { ModelName string ModuleName string ModuleNameK string FormFields []TemplateFieldConfig HasCreate bool HasEdit bool HasDelete bool }{ ModelName: toPascalCase(moduleName), ModuleName: moduleName, ModuleNameK: toKebabCase(moduleName), FormFields: templateFields, HasCreate: hasCreate, HasEdit: hasEdit, HasDelete: hasDelete, } content, err := s.executeTemplate(string(templateContent), data) if err != nil { return GeneratedFile{}, err } return GeneratedFile{ Path: fmt.Sprintf("html/src/api/%s.js", toKebabCase(moduleName)), Content: content, }, nil } func (s *CodeGeneratorServiceImpl) generateFrontendListPage(moduleName, tableName string, fields []FieldConfig, options map[string]bool) (GeneratedFile, error) { templateContent, err := templates.ReadFile("templates/list_page.vue.tpl") if err != nil { return GeneratedFile{}, fmt.Errorf("failed to read list_page template: %w", err) } // Default options hasCreate := true hasEdit := true hasDelete := true if options != nil { if val, ok := options["has_create"]; ok { hasCreate = val } if val, ok := options["has_edit"]; ok { hasEdit = val } if val, ok := options["has_delete"]; ok { hasDelete = val } } templateFields := s.convertFieldsToTemplateFields(fields) data := struct { ModelName string ModuleName string ModuleNameK string SearchableFields []TemplateFieldConfig ListFields []TemplateFieldConfig FormFields []TemplateFieldConfig HasCreate bool HasEdit bool HasDelete bool }{ ModelName: toPascalCase(moduleName), ModuleName: moduleName, ModuleNameK: toKebabCase(moduleName), SearchableFields: templateFields, ListFields: templateFields, FormFields: templateFields, HasCreate: hasCreate, HasEdit: hasEdit, HasDelete: hasDelete, } content, err := s.executeTemplate(string(templateContent), data) if err != nil { return GeneratedFile{}, err } return GeneratedFile{ Path: fmt.Sprintf("html/src/views/%s/%sList.vue", toKebabCase(moduleName), toPascalCase(moduleName)), Content: content, }, nil } func (s *CodeGeneratorServiceImpl) generateFrontendFormPage(moduleName, tableName string, fields []FieldConfig, options map[string]bool) (GeneratedFile, error) { templateContent, err := templates.ReadFile("templates/form_page.vue.tpl") if err != nil { return GeneratedFile{}, fmt.Errorf("failed to read form_page template: %w", err) } // Default options hasCreate := true hasEdit := true if options != nil { if val, ok := options["has_create"]; ok { hasCreate = val } if val, ok := options["has_edit"]; ok { hasEdit = val } } templateFields := s.convertFieldsToTemplateFields(fields) data := struct { ModelName string ModuleName string ModuleNameK string FormFields []TemplateFieldConfig HasCreate bool HasEdit bool }{ ModelName: toPascalCase(moduleName), ModuleName: moduleName, ModuleNameK: toKebabCase(moduleName), FormFields: templateFields, HasCreate: hasCreate, HasEdit: hasEdit, } content, err := s.executeTemplate(string(templateContent), data) if err != nil { return GeneratedFile{}, err } return GeneratedFile{ Path: fmt.Sprintf("html/src/views/%s/%sForm.vue", toKebabCase(moduleName), toPascalCase(moduleName)), Content: content, }, nil } func (s *CodeGeneratorServiceImpl) executeTemplate(templateContent string, data interface{}) (string, error) { tmpl, err := template.New("code").Delims("<<", ">>").Parse(templateContent) if err != nil { return "", fmt.Errorf("failed to parse template: %w", err) } var builder strings.Builder if err := tmpl.Execute(&builder, data); err != nil { return "", fmt.Errorf("failed to execute template: %w", err) } return builder.String(), nil } func toSnakeCase(s string) string { result := "" for i, r := range s { if i > 0 && r >= 'A' && r <= 'Z' { result += "_" } result += string(r) } return strings.ToLower(result) } func toPascalCase(s string) string { words := strings.Split(s, "_") for i := range words { if len(words[i]) > 0 { words[i] = strings.ToUpper(words[i][:1]) + strings.ToLower(words[i][1:]) } } return strings.Join(words, "") } func toKebabCase(s string) string { return strings.ReplaceAll(toSnakeCase(s), "_", "-") } func getMigrationMethod(dbType string) string { switch dbType { case "string": return "String" case "text": return "Text" case "integer": return "Integer" case "decimal": return "Decimal" case "boolean": return "Boolean" case "date": return "Date" case "datetime": return "DateTime" case "timestamp": return "Timestamp" case "json": return "Json" default: return "String" } } func getFormType(dbType string) string { switch dbType { case "string": return "input" case "text": return "textarea" case "integer": return "input" case "decimal": return "input" case "boolean": return "switch" case "date": return "date-picker" case "datetime": return "datetime-picker" case "timestamp": return "datetime-picker" case "json": return "textarea" default: return "input" } } func getSearchType(searchType string) string { if searchType == "" { return "like" } return searchType } func getSearchUIType(searchUIType string, dbType string) string { if searchUIType != "" { return searchUIType } switch dbType { case "date": return "date" case "datetime", "timestamp": return "datetime" case "boolean": return "select" default: return "input" } } func getApiUrl(dictionary string, apiUrl string) string { if apiUrl != "" { return apiUrl } if dictionary != "" { return "/options?type=" + dictionary } return "" } func getSortable(dbType string) bool { switch dbType { case "string", "integer", "decimal", "date", "datetime", "timestamp": return true case "text", "boolean", "json": return false default: return true } }