Skip to content

Commit 1a7be25

Browse files
uptutuCopilotjinzhu
authored
Fix code generation to support pointer embedding of gorm.Model and include base fields (#16)
* fix #15 *gorm.Model * fix #15 *gorm.Model * Apply suggestion from @Copilot Co-authored-by: Copilot <[email protected]> * caching findGoModDir(p.inputPath) * fix models examples * fix models examples * fix models examples * test: add created CreditCard fields for test *gorm.Model * Refactor goModDir comments for clarity --------- Co-authored-by: Copilot <[email protected]> Co-authored-by: Jinzhu <[email protected]>
1 parent eada5ff commit 1a7be25

File tree

5 files changed

+203
-17
lines changed

5 files changed

+203
-17
lines changed

.gitignore

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,145 @@
1+
# Binaries for programs and plugins
2+
*.exe
3+
*.exe~
4+
*.dll
5+
*.so
6+
*.dylib
7+
8+
# Test binary, built with `go test -c`
9+
*.test
10+
11+
# Output of the go coverage tool, specifically when used with LiteIDE
12+
*.out
13+
14+
# Dependency directories (remove the comment below to include it)
15+
# vendor/
16+
17+
# Go workspace file
18+
go.work
19+
20+
# Build output
121
g
222
gorm
23+
/bin/
24+
/build/
25+
/dist/
26+
27+
# IDE and Editor files
28+
.idea/
29+
.vscode/
30+
*.swp
31+
*.swo
32+
*~
33+
34+
# OS generated files
35+
.DS_Store
36+
.DS_Store?
37+
._*
38+
.Spotlight-V100
39+
.Trashes
40+
ehthumbs.db
41+
Thumbs.db
42+
43+
# Log files
44+
*.log
45+
46+
# Runtime data
47+
pids
48+
*.pid
49+
*.seed
50+
*.pid.lock
51+
52+
# Coverage directory used by tools like istanbul
53+
coverage/
54+
55+
# nyc test coverage
56+
.nyc_output
57+
58+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
59+
.grunt
60+
61+
# Bower dependency directory (https://bower.io/)
62+
bower_components
63+
64+
# node_modules (if using Node.js tools)
65+
node_modules/
66+
67+
# Temporary folders
68+
tmp/
69+
temp/
70+
71+
# Go module download cache
72+
$GOPATH/pkg/mod/
73+
74+
# Go build cache
75+
$GOCACHE/
76+
77+
# Air live reload
78+
tmp/
79+
80+
# Local environment variables
81+
.env
82+
.env.local
83+
.env.*.local
84+
85+
# Database files
86+
*.db
87+
*.sqlite
88+
*.sqlite3
89+
90+
# Compiled Object files, Static and Dynamic libs (Shared Objects)
91+
*.o
92+
*.a
93+
94+
# Folders
95+
_obj
96+
_test
97+
98+
# Architecture specific extensions/prefixes
99+
*.[568vq]
100+
[568vq].out
101+
102+
# cgo generated files
103+
_cgo_gotypes.go
104+
_cgo_export.*
105+
106+
# test generated files
107+
*_test_gen.go
108+
109+
# Ignore generated documentation
110+
*.html
111+
112+
# Ignore backup files
113+
*.bak
114+
*.backup
115+
*.orig
116+
117+
# Ignore profiling files
118+
*.prof
119+
cpu.out
120+
mem.out
121+
profile.out
122+
123+
# Ignore security sensitive files
124+
*.pem
125+
*.key
126+
*.crt
127+
*.p12
128+
129+
# Docker files (if not needed in repo)
130+
# Dockerfile
131+
# docker-compose.yml
132+
# .dockerignore
133+
134+
# Kubernetes files (if not needed in repo)
135+
# *.yaml
136+
# *.yml
137+
138+
# Generated code (if applicable)
139+
# *_gen.go
140+
# *.pb.go
141+
142+
# Config files with sensitive data
143+
config.json
144+
secrets.yaml
145+
secrets.yml

examples/models/user.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,8 @@ type Language struct {
6464
Code string `gorm:"primarykey"`
6565
Name string
6666
}
67+
68+
type CreditCard struct {
69+
*gorm.Model
70+
Number string
71+
}

examples/output/models/user.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/output/models_field_helpers_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,13 @@ func TestGeneratedModels_FieldTypes(t *testing.T) {
396396
// Language
397397
_ field.String = generated.Language.Code
398398
_ field.String = generated.Language.Name
399+
400+
// CreditCard
401+
_ field.Number[uint] = generated.CreditCard.ID
402+
_ field.Time = generated.CreditCard.CreatedAt
403+
_ field.Time = generated.CreditCard.UpdatedAt
404+
_ field.Field[gorm.DeletedAt] = generated.CreditCard.DeletedAt
405+
_ field.String = generated.CreditCard.Number
399406
)
400407
}
401408

