refactor: ♻️ change resolvspec types to build in sqltypes

This commit is contained in:
Hein
2026-07-07 15:28:01 +02:00
parent 99d63aa5f4
commit 40a0e6a0aa
26 changed files with 2362 additions and 121 deletions
+19 -17
View File
@@ -6,6 +6,8 @@ Generates Go source files with Bun model definitions from database schema inform
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
@@ -46,19 +48,19 @@ func main() {
### CLI Examples
```bash
# Generate Bun models from a DBML schema (default: resolvespec types)
# 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 of resolvespec
# 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
# Explicitly select resolvespec types (same as omitting --types)
# 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 resolvespec
--types sqltypes
# Multi-file output (one file per table)
relspec convert --from json --from-path schema.json \
@@ -67,24 +69,24 @@ relspec convert --from json --from-path schema.json \
## Generated Code Examples
### Default — resolvespec types (`--types resolvespec`)
### sqltypes package types (`--types sqltypes`)
```go
package models
import (
resolvespec_common "github.com/bitechdev/ResolveSpec/pkg/spectypes"
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 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"`
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"`
}
```
@@ -126,7 +128,7 @@ type User struct {
The nullable type package is selected with `--types` (or `WriterOptions.NullableTypes`).
| SQL Type | NOT NULL (both) | Nullable — resolvespec | Nullable — stdlib |
| SQL Type | NOT NULL (both) | Nullable — sqltypes | Nullable — stdlib |
|---|---|---|---|
| `bigint` | `int64` | `SqlInt64` | `sql.NullInt64` |
| `integer` | `int32` | `SqlInt32` | `sql.NullInt32` |
@@ -142,7 +144,7 @@ The nullable type package is selected with `--types` (or `WriterOptions.Nullable
| `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`.
\* 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
@@ -151,11 +153,11 @@ The nullable type package is selected with `--types` (or `WriterOptions.Nullable
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")
// Use sqltypes package types
options := &writers.WriterOptions{
OutputPath: "models.go",
PackageName: "models",
NullableTypes: writers.NullableTypeResolveSpec,
NullableTypes: writers.NullableTypeSqlTypes,
}
// Use standard library database/sql types
@@ -184,8 +186,8 @@ options := &writers.WriterOptions{
- 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`, …)
- 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
+3 -3
View File
@@ -141,10 +141,10 @@ func NewModelData(table *models.Table, schema string, typeMapper *TypeMapper, fl
safeName := writers.SanitizeStructTagValue(col.Name)
model.PrimaryKeyField = SnakeCaseToPascalCase(safeName)
model.IDColumnName = safeName
// Check if PK type is a SQL type (contains resolvespec_common or sql_types)
// Check if PK type is a SQL type (contains sql_types)
goType := typeMapper.SQLTypeToGoType(col.Type, col.NotNull)
model.PrimaryKeyType = goType
model.PrimaryKeyIsSQL = strings.Contains(goType, "resolvespec_common") || strings.Contains(goType, "sql_types")
model.PrimaryKeyIsSQL = strings.Contains(goType, "sql_types")
model.PrimaryKeyIsStr = isStringLikePrimaryKeyType(goType)
model.PrimaryKeyIDType = "int64"
if model.PrimaryKeyIsStr {
@@ -203,7 +203,7 @@ func formatComment(description, comment string) string {
func isStringLikePrimaryKeyType(goType string) bool {
switch goType {
case "string", "sql.NullString", "resolvespec_common.SqlString", "resolvespec_common.SqlUUID":
case "string", "sql.NullString", "sql_types.SqlString", "sql_types.SqlUUID":
return true
default:
return false
+9 -9
View File
@@ -12,18 +12,18 @@ import (
// TypeMapper handles type conversions between SQL and Go types for Bun
type TypeMapper struct {
sqlTypesAlias string
typeStyle string // writers.NullableTypeResolveSpec | writers.NullableTypeStdlib
typeStyle string // writers.NullableTypeSqlTypes | writers.NullableTypeStdlib | writers.NullableTypeBaselib
}
// NewTypeMapper creates a new TypeMapper.
// typeStyle should be writers.NullableTypeResolveSpec or writers.NullableTypeStdlib;
// an empty string defaults to resolvespec.
// typeStyle should be writers.NullableTypeSqlTypes, writers.NullableTypeStdlib, or
// writers.NullableTypeBaselib; an empty string defaults to baselib.
func NewTypeMapper(typeStyle string) *TypeMapper {
if typeStyle == "" {
typeStyle = writers.NullableTypeBaselib
}
return &TypeMapper{
sqlTypesAlias: "resolvespec_common",
sqlTypesAlias: "sql_types",
typeStyle: typeStyle,
}
}
@@ -50,7 +50,7 @@ func (tm *TypeMapper) SQLTypeToGoType(sqlType string, notNull bool) string {
return tm.baselibNullableGoType(baseType)
}
// resolvespec (default): use base Go types only for simple NOT NULL fields.
// sqltypes: use base Go types only for simple NOT NULL fields.
if notNull && tm.isSimpleType(baseType) {
return tm.baseGoType(baseType)
}
@@ -106,11 +106,11 @@ func (tm *TypeMapper) baseGoType(sqlType string) string {
return goType
}
// Default to resolvespec type
// Default to sqltypes type
return tm.bunGoType(sqlType)
}
// bunGoType returns the Bun/ResolveSpec common type
// bunGoType returns the Bun/sqltypes common type
func (tm *TypeMapper) bunGoType(sqlType string) string {
typeMap := map[string]string{
// Integer types
@@ -461,9 +461,9 @@ func (tm *TypeMapper) NeedsFmtImport(generateGetIDStr bool) bool {
return generateGetIDStr
}
// GetSQLTypesImport returns the import path for the spectypes package.
// GetSQLTypesImport returns the import path for the sqltypes package.
func (tm *TypeMapper) GetSQLTypesImport() string {
return "git.warky.dev/wdevs/relspecgo/pkg/spectypes"
return "git.warky.dev/wdevs/relspecgo/pkg/sqltypes"
}
// GetNullableTypeImportLine returns the full Go import line for the nullable type
+2 -2
View File
@@ -80,7 +80,7 @@ func (w *Writer) writeSingleFile(db *models.Database) error {
// Add bun import (always needed)
templateData.AddImport(fmt.Sprintf("\"%s\"", w.typeMapper.GetBunImport()))
// Add nullable types import (resolvespec or stdlib depending on options)
// Add nullable types import (sqltypes or stdlib depending on options)
templateData.AddImport(w.typeMapper.GetNullableTypeImportLine())
// Collect all models
@@ -177,7 +177,7 @@ func (w *Writer) writeMultiFile(db *models.Database) error {
// Add bun import
templateData.AddImport(fmt.Sprintf("\"%s\"", w.typeMapper.GetBunImport()))
// Add nullable types import (resolvespec or stdlib depending on options)
// Add nullable types import (sqltypes or stdlib depending on options)
templateData.AddImport(w.typeMapper.GetNullableTypeImportLine())
// Create model data
+3 -3
View File
@@ -550,7 +550,7 @@ func TestWriter_FieldNameCollision(t *testing.T) {
}
// Verify NO field named just "TableName" (without underscore)
if strings.Contains(generated, "TableName resolvespec_common") || strings.Contains(generated, "TableName string") {
if strings.Contains(generated, "TableName sql_types") || strings.Contains(generated, "TableName string") {
t.Errorf("Field 'TableName' without underscore should not exist (would conflict with method)\nGenerated:\n%s", generated)
}
}
@@ -827,9 +827,9 @@ func TestTypeMapper_BuildBunTag(t *testing.T) {
t.Errorf("BuildBunTag() = %q, missing %q", result, part)
}
}
// resolvespec mode must NOT add "array" — SqlXxxArray uses sql.Scanner
// sqltypes mode must NOT add "array" — SqlXxxArray uses sql.Scanner
if strings.Contains(result, ",array,") || strings.HasSuffix(result, ",array,") {
t.Errorf("BuildBunTag() = %q, must not contain 'array' in resolvespec mode", result)
t.Errorf("BuildBunTag() = %q, must not contain 'array' in sqltypes mode", result)
}
})
}
+11 -9
View File
@@ -6,6 +6,8 @@ Generates Go source files with GORM model definitions from database schema infor
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
@@ -48,19 +50,19 @@ func main() {
### CLI Examples
```bash
# Generate GORM models from a DBML schema (default: resolvespec types)
# 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 of resolvespec
# 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
# Explicitly select resolvespec types (same as omitting --types)
# 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 resolvespec
--types sqltypes
# Multi-file output (one file per table)
relspec convert --from json --from-path schema.json \
@@ -89,13 +91,13 @@ Files are named: `sql_{schema}_{table}.go`
## Generated Code Examples
### Default — resolvespec types (`--types resolvespec`)
### sqltypes package types (`--types sqltypes`)
```go
package models
import (
sql_types "github.com/bitechdev/ResolveSpec/pkg/spectypes"
sql_types "git.warky.dev/wdevs/relspecgo/pkg/sqltypes"
)
type ModelUser struct {
@@ -141,11 +143,11 @@ func (ModelUser) TableName() string {
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")
// Use sqltypes package types
options := &writers.WriterOptions{
OutputPath: "models.go",
PackageName: "models",
NullableTypes: writers.NullableTypeResolveSpec,
NullableTypes: writers.NullableTypeSqlTypes,
}
// Use standard library database/sql types
@@ -176,7 +178,7 @@ options := &writers.WriterOptions{
The nullable type package is selected with `--types` (or `WriterOptions.NullableTypes`).
| SQL Type | NOT NULL — both | Nullable — resolvespec | Nullable — stdlib |
| SQL Type | NOT NULL — both | Nullable — sqltypes | Nullable — stdlib |
|---|---|---|---|
| `bigint` | `int64` | `SqlInt64` | `sql.NullInt64` |
| `integer` | `int32` | `SqlInt32` | `sql.NullInt32` |
+6 -6
View File
@@ -12,12 +12,12 @@ import (
// TypeMapper handles type conversions between SQL and Go types
type TypeMapper struct {
sqlTypesAlias string
typeStyle string // writers.NullableTypeResolveSpec | writers.NullableTypeStdlib
typeStyle string // writers.NullableTypeSqlTypes | writers.NullableTypeStdlib | writers.NullableTypeBaselib
}
// NewTypeMapper creates a new TypeMapper.
// typeStyle should be writers.NullableTypeResolveSpec or writers.NullableTypeStdlib;
// an empty string defaults to resolvespec.
// typeStyle should be writers.NullableTypeSqlTypes, writers.NullableTypeStdlib, or
// writers.NullableTypeBaselib; an empty string defaults to baselib.
func NewTypeMapper(typeStyle string) *TypeMapper {
if typeStyle == "" {
typeStyle = writers.NullableTypeBaselib
@@ -50,7 +50,7 @@ func (tm *TypeMapper) SQLTypeToGoType(sqlType string, notNull bool) string {
return tm.baselibNullableGoType(baseType)
}
// resolvespec (default)
// sqltypes
if notNull {
return tm.baseGoType(baseType)
}
@@ -505,9 +505,9 @@ func (tm *TypeMapper) NeedsFmtImport(generateGetIDStr bool) bool {
return generateGetIDStr
}
// GetSQLTypesImport returns the import path for the spectypes package.
// GetSQLTypesImport returns the import path for the sqltypes package.
func (tm *TypeMapper) GetSQLTypesImport() string {
return "git.warky.dev/wdevs/relspecgo/pkg/spectypes"
return "git.warky.dev/wdevs/relspecgo/pkg/sqltypes"
}
// GetNullableTypeImportLine returns the full Go import line for the nullable type
+2 -2
View File
@@ -77,7 +77,7 @@ func (w *Writer) writeSingleFile(db *models.Database) error {
packageName := w.getPackageName()
templateData := NewTemplateData(packageName, w.config)
// Add nullable types import (resolvespec or stdlib depending on options)
// Add nullable types import (sqltypes or stdlib depending on options)
templateData.AddImport(w.typeMapper.GetNullableTypeImportLine())
// Collect all models
@@ -171,7 +171,7 @@ func (w *Writer) writeMultiFile(db *models.Database) error {
// Create template data for this single table
templateData := NewTemplateData(packageName, w.config)
// Add nullable types import (resolvespec or stdlib depending on options)
// Add nullable types import (sqltypes or stdlib depending on options)
templateData.AddImport(w.typeMapper.GetNullableTypeImportLine())
// Create model data
+4 -4
View File
@@ -23,9 +23,9 @@ type Writer interface {
// NullableType constants control which Go package is used for nullable column types
// in code-generation writers (Bun, GORM).
const (
// NullableTypeResolveSpec uses git.warky.dev/wdevs/relspecgo/pkg/spectypes
// NullableTypeSqlTypes uses git.warky.dev/wdevs/relspecgo/pkg/sqltypes
// (SqlString, SqlInt32, SqlVector, SqlStringArray, …).
NullableTypeResolveSpec = "resolvespec"
NullableTypeSqlTypes = "sqltypes"
// NullableTypeStdlib uses the standard library database/sql nullable types
// (sql.NullString, sql.NullInt32, …) and plain Go slices for arrays.
@@ -52,9 +52,9 @@ type WriterOptions struct {
// NullableTypes selects the Go type package used for nullable columns in
// code-generation writers (bun, gorm). Accepted values:
// "resolvespec" (default) — git.warky.dev/wdevs/relspecgo/pkg/spectypes
// "sqltypes" — git.warky.dev/wdevs/relspecgo/pkg/sqltypes
// "stdlib" — database/sql (sql.NullString, sql.NullInt32, …)
// "baselib" — plain Go pointer types (*string, *int32, …)
// "baselib" (default) — plain Go pointer types (*string, *int32, …)
NullableTypes string
// Prisma7 enables Prisma 7-specific output for Prisma writers.