# GORM Writer

Generates Go source files with GORM model definitions from database schema information.

## Overview

The GORM Writer converts RelSpec's internal database model representation into Go source code with GORM struct definitions, complete with proper tags, relationships, and methods.

With `--types sqltypes`, nullable fields use the [`pkg/sqltypes`](../../sqltypes/README.md) package.

## Features

- Generates GORM-compatible Go structs
- Creates proper `gorm` struct tags
- Generates `TableName()` methods
- Adds relationship fields (belongs-to, has-many)
- Supports both single-file and multi-file output
- Auto-generates helper methods (optional)
- Maps SQL types to Go types
- Handles nullable fields with custom sql_types

## 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/gorm"
)

func main() {
    // Assume db is a *models.Database from a reader
    options := &writers.WriterOptions{
        OutputPath:  "models.go",
        PackageName: "models",
    }

    writer := gorm.NewWriter(options)
    err := writer.WriteDatabase(db)
    if err != nil {
        panic(err)
    }
}
```

### CLI Examples

```bash
# Generate GORM models from a DBML schema (default: baselib pointer types)
relspec convert --from dbml --from-path schema.dbml \
  --to gorm --to-path models.go --package models

# Use standard library database/sql nullable types instead
relspec convert --from dbml --from-path schema.dbml \
  --to gorm --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 gorm --to-path models.go --package models \
  --types sqltypes

# Multi-file output (one file per table)
relspec convert --from json --from-path schema.json \
  --to gorm --to-path models/ --package models
```

## Output Modes

### Single File Mode

Generates all models in one file:

```bash
relspec --input pgsql --conn "..." --output gorm --out-file models.go
```

### Multi-File Mode

Generates one file per table (auto-detected when output is a directory):

```bash
relspec --input pgsql --conn "..." --output gorm --out-file models/
```

Files are named: `sql_{schema}_{table}.go`

## Generated Code Examples

### sqltypes package types (`--types sqltypes`)

```go
package models

import (
    sql_types "git.warky.dev/wdevs/relspecgo/pkg/sqltypes"
)

type ModelUser struct {
    ID        string                      `gorm:"column:id;type:uuid;primaryKey" json:"id"`
    Username  string                      `gorm:"column:username;type:text;not null" json:"username"`
    Email     sql_types.SqlString         `gorm:"column:email;type:text" json:"email,omitempty"`
    Tags      sql_types.SqlStringArray    `gorm:"column:tags;type:text[];not null;default:'{}'" json:"tags"`
    CreatedAt sql_types.SqlTimeStamp      `gorm:"column:created_at;type:timestamptz;not null;default:now()" json:"created_at"`
}

func (ModelUser) TableName() string {
    return "public.users"
}
```

### Standard library — `--types stdlib`

```go
package models

import (
    "database/sql"
    "time"
)

type ModelUser struct {
    ID        string         `gorm:"column:id;type:uuid;primaryKey" json:"id"`
    Username  string         `gorm:"column:username;type:text;not null" json:"username"`
    Email     sql.NullString `gorm:"column:email;type:text" json:"email,omitempty"`
    Tags      []string       `gorm:"column:tags;type:text[];not null;default:'{}'" json:"tags"`
    CreatedAt time.Time      `gorm:"column:created_at;type:timestamptz;not null;default:now()" json:"created_at"`
}

func (ModelUser) TableName() string {
    return "public.users"
}
```

## 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

Configure additional writer behavior using metadata in `WriterOptions`:

```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
    },
}
```

## 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` | `SqlString` | `sql.NullString` |
| `text[]` | `SqlStringArray` | `SqlStringArray` | `[]string` |
| `integer[]` | `SqlInt32Array` | `SqlInt32Array` | `[]int32` |
| `uuid[]` | `SqlUUIDArray` | `SqlUUIDArray` | `[]string` |
| `vector` | `SqlVector` | `SqlVector` | `[]float32` |

## Relationship Generation

The writer automatically generates relationship fields:

- **Belongs-to**: Generated for tables with foreign keys
- **Has-many**: Generated for tables referenced by foreign keys
- Relationship field names use 3-letter prefixes
- Includes proper `gorm` tags with `foreignKey` and `references`

## Notes

- Model names are prefixed with "Model" (e.g., `ModelUser`)
- Nullable columns use `sql_types.SqlString`, `sql_types.SqlInt64`, etc. by default; pass `--types stdlib` to use `sql.NullString`, `sql.NullInt64`, etc. instead
- Array columns use `sql_types.SqlStringArray`, `sql_types.SqlInt32Array`, etc. by default; `--types stdlib` produces plain Go slices (`[]string`, `[]int32`, …)
- Generated code is auto-formatted with `go fmt`
- JSON tags are automatically added
- Supports schema-qualified table names in `TableName()` method
