feat: add --types flag and stdlib nullable type support for bun/gorm writers
* Fix pgsql reader double-quoting defaults: normalizePostgresDefault strips surrounding SQL string literal quotes from column_default before storing, matching the convention used by every other reader. * Add NullableTypes field to WriterOptions with NullableTypeResolveSpec (default) and NullableTypeStdlib constants. * Both bun and gorm TypeMappers now accept a typeStyle parameter. stdlib mode produces sql.NullString/NullInt32/NullTime etc. for nullable scalars, plain Go slices for arrays, and time.Time for NOT NULL timestamps. Default resolvespec behaviour is unchanged. * Add --types flag to convert and split commands. * Update bun/README.md and gorm/README.md with side-by-side generated code examples, updated type mapping tables, and Writer Options documentation.
This commit is contained in:
@@ -46,54 +46,67 @@ func main() {
|
||||
### CLI Examples
|
||||
|
||||
```bash
|
||||
# Generate Bun models from PostgreSQL database
|
||||
relspec --input pgsql \
|
||||
--conn "postgres://localhost/mydb" \
|
||||
--output bun \
|
||||
--out-file models.go \
|
||||
--package models
|
||||
# Generate Bun models from a DBML schema (default: resolvespec types)
|
||||
relspec convert --from dbml --from-path schema.dbml \
|
||||
--to bun --to-path models.go --package models
|
||||
|
||||
# Convert GORM models to Bun
|
||||
relspec --input gorm --in-file gorm_models.go --output bun --out-file bun_models.go
|
||||
# Use standard library database/sql nullable types instead of resolvespec
|
||||
relspec convert --from dbml --from-path schema.dbml \
|
||||
--to bun --to-path models.go --package models \
|
||||
--types stdlib
|
||||
|
||||
# Multi-file output
|
||||
relspec --input json --in-file schema.json --output bun --out-file models/
|
||||
# Explicitly select resolvespec types (same as omitting --types)
|
||||
relspec convert --from pgsql --from-conn "postgres://localhost/mydb" \
|
||||
--to bun --to-path models.go --package models \
|
||||
--types resolvespec
|
||||
|
||||
# Multi-file output (one file per table)
|
||||
relspec convert --from json --from-path schema.json \
|
||||
--to bun --to-path models/ --package models
|
||||
```
|
||||
|
||||
## Generated Code Example
|
||||
## Generated Code Examples
|
||||
|
||||
### Default — resolvespec types (`--types resolvespec`)
|
||||
|
||||
```go
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
"database/sql"
|
||||
resolvespec_common "github.com/bitechdev/ResolveSpec/pkg/spectypes"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
bun.BaseModel `bun:"table:users,alias:u"`
|
||||
|
||||
ID int64 `bun:"id,pk,autoincrement" json:"id"`
|
||||
Username string `bun:"username,notnull,unique" json:"username"`
|
||||
Email string `bun:"email,notnull" json:"email"`
|
||||
Bio sql.NullString `bun:"bio" json:"bio,omitempty"`
|
||||
CreatedAt time.Time `bun:"created_at,notnull,default:now()" json:"created_at"`
|
||||
|
||||
// Relationships
|
||||
Posts []*Post `bun:"rel:has-many,join:id=user_id" json:"posts,omitempty"`
|
||||
ID int64 `bun:"id,type:uuid,pk," json:"id"`
|
||||
Username string `bun:"username,type:text,notnull," json:"username"`
|
||||
Email resolvespec_common.SqlString `bun:"email,type:text,nullzero," json:"email"`
|
||||
Tags resolvespec_common.SqlStringArray `bun:"tags,type:text[],default:'{}',notnull," json:"tags"`
|
||||
CreatedAt resolvespec_common.SqlTimeStamp `bun:"created_at,type:timestamptz,default:now(),notnull," json:"created_at"`
|
||||
}
|
||||
```
|
||||
|
||||
type Post struct {
|
||||
bun.BaseModel `bun:"table:posts,alias:p"`
|
||||
### Standard library — `--types stdlib`
|
||||
|
||||
ID int64 `bun:"id,pk" json:"id"`
|
||||
UserID int64 `bun:"user_id,notnull" json:"user_id"`
|
||||
Title string `bun:"title,notnull" json:"title"`
|
||||
Content sql.NullString `bun:"content" json:"content,omitempty"`
|
||||
```go
|
||||
package models
|
||||
|
||||
// Belongs to
|
||||
User *User `bun:"rel:belongs-to,join:user_id=id" json:"user,omitempty"`
|
||||
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"`
|
||||
}
|
||||
```
|
||||
|
||||
@@ -111,19 +124,68 @@ type Post struct {
|
||||
|
||||
## Type Mapping
|
||||
|
||||
| SQL Type | Go Type | Nullable Type |
|
||||
|----------|---------|---------------|
|
||||
| bigint | int64 | sql.NullInt64 |
|
||||
| integer | int | sql.NullInt32 |
|
||||
| varchar, text | string | sql.NullString |
|
||||
| boolean | bool | sql.NullBool |
|
||||
| timestamp | time.Time | sql.NullTime |
|
||||
| numeric | float64 | sql.NullFloat64 |
|
||||
The nullable type package is selected with `--types` (or `WriterOptions.NullableTypes`).
|
||||
|
||||
| SQL Type | NOT NULL (both) | Nullable — resolvespec | 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 resolvespec 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 resolvespec types (default — omit NullableTypes or set to "resolvespec")
|
||||
options := &writers.WriterOptions{
|
||||
OutputPath: "models.go",
|
||||
PackageName: "models",
|
||||
NullableTypes: writers.NullableTypeResolveSpec,
|
||||
}
|
||||
|
||||
// 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
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Model names are derived from table names (singularized, PascalCase)
|
||||
- Table aliases are auto-generated from table names
|
||||
- Nullable columns use `resolvespec_common.SqlString`, `resolvespec_common.SqlTimeStamp`, etc. by default; pass `--types stdlib` to use `sql.NullString`, `sql.NullTime`, etc. instead
|
||||
- Array columns use `resolvespec_common.SqlStringArray`, `resolvespec_common.SqlInt32Array`, etc. by default; `--types stdlib` produces plain Go slices (`[]string`, `[]int32`, …)
|
||||
- Multi-file mode: one file per table named `sql_{schema}_{table}.go`
|
||||
- Generated code is auto-formatted
|
||||
- JSON tags are automatically added
|
||||
|
||||
Reference in New Issue
Block a user