Files
relspecgo/pkg/writers/template
Hein 8c602e3db0
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
Added go text template writier (#1)
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>
2026-01-03 19:05:53 +00:00
..
2026-01-03 19:05:53 +00:00
2026-01-03 19:05:53 +00:00
2026-01-03 19:05:53 +00:00
2026-01-03 19:05:53 +00:00
2026-01-03 19:05:53 +00:00

Template Writer

Custom template-based writer for RelSpec that allows users to generate any output format using Go text templates.

Overview

The template writer provides a powerful and flexible way to transform database schemas into any desired format. It supports multiple execution modes and provides 80+ template functions for data transformation.

For complete user documentation, see: /docs/TEMPLATE_MODE.md

Architecture

Package Structure

pkg/writers/template/
├── README.md              # This file
├── writer.go              # Core writer with entrypoint mode logic
├── template_data.go       # Data structures passed to templates
├── funcmap.go             # Template function registry
├── string_helpers.go      # String manipulation functions
├── type_mappers.go        # SQL type conversion (delegates to commontypes)
├── filters.go             # Database object filtering
├── formatters.go          # JSON/YAML formatting utilities
├── loop_helpers.go        # Iteration and collection utilities
├── safe_access.go         # Safe map/array access functions
└── errors.go              # Custom error types

Dependencies

  • pkg/commontypes - Centralized type mappings for Go, TypeScript, Java, Python, Rust, C#, PHP
  • pkg/reflectutil - Reflection utilities for safe type manipulation
  • pkg/models - Database schema models

Writer Interface Implementation

Implements the standard writers.Writer interface:

type Writer interface {
    WriteDatabase(db *models.Database) error
    WriteSchema(schema *models.Schema) error
    WriteTable(table *models.Table) error
}

Execution Modes

The writer supports four execution modes via WriterOptions.Metadata["mode"]:

Mode Data Passed Output Use Case
database Full database Single file Reports, documentation
schema One schema at a time File per schema Schema-specific docs
script One script at a time File per script Script processing
table One table at a time File per table Model generation

Configuration

Writer is configured via WriterOptions.Metadata:

metadata := map[string]interface{}{
    "template_path":    "/path/to/template.tmpl",  // Required
    "mode":             "table",                    // Default: "database"
    "filename_pattern": "{{.Name}}.ts",            // Default: "{{.Name}}.txt"
}

Template Data Structure

Templates receive a TemplateData struct:

type TemplateData struct {
    // Primary data (one populated based on mode)
    Database *models.Database
    Schema   *models.Schema
    Script   *models.Script
    Table    *models.Table

    // Parent context
    ParentSchema   *models.Schema
    ParentDatabase *models.Database

    // Pre-computed views
    FlatColumns       []*models.FlatColumn
    FlatTables        []*models.FlatTable
    FlatConstraints   []*models.FlatConstraint
    FlatRelationships []*models.FlatRelationship
    Summary           *models.DatabaseSummary

    // User metadata
    Metadata map[string]interface{}
}

Function Categories

String Utilities (string_helpers.go)

Case conversion, pluralization, trimming, splitting, joining

Type Mappers (type_mappers.go)

SQL type conversion to 7+ programming languages (delegates to pkg/commontypes)

Filters (filters.go)

Database object filtering by pattern, type, constraints

Formatters (formatters.go)

JSON/YAML serialization, indentation, escaping, commenting

Loop Helpers (loop_helpers.go)

Enumeration, batching, reversing, sorting, grouping (uses pkg/reflectutil)

Safe Access (safe_access.go)

Safe map/array access without panics (uses pkg/reflectutil)

Adding New Functions

To add a new template function:

  1. Implement the function in the appropriate file:

    // string_helpers.go
    func ToScreamingSnakeCase(s string) string {
        return strings.ToUpper(ToSnakeCase(s))
    }
    
  2. Register in funcmap.go:

    func BuildFuncMap() template.FuncMap {
        return template.FuncMap{
            // ... existing functions
            "toScreamingSnakeCase": ToScreamingSnakeCase,
        }
    }
    
  3. Document in /docs/TEMPLATE_MODE.md

Error Handling

Custom error types in errors.go:

  • TemplateLoadError - Template file not found or unreadable
  • TemplateParseError - Invalid template syntax
  • TemplateExecuteError - Error during template execution

All errors wrap the underlying error for context.

Testing

# Run tests
go test ./pkg/writers/template/...

# Test with example data
cat > test.tmpl << 'EOF'
{{ range .Database.Schemas }}
Schema: {{ .Name }} ({{ len .Tables }} tables)
{{ end }}
EOF

relspec templ --from json --from-path schema.json --template test.tmpl

Multi-file Output

For multi-file modes, the writer:

  1. Iterates through items (schemas/scripts/tables)
  2. Creates TemplateData for each item
  3. Executes template with item data
  4. Generates filename using filename_pattern template
  5. Writes output to generated filename

Output directory is created automatically if it doesn't exist.

Filename Pattern Execution

The filename pattern is itself a template:

// Pattern: "{{.Schema}}/{{.Name | toCamelCase}}.ts"
// For table "user_profile" in schema "public"
// Generates: "public/userProfile.ts"

Available in pattern template:

  • .Name - Item name (schema/script/table)
  • .Schema - Schema name (for scripts/tables)
  • All template functions

Example Usage

As a Library

import (
    "git.warky.dev/wdevs/relspecgo/pkg/writers"
    "git.warky.dev/wdevs/relspecgo/pkg/writers/template"
)

// Create writer
metadata := map[string]interface{}{
    "template_path":    "model.tmpl",
    "mode":             "table",
    "filename_pattern": "{{.Name}}.go",
}

opts := &writers.WriterOptions{
    OutputPath:  "./models/",
    PackageName: "models",
    Metadata:    metadata,
}

writer, err := template.NewWriter(opts)
if err != nil {
    // Handle error
}

// Write database
err = writer.WriteDatabase(db)

Via CLI

relspec templ \
    --from pgsql \
    --from-conn "postgres://localhost/mydb" \
    --template model.tmpl \
    --mode table \
    --output ./models/ \
    --filename-pattern "{{.Name | toPascalCase}}.go"

Performance Considerations

  1. Template Parsing - Template is parsed once in NewWriter(), not per execution
  2. Reflection - Loop and safe access helpers use reflection; cached where possible
  3. Pre-computed Views - FlatColumns, FlatTables, etc. computed once per data item
  4. File I/O - Multi-file mode creates directories as needed

Future Enhancements

Potential improvements:

  • Template caching for filename patterns
  • Parallel template execution for multi-file mode
  • Template function plugins
  • Custom function injection via metadata
  • Template includes/partials support
  • Dry-run mode to preview filenames
  • Progress reporting for large schemas

Contributing

When adding new features:

  1. Follow existing patterns (see similar functions)
  2. Add to appropriate category file
  3. Register in funcmap.go
  4. Update /docs/TEMPLATE_MODE.md
  5. Add tests
  6. Consider edge cases (nil, empty, invalid input)

See Also