internal/gen/generator.go

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type (
3838
applicableConfigs []*genconfig.Config
3939
inputPath string
4040
relPath string
41+
goModDir string
4142
Generator *Generator
4243
}
4344
Import struct {
@@ -245,6 +246,7 @@ func (g *Generator) processFile(inputFile, inputRoot string) error {
245246
Package: f.Name.Name,
246247
inputPath: inputFile,
247248
relPath: relPath,
249+
goModDir: findGoModDir(inputFile),
248250
Generator: g,
249251
}
250252

@@ -428,7 +430,7 @@ func (f Field) Type() string {
428430
return fmt.Sprintf("field.Number[%s]", goType)
429431
}
430432

431-
if typ := loadNamedType(findGoModDir(f.file.inputPath), f.file.getFullImportPath(pkgName), typName); typ != nil {
433+
if typ := loadNamedType(f.file.goModDir, f.file.getFullImportPath(pkgName), typName); typ != nil {
432434
if ImplementsAllowedInterfaces(typ) { // For interface-implementing types, use generic Field
433435
return fmt.Sprintf("field.Field[%s]", filepath.Base(goType))
434436
}
@@ -742,33 +744,48 @@ func (p *File) getFullImportPath(shortName string) string {
742744

743745
// handleAnonymousEmbedding processes anonymous embedded fields and returns true if handled
744746
func (p *File) handleAnonymousEmbedding(field *ast.Field, pkgName string, s *Struct) bool {
745-
switch t := field.Type.(type) {
747+
// Helper function to add fields from embedded struct
748+
addEmbeddedFields := func(structType *ast.StructType, typeName, embeddedPkgName string) bool {
749+
sub := p.processStructType(&ast.TypeSpec{Name: &ast.Ident{Name: typeName}}, structType, embeddedPkgName)
750+
s.Fields = append(s.Fields, sub.Fields...)
751+
return true
752+
}
753+
754+
// Helper function to load and process external struct type
755+
loadAndProcessExternalStruct := func(pkgName, typeName string) bool {
756+
st, err := loadNamedStructType(p.goModDir, p.getFullImportPath(pkgName), typeName)
757+
if err != nil || st == nil {
758+
return false
759+
}
760+
return addEmbeddedFields(st, typeName, pkgName)
761+
}
762+
763+
// Unwrap pointer types to get the underlying type
764+
fieldType := field.Type
765+
if starExpr, ok := fieldType.(*ast.StarExpr); ok {
766+
fieldType = starExpr.X
767+
}
768+
769+
switch t := fieldType.(type) {
746770
case *ast.Ident:
747-
// Local type embedding
771+
// Local type embedding (e.g., BaseStruct or *BaseStruct)
748772
if t.Obj != nil {
749773
if ts, ok := t.Obj.Decl.(*ast.TypeSpec); ok {
750774
if st, ok := ts.Type.(*ast.StructType); ok {
751-
sub := p.processStructType(ts, st, pkgName)
752-
s.Fields = append(s.Fields, sub.Fields...)
753-
return true
775+
return addEmbeddedFields(st, t.Name, pkgName)
754776
}
755777
}
756778
}
779+
757780
case *ast.SelectorExpr:
758-
// External package type embedding
781+
// External package type embedding (e.g., pkg.BaseStruct or *pkg.BaseStruct)
759782
if pkgIdent, ok := t.X.(*ast.Ident); ok {
760-
st, err := loadNamedStructType(findGoModDir(p.inputPath), p.getFullImportPath(pkgIdent.Name), t.Sel.Name)
761-
if err == nil && st != nil {
762-
sub := p.processStructType(&ast.TypeSpec{Name: &ast.Ident{Name: t.Sel.Name}}, st, pkgIdent.Name)
763-
s.Fields = append(s.Fields, sub.Fields...)
764-
return true
765-
}
783+
return loadAndProcessExternalStruct(pkgIdent.Name, t.Sel.Name)
766784
}
785+
767786
case *ast.StructType:
768-
// Anonymous inline struct embedding
769-
sub := p.processStructType(&ast.TypeSpec{Name: &ast.Ident{Name: "AnonymousStruct"}}, t, pkgName)
770-
s.Fields = append(s.Fields, sub.Fields...)
771-
return true
787+
// Anonymous inline struct embedding (e.g., struct{...})
788+
return addEmbeddedFields(t, "AnonymousStruct", pkgName)
772789
}
773790

774791
return false

0 commit comments

Comments
 (0)