Add --version flag and display version on startup

This commit is contained in:
2026-03-29 18:56:03 +02:00
parent 894cd458f5
commit 67240d9998
6 changed files with 94 additions and 96 deletions

1
.env
View File

@@ -1 +0,0 @@
DSN=root:Maal12345678@tcp(localhost:3306)/nalu

View File

@@ -4,46 +4,48 @@ Type-safe GORM column descriptors and model generation utilities.
## Library ## Library
The library provides [Field](#field) for type-safe column references and helper functions to extract column/table names. The library provides [Field](#field) for type-safe column references and methods to extract column/table names.
### Field ### Field
`Field` represents a GORM column descriptor with table context. Define package-level variables to get type-safe column references: `Field` represents a GORM column descriptor with table context. Generated code creates package-level variables with typed field references:
```go ```go
var PsAccess = struct { var ProductCols = struct {
IDProfile gormcol.Field ID gormcol.Field
IDAuthorizationRole gormcol.Field Name gormcol.Field
Price gormcol.Field
}{ }{
IDProfile: gormcol.Field{Table: "ps_access", Column: "id_profile"}, ID: gormcol.Field{}.Set((&Product{}).TableName(), "id_product"),
IDAuthorizationRole: gormcol.Field{Table: "ps_access", Column: "id_authorization_role"}, Name: gormcol.Field{}.Set((&Product{}).TableName(), "name"),
Price: gormcol.Field{}.Set((&Product{}).TableName(), "price"),
} }
``` ```
### Helper Functions ### Field Methods
#### Column #### TabCol
Returns just the column name from a Field descriptor. Returns "table.column" format for use in SQL with table qualification.
```go ```go
gormcol.Column(dbmodel.PsAccess.IDAuthorizationRole) // "id_authorization_role" model.ProductCols.Price.TabCol() // "ps_product.price"
``` ```
#### ColumnOnTable #### Col
Returns "table.column" format from a Field descriptor. Returns just the column name.
```go ```go
gormcol.ColumnOnTable(dbmodel.PsAccess.IDAuthorizationRole) // "ps_access.id_authorization_role" model.ProductCols.Price.Col() // "price"
``` ```
#### TableField #### Tab
Returns the table name from a Field descriptor. Returns the table name.
```go ```go
gormcol.TableField(dbmodel.PsAccess.IDAuthorizationRole) // "ps_access" model.ProductCols.Price.Tab() // "ps_product"
``` ```
## CLI ## CLI
@@ -78,13 +80,14 @@ DSN can be provided via `--dsn` flag or `DSN` env var (from `.env` file).
- **Interactive (default)**: select tables with fzf - **Interactive (default)**: select tables with fzf
- **Batch (`--filter` or `--all`)**: generate matching tables with confirmation - **Batch (`--filter` or `--all`)**: generate matching tables with confirmation
| Flag | Default | Description | | Flag | Default | Description |
|----------|------------------------|--------------------------------------------------| |-----------|------------------------|---------------------------------------------------|
| `--dsn` | *(from DSN env)* | MySQL/MariaDB DSN, e.g. `user:pass@tcp(localhost:3306)/dbname` | | `--dsn` | *(from DSN env)* | MySQL/MariaDB DSN, e.g. `user:pass@tcp(localhost:3306)/dbname` |
| `--filter` | *(interactive)* | Regex matching table names to generate (triggers batch mode) | | `--filter`| *(interactive)* | Regex matching table names to generate (triggers batch mode) |
| `--all` | *(interactive)* | Generate all tables matching filter (shows confirmation) | | `--all` | *(interactive)* | Generate all tables matching filter (shows confirmation) |
| `--out` | `./app/model/dbmodel` | Output directory for generated files | | `--out` | `./app/model/dbmodel` | Output directory for generated files |
| `--pkg` | `dbmodel` | Go package name for generated files | | `--pkg` | `dbmodel` | Go package name for generated files |
| `--clean` | false | Remove existing model files before generation |
### Interactive Mode (Default) ### Interactive Mode (Default)
@@ -189,6 +192,18 @@ ctx := context.Background()
err := gg.GenModels(ctx) err := gg.GenModels(ctx)
``` ```
## GenConfig
Configuration for model generation.
| Field | Default | Description |
|-----------------|--------------------------|---------------------------------------------------|
| `OutputDir` | `./app/model/dbmodel` | Directory for generated files |
| `PkgName` | `dbmodel` | Go package name |
| `TableFilter` | `ps_.*` | Regex pattern to match table names |
| `SelectedTables`| *(none)* | Specific tables to generate (overrides filter) |
| `Clean` | `true` | Remove existing files before generation |
## Generated Models ## Generated Models
After generation, each model file contains a struct and a `Cols` variable: After generation, each model file contains a struct and a `Cols` variable:
@@ -202,57 +217,57 @@ type Product struct {
} }
var ProductCols = struct { var ProductCols = struct {
ID Field ID gormcol.Field
Name Field Name gormcol.Field
Price Field Price gormcol.Field
}{ }{
ID: Field{Table: "ps_product", Column: "id_product"}, ID: gormcol.Field{}.Set((&Product{}).TableName(), "id_product"),
Name: Field{Table: "ps_product", Column: "name"}, Name: gormcol.Field{}.Set((&Product{}).TableName(), "name"),
Price: Field{Table: "ps_product", Column: "price"}, Price: gormcol.Field{}.Set((&Product{}).TableName(), "price"),
} }
``` ```
## Using Generated Models ## Using Generated Models
### GORM queries with type-safe columns ### Table-qualified columns
Use `ColumnOnTable` for table-qualified column references in GORM clauses: Use `TabCol()` for table-qualified column references:
```go ```go
import "git.ma-al.com/goc_marek/gormcol" import "git.ma-al.com/goc_marek/gormcol"
// Where clauses // Where clauses
db.Where( db.Where(
gormcol.ColumnOnTable(model.ProductCols.Price) + " > ?", model.ProductCols.Price.TabCol() + " > ?",
100.0, 100.0,
).Find(&products) ).Find(&products)
// Order // Order
db.Order(gormcol.ColumnOnTable(model.ProductCols.Name) + " ASC").Find(&products) db.Order(model.ProductCols.Name.TabCol() + " ASC").Find(&products)
// Joins // Joins
db.Joins("JOIN ps_category ON " + db.Joins("JOIN ps_category ON " +
gormcol.ColumnOnTable(model.ProductCols.ID) + " = ps_category.id_product", model.ProductCols.ID.TabCol() + " = ps_category.id_product",
).Find(&products) ).Find(&products)
``` ```
### Unqualified column names ### Unqualified column names
Use `Column` when the table is already scoped: Use `Col()` when the table is already scoped:
```go ```go
db.Select(gormcol.Column(model.ProductCols.Name)).Find(&products) db.Select(model.ProductCols.Name.Col()).Find(&products)
// Raw queries // Raw queries
db.Raw("SELECT " + gormcol.Column(model.ProductCols.Name) + " FROM ps_product").Scan(&names) db.Raw("SELECT " + model.ProductCols.Name.Col() + " FROM ps_product").Scan(&names)
``` ```
### Table name ### Table name
Use `TableField` to get the table name from a column descriptor: Use `Tab()` to get the table name from a column descriptor:
```go ```go
table := gormcol.TableField(model.ProductCols.ID) // "ps_product" table := model.ProductCols.ID.Tab() // "ps_product"
``` ```
## Dependencies ## Dependencies

