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>
277 lines
7.6 KiB
Markdown
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
|