Files
relspecgo/pkg/writers/template/README.md
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

277 lines
7.6 KiB
Markdown

# 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