Some checks failed
CI / Lint (push) Successful in -27m59s
CI / Test (1.25) (push) Successful in -27m46s
CI / Test (1.24) (push) Failing after 59s
CI / Build (push) Successful in -28m14s
Integration Tests / Integration Tests (push) Failing after -28m16s
Release / Build and Release (push) Successful in 1m1s
feat(templ): ✨ added templ to command line that reads go template and outputs code Reviewed-on: #1 Co-authored-by: Hein <hein.puth@gmail.com> Co-committed-by: Hein <hein.puth@gmail.com>
168 lines
5.9 KiB
Go
168 lines
5.9 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"git.warky.dev/wdevs/relspecgo/pkg/models"
|
|
"git.warky.dev/wdevs/relspecgo/pkg/writers"
|
|
wtemplate "git.warky.dev/wdevs/relspecgo/pkg/writers/template"
|
|
)
|
|
|
|
var (
|
|
templSourceType string
|
|
templSourcePath string
|
|
templSourceConn string
|
|
templTemplatePath string
|
|
templOutputPath string
|
|
templSchemaFilter string
|
|
templMode string
|
|
templFilenamePattern string
|
|
)
|
|
|
|
var templCmd = &cobra.Command{
|
|
Use: "templ",
|
|
Short: "Apply custom templates to database schemas",
|
|
Long: `Apply custom Go text templates to database schemas with flexible execution modes.
|
|
|
|
The templ command allows you to transform database schemas using custom Go text
|
|
templates. It supports multiple execution modes for different use cases:
|
|
|
|
Execution Modes:
|
|
database Execute template once for entire database (single output file)
|
|
schema Execute template once per schema (one file per schema)
|
|
script Execute template once per script (one file per script)
|
|
table Execute template once per table (one file per table)
|
|
|
|
Supported Input Formats:
|
|
dbml, dctx, drawdb, graphql, json, yaml, gorm, bun, drizzle, prisma, typeorm, pgsql
|
|
|
|
Template Functions:
|
|
String utilities: toUpper, toLower, toCamelCase, toPascalCase, toSnakeCase, toKebabCase,
|
|
pluralize, singularize, title, trim, split, join, replace
|
|
|
|
Type conversion: sqlToGo, sqlToTypeScript, sqlToJava, sqlToPython, sqlToRust,
|
|
sqlToCSharp, sqlToPhp
|
|
|
|
Filtering: filterTables, filterColumns, filterPrimaryKeys, filterForeignKeys,
|
|
filterNullable, filterNotNull, filterColumnsByType
|
|
|
|
Formatting: toJSON, toJSONPretty, toYAML, indent, escape, comment
|
|
|
|
Loop helpers: enumerate, batch, reverse, first, last, skip, take, concat,
|
|
unique, sortBy, groupBy
|
|
|
|
Safe access: get, getOr, getPath, has, keys, values, merge, pick, omit,
|
|
sliceContains, indexOf, pluck
|
|
|
|
Examples:
|
|
# Generate documentation from PostgreSQL database
|
|
relspec templ --from pgsql --from-conn "postgres://user:pass@localhost/db" \
|
|
--template docs.tmpl --output schema-docs.md
|
|
|
|
# Generate one TypeScript model file per table
|
|
relspec templ --from dbml --from-path schema.dbml \
|
|
--template ts-model.tmpl --mode table \
|
|
--output ./models/ \
|
|
--filename-pattern "{{.Name | toCamelCase}}.ts"
|
|
|
|
# Generate schema documentation files
|
|
relspec templ --from json --from-path db.json \
|
|
--template schema.tmpl --mode schema \
|
|
--output ./docs/ \
|
|
--filename-pattern "{{.Name}}_schema.md"`,
|
|
RunE: runTempl,
|
|
}
|
|
|
|
func init() {
|
|
templCmd.Flags().StringVar(&templSourceType, "from", "", "Source format (dbml, pgsql, json, etc.)")
|
|
templCmd.Flags().StringVar(&templSourcePath, "from-path", "", "Source file path (for file-based sources)")
|
|
templCmd.Flags().StringVar(&templSourceConn, "from-conn", "", "Source connection string (for database sources)")
|
|
templCmd.Flags().StringVar(&templTemplatePath, "template", "", "Template file path (required)")
|
|
templCmd.Flags().StringVar(&templOutputPath, "output", "", "Output path (file or directory, empty for stdout)")
|
|
templCmd.Flags().StringVar(&templSchemaFilter, "schema", "", "Filter to specific schema")
|
|
templCmd.Flags().StringVar(&templMode, "mode", "database", "Execution mode: database, schema, script, or table")
|
|
templCmd.Flags().StringVar(&templFilenamePattern, "filename-pattern", "{{.Name}}.txt", "Filename pattern for multi-output modes")
|
|
|
|
_ = templCmd.MarkFlagRequired("from")
|
|
_ = templCmd.MarkFlagRequired("template")
|
|
}
|
|
|
|
func runTempl(cmd *cobra.Command, args []string) error {
|
|
// Print header
|
|
fmt.Fprintf(os.Stderr, "=== RelSpec Template Execution ===\n")
|
|
fmt.Fprintf(os.Stderr, "Started at: %s\n\n", getCurrentTimestamp())
|
|
|
|
// Read database using the same function as convert
|
|
fmt.Fprintf(os.Stderr, "Reading from %s...\n", templSourceType)
|
|
db, err := readDatabaseForConvert(templSourceType, templSourcePath, templSourceConn)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read source: %w", err)
|
|
}
|
|
|
|
// Print database stats
|
|
schemaCount := len(db.Schemas)
|
|
tableCount := 0
|
|
for _, schema := range db.Schemas {
|
|
tableCount += len(schema.Tables)
|
|
}
|
|
fmt.Fprintf(os.Stderr, "✓ Successfully read database: %s\n", db.Name)
|
|
fmt.Fprintf(os.Stderr, " Schemas: %d\n", schemaCount)
|
|
fmt.Fprintf(os.Stderr, " Tables: %d\n\n", tableCount)
|
|
|
|
// Apply schema filter if specified
|
|
if templSchemaFilter != "" {
|
|
fmt.Fprintf(os.Stderr, "Filtering to schema: %s\n", templSchemaFilter)
|
|
found := false
|
|
for _, schema := range db.Schemas {
|
|
if schema.Name == templSchemaFilter {
|
|
db.Schemas = []*models.Schema{schema}
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return fmt.Errorf("schema not found: %s", templSchemaFilter)
|
|
}
|
|
}
|
|
|
|
// Create template writer
|
|
fmt.Fprintf(os.Stderr, "Loading template: %s\n", templTemplatePath)
|
|
fmt.Fprintf(os.Stderr, "Execution mode: %s\n", templMode)
|
|
|
|
metadata := map[string]interface{}{
|
|
"template_path": templTemplatePath,
|
|
"mode": templMode,
|
|
"filename_pattern": templFilenamePattern,
|
|
}
|
|
|
|
writerOpts := &writers.WriterOptions{
|
|
OutputPath: templOutputPath,
|
|
Metadata: metadata,
|
|
}
|
|
|
|
writer, err := wtemplate.NewWriter(writerOpts)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create template writer: %w", err)
|
|
}
|
|
|
|
// Execute template
|
|
fmt.Fprintf(os.Stderr, "\nExecuting template...\n")
|
|
if err := writer.WriteDatabase(db); err != nil {
|
|
return fmt.Errorf("failed to execute template: %w", err)
|
|
}
|
|
|
|
// Print success message
|
|
fmt.Fprintf(os.Stderr, "\n✓ Template executed successfully\n")
|
|
if templOutputPath != "" {
|
|
fmt.Fprintf(os.Stderr, "Output written to: %s\n", templOutputPath)
|
|
} else {
|
|
fmt.Fprintf(os.Stderr, "Output written to stdout\n")
|
|
}
|
|
fmt.Fprintf(os.Stderr, "Completed at: %s\n", getCurrentTimestamp())
|
|
|
|
return nil
|
|
}
|