# Bun Writer Generates Go source files with Bun model definitions from database schema information. ## Overview The Bun Writer converts RelSpec's internal database model representation into Go source code with Bun struct definitions, complete with proper tags, relationships, and table configuration. With `--types sqltypes`, nullable fields use the [`pkg/sqltypes`](../../sqltypes/README.md) package. ## Features - Generates Bun-compatible Go structs - Creates proper `bun` struct tags - Adds relationship fields - Supports both single-file and multi-file output - Maps SQL types to Go types - Handles nullable fields with sql.Null* types - Generates table aliases ## Usage ### Basic Example ```go package main import ( "git.warky.dev/wdevs/relspecgo/pkg/models" "git.warky.dev/wdevs/relspecgo/pkg/writers" "git.warky.dev/wdevs/relspecgo/pkg/writers/bun" ) func main() { options := &writers.WriterOptions{ OutputPath: "models.go", PackageName: "models", } writer := bun.NewWriter(options) err := writer.WriteDatabase(db) if err != nil { panic(err) } } ``` ### CLI Examples ```bash # Generate Bun models from a DBML schema (default: baselib pointer types) relspec convert --from dbml --from-path schema.dbml \ --to bun --to-path models.go --package models # Use standard library database/sql nullable types instead relspec convert --from dbml --from-path schema.dbml \ --to bun --to-path models.go --package models \ --types stdlib # Select sqltypes package types (git.warky.dev/wdevs/relspecgo/pkg/sqltypes) relspec convert --from pgsql --from-conn "postgres://localhost/mydb" \ --to bun --to-path models.go --package models \ --types sqltypes # Multi-file output (one file per table) relspec convert --from json --from-path schema.json \ --to bun --to-path models/ --package models # Inject computed/scan-only fields that are not present in the database schema # (extra-fields.json contains the JSON array shown below) relspec convert --from dbml --from-path schema.dbml \ --to bun --to-path models.go --package models \ --extra-fields extra-fields.json ``` ## Generated Code Examples ### sqltypes package types (`--types sqltypes`) ```go package models import ( sql_types "git.warky.dev/wdevs/relspecgo/pkg/sqltypes" "github.com/uptrace/bun" ) type User struct { bun.BaseModel `bun:"table:users,alias:u"` ID int64 `bun:"id,type:uuid,pk," json:"id"` Username string `bun:"username,type:text,notnull," json:"username"` Email sql_types.SqlString `bun:"email,type:text,nullzero," json:"email"` Tags sql_types.SqlStringArray `bun:"tags,type:text[],default:'{}',notnull," json:"tags"` CreatedAt sql_types.SqlTimeStamp `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"` } ``` ### Standard library — `--types stdlib` ```go package models import ( "database/sql" "time" "github.com/uptrace/bun" ) type User struct { bun.BaseModel `bun:"table:users,alias:u"` ID string `bun:"id,type:uuid,pk," json:"id"` Username string `bun:"username,type:text,notnull," json:"username"` Email sql.NullString `bun:"email,type:text,nullzero," json:"email"` Tags []string `bun:"tags,type:text[],default:'{}',notnull," json:"tags"` CreatedAt time.Time `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"` } ``` ## Supported Bun Tags - `table` - Table name and alias - `column` - Column name (auto-derived if not specified) - `pk` - Primary key - `autoincrement` - Auto-increment - `notnull` - NOT NULL constraint - `unique` - Unique constraint - `default` - Default value - `rel` - Relationship definition - `type` - Explicit SQL type ## Type Mapping The nullable type package is selected with `--types` (or `WriterOptions.NullableTypes`). | SQL Type | NOT NULL (both) | Nullable — sqltypes | Nullable — stdlib | |---|---|---|---| | `bigint` | `int64` | `SqlInt64` | `sql.NullInt64` | | `integer` | `int32` | `SqlInt32` | `sql.NullInt32` | | `smallint` | `int16` | `SqlInt16` | `sql.NullInt16` | | `text`, `varchar` | `string` | `SqlString` | `sql.NullString` | | `boolean` | `bool` | `SqlBool` | `sql.NullBool` | | `timestamp`, `timestamptz` | `time.Time`* | `SqlTimeStamp` | `sql.NullTime` | | `numeric`, `decimal` | `float64` | `SqlFloat64` | `sql.NullFloat64` | | `uuid` | `string` | `SqlUUID` | `sql.NullString` | | `jsonb` | `string` | `SqlJSONB` | `sql.NullString` | | `text[]` | `SqlStringArray` | `SqlStringArray` | `[]string` | | `integer[]` | `SqlInt32Array` | `SqlInt32Array` | `[]int32` | | `uuid[]` | `SqlUUIDArray` | `SqlUUIDArray` | `[]string` | | `vector` | `SqlVector` | `SqlVector` | `[]float32` | \* In sqltypes mode, NOT NULL timestamps use `SqlTimeStamp` (not `time.Time`) unless the base type is a simple integer or boolean. In stdlib mode, NOT NULL timestamps use `time.Time`. ## Writer Options ### NullableTypes Controls which Go package is used for nullable column types. Set via the `--types` CLI flag or `WriterOptions.NullableTypes`: ```go // Use sqltypes package types options := &writers.WriterOptions{ OutputPath: "models.go", PackageName: "models", NullableTypes: writers.NullableTypeSqlTypes, } // Use standard library database/sql types options := &writers.WriterOptions{ OutputPath: "models.go", PackageName: "models", NullableTypes: writers.NullableTypeStdlib, } ``` ### Metadata Options ```go options := &writers.WriterOptions{ OutputPath: "models.go", PackageName: "models", Metadata: map[string]any{ "multi_file": true, // Enable multi-file mode "populate_refs": true, // Populate RefDatabase/RefSchema "generate_get_id_str": true, // Generate GetIDStr() methods }, } ``` #### Extra fields Use `Metadata["extra_fields"]` (or the CLI `--extra-fields ` flag) to inject custom fields into generated Bun models without editing generated files. This is intended for computed columns and scan-only query fields that Bun must scan into but that are not real database table columns. The CLI flag expects a path to a JSON file, not inline JSON. This avoids shell quoting problems with struct tags and complex type names. Each entry supports: | Field | Required | Description | |---|---:|---| | `target_table` | No | Optional model scope. Accepts table name (`projects`), qualified table (`public.projects`), or model name (`ModelPublicProjects`). If omitted, the field is added to every generated model. | | `name` | Yes | Go struct field name. | | `type` | Yes | Go type to emit. | | `bun_tag` | No | Raw Bun tag contents, e.g. `thought_count,scanonly`. | | `json_tag` | No | Raw JSON tag contents. | | `comment` | No | Optional line comment. | ```go options := &writers.WriterOptions{ OutputPath: "models.go", PackageName: "models", Metadata: map[string]any{ "extra_fields": `[ { "target_table": "projects", "name": "ThoughtCount", "type": "sql_types.SqlInt64", "bun_tag": "thought_count,scanonly", "json_tag": "thought_count", "comment": "Computed by ResolveSpec queries" } ]`, }, } ``` Example `extra-fields.json`: ```json [ { "target_table": "projects", "name": "ThoughtCount", "type": "sql_types.SqlInt64", "bun_tag": "thought_count,scanonly", "json_tag": "thought_count", "comment": "Computed by ResolveSpec queries" } ] ``` ## Notes - Model names are derived from table names (singularized, PascalCase) - Table aliases are auto-generated from table names - Nullable columns use plain Go pointer types (`*string`, `*time.Time`, …) by default; pass `--types sqltypes` to use `sql_types.SqlString`, `sql_types.SqlTimeStamp`, etc., or `--types stdlib` to use `sql.NullString`, `sql.NullTime`, etc. - Array columns use plain Go slices (`[]string`, `[]int32`, …) by default; `--types sqltypes` uses `sql_types.SqlStringArray`, `sql_types.SqlInt32Array`, etc. - Multi-file mode: one file per table named `sql_{schema}_{table}.go` - Generated code is auto-formatted - JSON tags are automatically added