# 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