# 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](../../../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:

```go
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`:

```go
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:

```go
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:
   ```go
   // string_helpers.go
   func ToScreamingSnakeCase(s string) string {
       return strings.ToUpper(ToSnakeCase(s))
   }
   ```

2. **Register in funcmap.go:**
   ```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

```bash
# 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:

```go
// 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

```go
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

```bash
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

- [User Documentation](/docs/TEMPLATE_MODE.md) - Complete template function reference
- [Common Types Package](../../commontypes/) - Centralized type mappings
- [Reflect Utilities](../../reflectutil/) - Reflection helpers
- [Models Package](../../models/) - Database schema models
- [Go Template Docs](https://pkg.go.dev/text/template) - Official Go template documentation