View File

@@ -16,11 +16,33 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
// version returns the current version from git tag and last modification date.
func version() string {
tag := "dev"
if out, err := exec.Command("git", "describe", "--tags", "--abbrev=0").Output(); err == nil {
tag = strings.TrimSpace(string(out))
}
date := ""
if out, err := exec.Command("git", "log", "-1", "--format=%ci").Output(); err == nil {
date = strings.TrimSpace(string(out))
if len(date) >= 10 {
date = date[:10]
}
}
if date != "" {
return fmt.Sprintf("%s (%s)", tag, date)
}
return tag
}
// main is the entry point for the gormcol-gen CLI tool. // main is the entry point for the gormcol-gen CLI tool.
// It parses flags, loads configuration from .env, connects to the database, // It parses flags, loads configuration from .env, connects to the database,
// and generates GORM models with column descriptors. // and generates GORM models with column descriptors.
func main() { func main() {
versionFlag := flag.Bool("version", false, "display version information")
dsn := flag.String("dsn", "", "database DSN (e.g. user:pass@tcp(host:3306)/dbname)") dsn := flag.String("dsn", "", "database DSN (e.g. user:pass@tcp(host:3306)/dbname)")
filter := flag.String("filter", "", "regex to match table names (triggers batch mode)") filter := flag.String("filter", "", "regex to match table names (triggers batch mode)")
all := flag.Bool("all", false, "generate all tables matching filter (shows confirmation)") all := flag.Bool("all", false, "generate all tables matching filter (shows confirmation)")
@@ -44,7 +66,15 @@ func main() {
flag.Parse() flag.Parse()
// Get DSN from flag or environment variable. // Display version on startup.
fmt.Printf("gormcol %s\n", version())
// Display version if requested.
if *versionFlag {
return
}
// Connect to the database.
dsnValue := *dsn dsnValue := *dsn
if dsnValue == "" { if dsnValue == "" {
dsnValue = os.Getenv("DSN") dsnValue = os.Getenv("DSN")

View File

@@ -181,37 +181,6 @@ func generateColsVarBlock(si *structInfo) string {
return b.String() return b.String()
} }
// findGoMod searches upward from startDir for a go.mod file.
func findGoMod(startDir string) (string, error) {
dir := startDir
for {
path := filepath.Join(dir, "go.mod")
if _, err := os.Stat(path); err == nil {
return path, nil
}
parent := filepath.Dir(dir)
if parent == dir {
return "", fmt.Errorf("go.mod not found from %s", startDir)
}
dir = parent
}
}
// readModulePath extracts the module path from a go.mod file.
func readModulePath(goModPath string) (string, error) {
content, err := os.ReadFile(goModPath)
if err != nil {
return "", err
}
for _, line := range strings.Split(string(content), "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "module ") {
return strings.TrimSpace(strings.TrimPrefix(line, "module ")), nil
}
}
return "", fmt.Errorf("module directive not found in %s", goModPath)
}
// generateCols appends <Model>Cols variables to generated model files. // generateCols appends <Model>Cols variables to generated model files.
// It parses each .go file in the output directory, extracts struct fields // It parses each .go file in the output directory, extracts struct fields
// with gorm column tags, and generates type-safe Field descriptors. // with gorm column tags, and generates type-safe Field descriptors.
@@ -231,15 +200,7 @@ func (m *GormGen) generateCols() error {
return err return err
} }
goModPath, err := findGoMod(absDir) gormcolImport := `"git.ma-al.com/goc_marek/gormcol"`
if err != nil {
return err
}
modulePath, err := readModulePath(goModPath)
if err != nil {
return err
}
gormcolImport := fmt.Sprintf("%q", modulePath+"")
var fileFilter *regexp.Regexp var fileFilter *regexp.Regexp
if len(m.cfg.SelectedTables) > 0 { if len(m.cfg.SelectedTables) > 0 {

BIN
gormcol

Binary file not shown.

View File

@@ -1,17 +1,3 @@
// Package gormcol provides type-safe GORM column descriptors.
//
// This package enables defining struct-like variables that map Go field names
// to database column names, allowing type-safe queries in GORM.
//
// Example usage:
//
// var PsAccessCols = struct {
// IDProfile gormcol.Field
// IDAuthorizationRole gormcol.Field
// }{
// IDProfile: gormcol.Field{}.Set((&PsAccess{}).TableName(), "id_profile"),
// IDAuthorizationRole: gormcol.Field{}.Set((&PsAccess{}).TableName(), "id_authorization_role"),
// }
package gormcol package gormcol
// Field represents a GORM column descriptor. // Field represents a GORM column descriptor.
@@ -21,18 +7,25 @@ type Field struct {
column string // Database column name column string // Database column name
} }
// TabCol returns the fully qualified "table.column" reference.
func (f Field) TabCol() string { func (f Field) TabCol() string {
return f.table + "." + f.column return f.table + "." + f.column
} }
// Col returns the column name without table qualification.
func (f Field) Col() string { func (f Field) Col() string {
return f.column return f.column
} }
// Tab returns the table name.
func (f Field) Tab() string { func (f Field) Tab() string {
return f.table return f.table
} }
// Set initializes a Field with the given table and column names.
// Use with TableName() for type-safe initialization, e.g.:
//
// Field{}.Set((&MyModel{}).TableName(), "column_name")
func (f Field) Set(tab, field string) Field { func (f Field) Set(tab, field string) Field {
return Field{ return Field{
table: tab, table: tab,