Added Drizzle ORM support
This commit is contained in:
221
pkg/writers/drizzle/template_data.go
Normal file
221
pkg/writers/drizzle/template_data.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package drizzle
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
||||
)
|
||||
|
||||
// TemplateData represents the data passed to the template for code generation
|
||||
type TemplateData struct {
|
||||
Imports []string
|
||||
Enums []*EnumData
|
||||
Tables []*TableData
|
||||
}
|
||||
|
||||
// EnumData represents an enum in the schema
|
||||
type EnumData struct {
|
||||
Name string // Enum name (PascalCase)
|
||||
VarName string // Variable name for the enum (camelCase)
|
||||
Values []string // Enum values
|
||||
ValuesStr string // Comma-separated quoted values for pgEnum()
|
||||
TypeUnion string // TypeScript union type (e.g., "'admin' | 'user' | 'guest'")
|
||||
SchemaName string // Schema name
|
||||
}
|
||||
|
||||
// TableData represents a table in the template
|
||||
type TableData struct {
|
||||
Name string // Table variable name (camelCase, e.g., users)
|
||||
TableName string // Actual database table name (e.g., users)
|
||||
TypeName string // TypeScript type name (PascalCase, e.g., Users)
|
||||
Columns []*ColumnData // Column definitions
|
||||
Indexes []*IndexData // Index definitions
|
||||
Comment string // Table comment
|
||||
SchemaName string // Schema name
|
||||
NeedsSQLTag bool // Whether we need to import 'sql' from drizzle-orm
|
||||
IndexColumnFields []string // Column field names used in indexes (for destructuring)
|
||||
}
|
||||
|
||||
// ColumnData represents a column in a table
|
||||
type ColumnData struct {
|
||||
Name string // Column name in database
|
||||
FieldName string // Field name in TypeScript (camelCase)
|
||||
DrizzleChain string // Complete Drizzle column chain (e.g., "integer('id').primaryKey()")
|
||||
TypeScriptType string // TypeScript type for interface (e.g., "string", "number | null")
|
||||
IsForeignKey bool // Whether this is a foreign key
|
||||
ReferencesLine string // The .references() line if FK
|
||||
Comment string // Column comment
|
||||
}
|
||||
|
||||
// IndexData represents an index definition
|
||||
type IndexData struct {
|
||||
Name string // Index name
|
||||
Columns []string // Column names
|
||||
IsUnique bool // Whether it's a unique index
|
||||
Definition string // Complete index definition line
|
||||
}
|
||||
|
||||
// NewTemplateData creates a new TemplateData
|
||||
func NewTemplateData() *TemplateData {
|
||||
return &TemplateData{
|
||||
Imports: make([]string, 0),
|
||||
Enums: make([]*EnumData, 0),
|
||||
Tables: make([]*TableData, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// AddImport adds an import to the template data (deduplicates automatically)
|
||||
func (td *TemplateData) AddImport(importLine string) {
|
||||
// Check if already exists
|
||||
for _, imp := range td.Imports {
|
||||
if imp == importLine {
|
||||
return
|
||||
}
|
||||
}
|
||||
td.Imports = append(td.Imports, importLine)
|
||||
}
|
||||
|
||||
// AddEnum adds an enum to the template data
|
||||
func (td *TemplateData) AddEnum(enum *EnumData) {
|
||||
td.Enums = append(td.Enums, enum)
|
||||
}
|
||||
|
||||
// AddTable adds a table to the template data
|
||||
func (td *TemplateData) AddTable(table *TableData) {
|
||||
td.Tables = append(td.Tables, table)
|
||||
}
|
||||
|
||||
// FinalizeImports sorts imports
|
||||
func (td *TemplateData) FinalizeImports() {
|
||||
sort.Strings(td.Imports)
|
||||
}
|
||||
|
||||
// NewEnumData creates EnumData from a models.Enum
|
||||
func NewEnumData(enum *models.Enum, tm *TypeMapper) *EnumData {
|
||||
// Keep enum name as-is (it should already be PascalCase from the source)
|
||||
enumName := enum.Name
|
||||
// Variable name is camelCase version
|
||||
varName := tm.ToCamelCase(enum.Name)
|
||||
|
||||
// Format values as comma-separated quoted strings for pgEnum()
|
||||
quotedValues := make([]string, len(enum.Values))
|
||||
for i, v := range enum.Values {
|
||||
quotedValues[i] = "'" + v + "'"
|
||||
}
|
||||
valuesStr := ""
|
||||
for i, qv := range quotedValues {
|
||||
if i > 0 {
|
||||
valuesStr += ", "
|
||||
}
|
||||
valuesStr += qv
|
||||
}
|
||||
|
||||
// Build TypeScript union type (e.g., "'admin' | 'user' | 'guest'")
|
||||
typeUnion := ""
|
||||
for i, qv := range quotedValues {
|
||||
if i > 0 {
|
||||
typeUnion += " | "
|
||||
}
|
||||
typeUnion += qv
|
||||
}
|
||||
|
||||
return &EnumData{
|
||||
Name: enumName,
|
||||
VarName: varName,
|
||||
Values: enum.Values,
|
||||
ValuesStr: valuesStr,
|
||||
TypeUnion: typeUnion,
|
||||
SchemaName: enum.Schema,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTableData creates TableData from a models.Table
|
||||
func NewTableData(table *models.Table, tm *TypeMapper) *TableData {
|
||||
tableName := tm.ToCamelCase(table.Name)
|
||||
typeName := tm.ToPascalCase(table.Name)
|
||||
|
||||
return &TableData{
|
||||
Name: tableName,
|
||||
TableName: table.Name,
|
||||
TypeName: typeName,
|
||||
Columns: make([]*ColumnData, 0),
|
||||
Indexes: make([]*IndexData, 0),
|
||||
Comment: formatComment(table.Description, table.Comment),
|
||||
SchemaName: table.Schema,
|
||||
}
|
||||
}
|
||||
|
||||
// AddColumn adds a column to the table data
|
||||
func (td *TableData) AddColumn(col *ColumnData) {
|
||||
td.Columns = append(td.Columns, col)
|
||||
}
|
||||
|
||||
// AddIndex adds an index to the table data
|
||||
func (td *TableData) AddIndex(idx *IndexData) {
|
||||
td.Indexes = append(td.Indexes, idx)
|
||||
}
|
||||
|
||||
// NewColumnData creates ColumnData from a models.Column
|
||||
func NewColumnData(col *models.Column, table *models.Table, tm *TypeMapper, isEnum bool) *ColumnData {
|
||||
fieldName := tm.ToCamelCase(col.Name)
|
||||
drizzleChain := tm.BuildColumnChain(col, table, isEnum)
|
||||
|
||||
return &ColumnData{
|
||||
Name: col.Name,
|
||||
FieldName: fieldName,
|
||||
DrizzleChain: drizzleChain,
|
||||
Comment: formatComment(col.Description, col.Comment),
|
||||
}
|
||||
}
|
||||
|
||||
// NewIndexData creates IndexData from a models.Index
|
||||
func NewIndexData(index *models.Index, tableVar string, tm *TypeMapper) *IndexData {
|
||||
indexName := tm.ToCamelCase(index.Name) + "Idx"
|
||||
|
||||
// Build column references as field names (will be used with destructuring)
|
||||
colRefs := make([]string, len(index.Columns))
|
||||
for i, colName := range index.Columns {
|
||||
// Use just the field name for destructured parameters
|
||||
colRefs[i] = tm.ToCamelCase(colName)
|
||||
}
|
||||
|
||||
// Build the complete definition
|
||||
// Example: index('email_idx').on(email)
|
||||
// or: uniqueIndex('unique_email_idx').on(email)
|
||||
definition := ""
|
||||
if index.Unique {
|
||||
definition = "uniqueIndex('" + index.Name + "').on(" + joinStrings(colRefs, ", ") + ")"
|
||||
} else {
|
||||
definition = "index('" + index.Name + "').on(" + joinStrings(colRefs, ", ") + ")"
|
||||
}
|
||||
|
||||
return &IndexData{
|
||||
Name: indexName,
|
||||
Columns: index.Columns,
|
||||
IsUnique: index.Unique,
|
||||
Definition: definition,
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// joinStrings joins a slice of strings with a separator
|
||||
func joinStrings(strs []string, sep string) string {
|
||||
result := ""
|
||||
for i, s := range strs {
|
||||
if i > 0 {
|
||||
result += sep
|
||||
}
|
||||
result += s
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user