diff --git a/internal/cmd/option.go b/internal/cmd/option.go index 68678b4..b51c298 100644 --- a/internal/cmd/option.go +++ b/internal/cmd/option.go @@ -18,6 +18,7 @@ type Options struct { useContext bool useValueModifier bool config string + targetDir string } func (o *Options) packageDir() string { @@ -45,11 +46,12 @@ func (o *Options) buildGenOptions() []gen.OptionFunc { type GeneratorType string const ( - GeneratorTypeYo GeneratorType = "yo" - GeneratorTypeEnt GeneratorType = "ent" + GeneratorTypeYo GeneratorType = "yo" + GeneratorTypeEnt GeneratorType = "ent" + GeneratorTypeStructs GeneratorType = "structs" ) -var generatorTypes = []GeneratorType{GeneratorTypeYo, GeneratorTypeEnt} +var generatorTypes = []GeneratorType{GeneratorTypeYo, GeneratorTypeEnt, GeneratorTypeStructs} func (t GeneratorType) Validate() error { if t == "" { diff --git a/internal/cmd/root.go b/internal/cmd/root.go index aa31f10..fc5a4e2 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -11,6 +11,7 @@ import ( "github.com/earlgray283/fixgen/internal/config" "github.com/earlgray283/fixgen/internal/gen" gen_ent "github.com/earlgray283/fixgen/internal/gen/ent" + gen_structs "github.com/earlgray283/fixgen/internal/gen/structs" gen_yo "github.com/earlgray283/fixgen/internal/gen/yo" "github.com/spf13/cobra" ) @@ -32,8 +33,11 @@ func NewCommand() *cobra.Command { if err := generatorType.Validate(); err != nil { return fmt.Errorf("%+w", err) } + if generatorType == GeneratorTypeStructs && opts.targetDir == "" { + return errors.New("`--target-dir` is required for structs generator") + } - generator, err := loadGenerator(generatorType, ".") + generator, err := loadGenerator(generatorType, ".", opts.targetDir) if err != nil { return fmt.Errorf("failed to load generator: %+w", err) } @@ -71,16 +75,19 @@ func NewCommand() *cobra.Command { fs.BoolVar(&opts.useContext, "use-context", false, "add context.Context argument for the generated functions") fs.BoolVar(&opts.useValueModifier, "use-value-modifier", false, "use value modifier for the generated functions") fs.StringVarP(&opts.config, "config", "c", "fixgen.yaml", "config file path") + fs.StringVar(&opts.targetDir, "target-dir", "", "target directory for the generated files") return cmd } -func loadGenerator(typ GeneratorType, workDir string) (gen.Generator, error) { +func loadGenerator(typ GeneratorType, workDir, targetDir string) (gen.Generator, error) { switch typ { case GeneratorTypeEnt: return gen_ent.NewGenerator(workDir) case GeneratorTypeYo: return gen_yo.NewGenerator(workDir) + case GeneratorTypeStructs: + return gen_structs.NewGenerator(workDir, targetDir) default: return nil, fmt.Errorf("unrecognized generator type: %s", typ) } diff --git a/internal/gen/generator.go b/internal/gen/generator.go index e9cd17f..b7dc905 100644 --- a/internal/gen/generator.go +++ b/internal/gen/generator.go @@ -78,7 +78,7 @@ func GenerateWithFormat[G Generator](g G, c *config.Config, opts ...OptionFunc) for _, f := range files { content, err := Format(append(header, f.Content...)) if err != nil { - return nil, fmt.Errorf("failed to Format: %+w", err) + return nil, fmt.Errorf("failed to Format(%s): %+w", f.Name, err) } f.Content = content } diff --git a/internal/gen/option.go b/internal/gen/option.go index 45eeab3..2147402 100644 --- a/internal/gen/option.go +++ b/internal/gen/option.go @@ -4,6 +4,7 @@ type option struct { packageName string // default: "fixture" useContext bool useValueModifier bool + targetDir string } func defaultOption() *option { @@ -11,6 +12,7 @@ func defaultOption() *option { packageName: "fixture", useContext: false, useValueModifier: false, + targetDir: "", } } @@ -39,3 +41,9 @@ func UseValueModifier() OptionFunc { o.useValueModifier = true } } + +func TargetDir(targetDir string) OptionFunc { + return func(o *option) { + o.targetDir = targetDir + } +} diff --git a/internal/gen/structs/structs.go b/internal/gen/structs/structs.go new file mode 100644 index 0000000..acb2577 --- /dev/null +++ b/internal/gen/structs/structs.go @@ -0,0 +1,95 @@ +package datastore + +import ( + "fmt" + "maps" + "path/filepath" + "strings" + + "github.com/earlgray283/fixgen/internal/caseconv" + "github.com/earlgray283/fixgen/internal/gen" + "github.com/earlgray283/fixgen/internal/load" + "github.com/earlgray283/fixgen/internal/templates" +) + +type Generator struct { + packagePath string + dirPath string + filepaths []string +} + +var _ gen.Generator = (*Generator)(nil) + +func NewGenerator(workDir, packageDirPath string) (*Generator, error) { + goModulePath, err := gen.LoadGoModulePath(workDir) + if err != nil { + return nil, fmt.Errorf("failed to load go module path: %+w", err) + } + + filepaths, err := gen.ReadDir(packageDirPath) + if err != nil { + return nil, fmt.Errorf("failed to read dir: %+w", err) + } + + rel, err := filepath.Rel(".", packageDirPath) + if err != nil { + return nil, fmt.Errorf("failed to get relative path: %+w", err) + } + + return &Generator{ + packagePath: strings.Join([]string{goModulePath, rel}, "/"), + dirPath: packageDirPath, + filepaths: filepaths, + }, nil +} + +// Generate implements gen.Generator. +func (g *Generator) Generate(structInfos []*load.StructInfo, data map[string]any) ([]*gen.File, error) { + files := make([]*gen.File, 0, len(structInfos)+1) + + content, err := templates.Execute(templates.TmplStructsCommonFile, nil) + if err != nil { + return nil, err + } + files = append(files, &gen.File{ + Name: "structs_common", + Content: content, + }) + + for _, si := range structInfos { + file, err := g.execute(si, data) + if err != nil { + return nil, err + } + files = append(files, file) + } + + return files, nil +} + +func (g *Generator) execute(si *load.StructInfo, data map[string]any) (*gen.File, error) { + newData := map[string]any{ + "TableName": si.Name, + "Fields": si.Fields, + } + maps.Copy(newData, data) + + content, err := templates.Execute(templates.TmplStructsFile, newData) + if err != nil { + return nil, err + } + + return &gen.File{ + Name: caseconv.ConvertPascalToSnake(si.Name), + Content: content, + }, nil +} + +// PackageInfo implements gen.Generator. +func (g *Generator) PackageInfo() *gen.PackageInfo { + return &gen.PackageInfo{ + PackagePath: g.packagePath, + PackageAlias: "structs_gen", + PackageLocation: g.dirPath, + } +} diff --git a/internal/gen/util.go b/internal/gen/util.go index 6478024..477e1ba 100644 --- a/internal/gen/util.go +++ b/internal/gen/util.go @@ -28,9 +28,18 @@ func findAndReadDir(rootDir string, keyFunc func(d fs.DirEntry) bool) (string, [ } dirPath := filepath.Dir(keyPath) + filepaths, err := ReadDir(dirPath) + if err != nil { + return "", nil, fmt.Errorf("failed to read dir: %+w", err) + } + + return dirPath, filepaths, nil +} + +func ReadDir(dirPath string) ([]string, error) { entries, err := os.ReadDir(dirPath) if err != nil { - return "", nil, err + return nil, err } filepaths := make([]string, 0, len(entries)) @@ -41,7 +50,7 @@ func findAndReadDir(rootDir string, keyFunc func(d fs.DirEntry) bool) (string, [ filepaths = append(filepaths, filepath.Join(dirPath, e.Name())) } - return dirPath, filepaths, nil + return filepaths, nil } func findByKey(rootDir string, keyFunc func(d fs.DirEntry) bool) (string, error) { diff --git a/internal/templates/structs.go.tmpl b/internal/templates/structs.go.tmpl new file mode 100644 index 0000000..eb3c69f --- /dev/null +++ b/internal/templates/structs.go.tmpl @@ -0,0 +1,35 @@ +func Create{{.TableName}}({{ if .UseContext}}ctx context.Context,{{ end }} t *testing.T, db Inserter[*structs_gen.{{ .TableName }}], m {{ if not .UseValueModifier }}*{{ end }}structs_gen.{{.TableName}}, opts ...func(*structs_gen.{{ .TableName }})) *structs_gen.{{.TableName}} { + t.Helper() + + tbl := &structs_gen.{{.TableName}} { + {{ range $i, $f := .Fields -}} + {{ if $f.DefaultValue }} {{ $f.Name }}: {{ $f.DefaultValue }}, {{ if $f.IsOverwritten }} // {{ $f.Name }} is overwritten {{ end }} + {{ else if $f.Type.IsSlice }} // {{ $f.Name }} is slice + {{ else -}} // {{ $f.Name }} is unknown + {{ end }} + {{- end -}} + } + + {{ range $i, $f := .Fields -}} + {{ if $f.MustOverwrite -}} tbl.{{ $f.Name }} = m.{{ $f.Name }} // must overwrite + {{ else -}} + if + {{ if $f.IsModifiedCond -}} {{ $f.IsModifiedCond }} + {{- else if $f.Type.IsSlice -}} len(m.{{ $f.Name }}) > 0 + {{- else -}} isModified(m.{{ $f.Name }}) + {{- end -}} { + tbl.{{ $f.Name }} = m.{{ $f.Name }} + } + {{ end -}} + {{- end -}} + for _, opt := range opts { + opt(tbl) + } + + m, err := db.Insert({{ if .UseContext }}ctx{{ else }}context.Background(){{ end}}, tbl) + if err != nil { + t.Fatal(err) + } + + return m +} diff --git a/internal/templates/structs_common.go.tmpl b/internal/templates/structs_common.go.tmpl new file mode 100644 index 0000000..7881e8e --- /dev/null +++ b/internal/templates/structs_common.go.tmpl @@ -0,0 +1,3 @@ +type Inserter[M any] interface { + Insert(ctx context.Context, m M) (M, error) +} diff --git a/internal/templates/templates.go b/internal/templates/templates.go index f636d48..0f46070 100644 --- a/internal/templates/templates.go +++ b/internal/templates/templates.go @@ -9,11 +9,13 @@ import ( var ( //go:embed * - embedFs embed.FS - TmplEntFile *template.Template - TmplYoFile *template.Template - TmplCommonFile *template.Template - TmplHeaderFile *template.Template + embedFs embed.FS + TmplEntFile *template.Template + TmplYoFile *template.Template + TmplCommonFile *template.Template + TmplHeaderFile *template.Template + TmplStructsFile *template.Template + TmplStructsCommonFile *template.Template ) func init() { @@ -21,6 +23,8 @@ func init() { TmplYoFile = parseFS("yo.go.tmpl") TmplCommonFile = parseFS("common.go.tmpl") TmplHeaderFile = parseFS("header.go.tmpl") + TmplStructsFile = parseFS("structs.go.tmpl") + TmplStructsCommonFile = parseFS("structs_common.go.tmpl") } func parseFS(pattern string) *template.Template { diff --git a/test/fixgen_test.go b/test/fixgen_test.go index 37e0945..823b1ec 100644 --- a/test/fixgen_test.go +++ b/test/fixgen_test.go @@ -12,6 +12,7 @@ import ( "github.com/earlgray283/fixgen/internal/config" "github.com/earlgray283/fixgen/internal/gen" gen_ent "github.com/earlgray283/fixgen/internal/gen/ent" + gen_structs "github.com/earlgray283/fixgen/internal/gen/structs" gen_yo "github.com/earlgray283/fixgen/internal/gen/yo" ) @@ -50,7 +51,7 @@ func Test_GoldenTest(t *testing.T) { } } - generators := []string{"yo", "ent"} + generators := []string{"yo", "ent", "structs"} for _, typ := range generators { testDir := filepath.Join(wd, typ, "test") @@ -131,6 +132,8 @@ func mustNewGenerator(t *testing.T, typ string) gen.Generator { g, err = gen_yo.NewGenerator(".") case "ent": g, err = gen_ent.NewGenerator(".") + case "structs": + g, err = gen_structs.NewGenerator(".", "models") default: t.Fatalf("unrecognized generator type `%s`", typ) } diff --git a/test/structs/test/go.mod b/test/structs/test/go.mod new file mode 100644 index 0000000..827bfb1 --- /dev/null +++ b/test/structs/test/go.mod @@ -0,0 +1,7 @@ +module structs + +go 1.24.1 + +require github.com/samber/lo v1.49.1 + +require golang.org/x/text v0.21.0 // indirect diff --git a/test/structs/test/go.sum b/test/structs/test/go.sum new file mode 100644 index 0000000..f01c319 --- /dev/null +++ b/test/structs/test/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew= +github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/test/structs/test/models/todo.go b/test/structs/test/models/todo.go new file mode 100644 index 0000000..e7dcf25 --- /dev/null +++ b/test/structs/test/models/todo.go @@ -0,0 +1,13 @@ +package models + +import "time" + +type Todo struct { + ID int64 + Title string + Description string + Tags []string + CreatedAt time.Time + UpdatedAt *time.Time + DoneAt *time.Time +} diff --git a/test/structs/test/models/user.go b/test/structs/test/models/user.go new file mode 100644 index 0000000..e40c224 --- /dev/null +++ b/test/structs/test/models/user.go @@ -0,0 +1,12 @@ +package models + +import "time" + +type User struct { + ID int64 + Name string + IconURL string + UserType int64 + CreatedAt time.Time + UpdatedAt *time.Time +} diff --git a/test/structs/test/testdata-context/fixgen.yaml b/test/structs/test/testdata-context/fixgen.yaml new file mode 100644 index 0000000..7ebe424 --- /dev/null +++ b/test/structs/test/testdata-context/fixgen.yaml @@ -0,0 +1,16 @@ +structs: + Todo: + fields: + Title: + overwrite: true + User: + fields: + IconURL: + expr: fmt.Sprintf("http://example.com/%d", 123456) + Name: + value: Taro Yamada + UserType: + value: 1 + isModifiedCond: m.UserType != 1 +imports: +- package: fmt diff --git a/test/structs/test/testdata-context/goldie-common.go b/test/structs/test/testdata-context/goldie-common.go new file mode 100644 index 0000000..9c73a01 --- /dev/null +++ b/test/structs/test/testdata-context/goldie-common.go @@ -0,0 +1,8 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +func isModified[T comparable](v T) bool { + var zero T + return v != zero +} diff --git a/test/structs/test/testdata-context/goldie-structs_common.go b/test/structs/test/testdata-context/goldie-structs_common.go new file mode 100644 index 0000000..13caa89 --- /dev/null +++ b/test/structs/test/testdata-context/goldie-structs_common.go @@ -0,0 +1,9 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import "context" + +type Inserter[M any] interface { + Insert(ctx context.Context, m M) (M, error) +} diff --git a/test/structs/test/testdata-context/goldie-todo.go b/test/structs/test/testdata-context/goldie-todo.go new file mode 100644 index 0000000..79f34e6 --- /dev/null +++ b/test/structs/test/testdata-context/goldie-todo.go @@ -0,0 +1,57 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import ( + "context" + "math/rand/v2" + structs_gen "structs/models" + "testing" + "time" + + "github.com/samber/lo" +) + +func CreateTodo(ctx context.Context, t *testing.T, db Inserter[*structs_gen.Todo], m *structs_gen.Todo, opts ...func(*structs_gen.Todo)) *structs_gen.Todo { + t.Helper() + + tbl := &structs_gen.Todo{ + ID: rand.Int64(), + Title: lo.RandomString(32, lo.AlphanumericCharset), + Description: lo.RandomString(32, lo.AlphanumericCharset), + // Tags is slice + CreatedAt: time.Now(), + // UpdatedAt is unknown + // DoneAt is unknown + } + + if isModified(m.ID) { + tbl.ID = m.ID + } + tbl.Title = m.Title // must overwrite + if isModified(m.Description) { + tbl.Description = m.Description + } + if len(m.Tags) > 0 { + tbl.Tags = m.Tags + } + if isModified(m.CreatedAt) { + tbl.CreatedAt = m.CreatedAt + } + if isModified(m.UpdatedAt) { + tbl.UpdatedAt = m.UpdatedAt + } + if isModified(m.DoneAt) { + tbl.DoneAt = m.DoneAt + } + for _, opt := range opts { + opt(tbl) + } + + m, err := db.Insert(ctx, tbl) + if err != nil { + t.Fatal(err) + } + + return m +} diff --git a/test/structs/test/testdata-context/goldie-user.go b/test/structs/test/testdata-context/goldie-user.go new file mode 100644 index 0000000..9522ee5 --- /dev/null +++ b/test/structs/test/testdata-context/goldie-user.go @@ -0,0 +1,54 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import ( + "context" + "fmt" + "math/rand/v2" + structs_gen "structs/models" + "testing" + "time" +) + +func CreateUser(ctx context.Context, t *testing.T, db Inserter[*structs_gen.User], m *structs_gen.User, opts ...func(*structs_gen.User)) *structs_gen.User { + t.Helper() + + tbl := &structs_gen.User{ + ID: rand.Int64(), + Name: "Taro Yamada", // Name is overwritten + IconURL: fmt.Sprintf("http://example.com/%d", 123456), // IconURL is overwritten + UserType: 1, // UserType is overwritten + CreatedAt: time.Now(), + // UpdatedAt is unknown + } + + if isModified(m.ID) { + tbl.ID = m.ID + } + if isModified(m.Name) { + tbl.Name = m.Name + } + if isModified(m.IconURL) { + tbl.IconURL = m.IconURL + } + if m.UserType != 1 { + tbl.UserType = m.UserType + } + if isModified(m.CreatedAt) { + tbl.CreatedAt = m.CreatedAt + } + if isModified(m.UpdatedAt) { + tbl.UpdatedAt = m.UpdatedAt + } + for _, opt := range opts { + opt(tbl) + } + + m, err := db.Insert(ctx, tbl) + if err != nil { + t.Fatal(err) + } + + return m +} diff --git a/test/structs/test/testdata-math-v1/fixgen.yaml b/test/structs/test/testdata-math-v1/fixgen.yaml new file mode 100644 index 0000000..128edff --- /dev/null +++ b/test/structs/test/testdata-math-v1/fixgen.yaml @@ -0,0 +1,18 @@ +defaultValuePolicy: + type: randlegacy +structs: + Todo: + fields: + Title: + overwrite: true + User: + fields: + IconURL: + expr: fmt.Sprintf("http://example.com/%d", 123456) + Name: + value: Taro Yamada + UserType: + value: 1 + isModifiedCond: m.UserType != 1 +imports: +- package: fmt diff --git a/test/structs/test/testdata-math-v1/goldie-common.go b/test/structs/test/testdata-math-v1/goldie-common.go new file mode 100644 index 0000000..9c73a01 --- /dev/null +++ b/test/structs/test/testdata-math-v1/goldie-common.go @@ -0,0 +1,8 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +func isModified[T comparable](v T) bool { + var zero T + return v != zero +} diff --git a/test/structs/test/testdata-math-v1/goldie-structs_common.go b/test/structs/test/testdata-math-v1/goldie-structs_common.go new file mode 100644 index 0000000..13caa89 --- /dev/null +++ b/test/structs/test/testdata-math-v1/goldie-structs_common.go @@ -0,0 +1,9 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import "context" + +type Inserter[M any] interface { + Insert(ctx context.Context, m M) (M, error) +} diff --git a/test/structs/test/testdata-math-v1/goldie-todo.go b/test/structs/test/testdata-math-v1/goldie-todo.go new file mode 100644 index 0000000..74785ae --- /dev/null +++ b/test/structs/test/testdata-math-v1/goldie-todo.go @@ -0,0 +1,57 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import ( + "context" + "math/rand" + structs_gen "structs/models" + "testing" + "time" + + "github.com/samber/lo" +) + +func CreateTodo(t *testing.T, db Inserter[*structs_gen.Todo], m *structs_gen.Todo, opts ...func(*structs_gen.Todo)) *structs_gen.Todo { + t.Helper() + + tbl := &structs_gen.Todo{ + ID: rand.Int63(), + Title: lo.RandomString(32, lo.AlphanumericCharset), + Description: lo.RandomString(32, lo.AlphanumericCharset), + // Tags is slice + CreatedAt: time.Now(), + // UpdatedAt is unknown + // DoneAt is unknown + } + + if isModified(m.ID) { + tbl.ID = m.ID + } + tbl.Title = m.Title // must overwrite + if isModified(m.Description) { + tbl.Description = m.Description + } + if len(m.Tags) > 0 { + tbl.Tags = m.Tags + } + if isModified(m.CreatedAt) { + tbl.CreatedAt = m.CreatedAt + } + if isModified(m.UpdatedAt) { + tbl.UpdatedAt = m.UpdatedAt + } + if isModified(m.DoneAt) { + tbl.DoneAt = m.DoneAt + } + for _, opt := range opts { + opt(tbl) + } + + m, err := db.Insert(context.Background(), tbl) + if err != nil { + t.Fatal(err) + } + + return m +} diff --git a/test/structs/test/testdata-math-v1/goldie-user.go b/test/structs/test/testdata-math-v1/goldie-user.go new file mode 100644 index 0000000..7d6031b --- /dev/null +++ b/test/structs/test/testdata-math-v1/goldie-user.go @@ -0,0 +1,54 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import ( + "context" + "fmt" + "math/rand" + structs_gen "structs/models" + "testing" + "time" +) + +func CreateUser(t *testing.T, db Inserter[*structs_gen.User], m *structs_gen.User, opts ...func(*structs_gen.User)) *structs_gen.User { + t.Helper() + + tbl := &structs_gen.User{ + ID: rand.Int63(), + Name: "Taro Yamada", // Name is overwritten + IconURL: fmt.Sprintf("http://example.com/%d", 123456), // IconURL is overwritten + UserType: 1, // UserType is overwritten + CreatedAt: time.Now(), + // UpdatedAt is unknown + } + + if isModified(m.ID) { + tbl.ID = m.ID + } + if isModified(m.Name) { + tbl.Name = m.Name + } + if isModified(m.IconURL) { + tbl.IconURL = m.IconURL + } + if m.UserType != 1 { + tbl.UserType = m.UserType + } + if isModified(m.CreatedAt) { + tbl.CreatedAt = m.CreatedAt + } + if isModified(m.UpdatedAt) { + tbl.UpdatedAt = m.UpdatedAt + } + for _, opt := range opts { + opt(tbl) + } + + m, err := db.Insert(context.Background(), tbl) + if err != nil { + t.Fatal(err) + } + + return m +} diff --git a/test/structs/test/testdata-value-modifier/fixgen.yaml b/test/structs/test/testdata-value-modifier/fixgen.yaml new file mode 100644 index 0000000..7ebe424 --- /dev/null +++ b/test/structs/test/testdata-value-modifier/fixgen.yaml @@ -0,0 +1,16 @@ +structs: + Todo: + fields: + Title: + overwrite: true + User: + fields: + IconURL: + expr: fmt.Sprintf("http://example.com/%d", 123456) + Name: + value: Taro Yamada + UserType: + value: 1 + isModifiedCond: m.UserType != 1 +imports: +- package: fmt diff --git a/test/structs/test/testdata-value-modifier/goldie-common.go b/test/structs/test/testdata-value-modifier/goldie-common.go new file mode 100644 index 0000000..9c73a01 --- /dev/null +++ b/test/structs/test/testdata-value-modifier/goldie-common.go @@ -0,0 +1,8 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +func isModified[T comparable](v T) bool { + var zero T + return v != zero +} diff --git a/test/structs/test/testdata-value-modifier/goldie-structs_common.go b/test/structs/test/testdata-value-modifier/goldie-structs_common.go new file mode 100644 index 0000000..13caa89 --- /dev/null +++ b/test/structs/test/testdata-value-modifier/goldie-structs_common.go @@ -0,0 +1,9 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import "context" + +type Inserter[M any] interface { + Insert(ctx context.Context, m M) (M, error) +} diff --git a/test/structs/test/testdata-value-modifier/goldie-todo.go b/test/structs/test/testdata-value-modifier/goldie-todo.go new file mode 100644 index 0000000..0a1753a --- /dev/null +++ b/test/structs/test/testdata-value-modifier/goldie-todo.go @@ -0,0 +1,57 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import ( + "context" + "math/rand/v2" + structs_gen "structs/models" + "testing" + "time" + + "github.com/samber/lo" +) + +func CreateTodo(t *testing.T, db Inserter[*structs_gen.Todo], m structs_gen.Todo, opts ...func(*structs_gen.Todo)) *structs_gen.Todo { + t.Helper() + + tbl := &structs_gen.Todo{ + ID: rand.Int64(), + Title: lo.RandomString(32, lo.AlphanumericCharset), + Description: lo.RandomString(32, lo.AlphanumericCharset), + // Tags is slice + CreatedAt: time.Now(), + // UpdatedAt is unknown + // DoneAt is unknown + } + + if isModified(m.ID) { + tbl.ID = m.ID + } + tbl.Title = m.Title // must overwrite + if isModified(m.Description) { + tbl.Description = m.Description + } + if len(m.Tags) > 0 { + tbl.Tags = m.Tags + } + if isModified(m.CreatedAt) { + tbl.CreatedAt = m.CreatedAt + } + if isModified(m.UpdatedAt) { + tbl.UpdatedAt = m.UpdatedAt + } + if isModified(m.DoneAt) { + tbl.DoneAt = m.DoneAt + } + for _, opt := range opts { + opt(tbl) + } + + m, err := db.Insert(context.Background(), tbl) + if err != nil { + t.Fatal(err) + } + + return m +} diff --git a/test/structs/test/testdata-value-modifier/goldie-user.go b/test/structs/test/testdata-value-modifier/goldie-user.go new file mode 100644 index 0000000..c65501e --- /dev/null +++ b/test/structs/test/testdata-value-modifier/goldie-user.go @@ -0,0 +1,54 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import ( + "context" + "fmt" + "math/rand/v2" + structs_gen "structs/models" + "testing" + "time" +) + +func CreateUser(t *testing.T, db Inserter[*structs_gen.User], m structs_gen.User, opts ...func(*structs_gen.User)) *structs_gen.User { + t.Helper() + + tbl := &structs_gen.User{ + ID: rand.Int64(), + Name: "Taro Yamada", // Name is overwritten + IconURL: fmt.Sprintf("http://example.com/%d", 123456), // IconURL is overwritten + UserType: 1, // UserType is overwritten + CreatedAt: time.Now(), + // UpdatedAt is unknown + } + + if isModified(m.ID) { + tbl.ID = m.ID + } + if isModified(m.Name) { + tbl.Name = m.Name + } + if isModified(m.IconURL) { + tbl.IconURL = m.IconURL + } + if m.UserType != 1 { + tbl.UserType = m.UserType + } + if isModified(m.CreatedAt) { + tbl.CreatedAt = m.CreatedAt + } + if isModified(m.UpdatedAt) { + tbl.UpdatedAt = m.UpdatedAt + } + for _, opt := range opts { + opt(tbl) + } + + m, err := db.Insert(context.Background(), tbl) + if err != nil { + t.Fatal(err) + } + + return m +} diff --git a/test/structs/test/testdata-zero/fixgen.yaml b/test/structs/test/testdata-zero/fixgen.yaml new file mode 100644 index 0000000..efa7dd9 --- /dev/null +++ b/test/structs/test/testdata-zero/fixgen.yaml @@ -0,0 +1,18 @@ +defaultValuePolicy: + type: zero +structs: + Todo: + fields: + Title: + overwrite: true + User: + fields: + IconURL: + expr: fmt.Sprintf("http://example.com/%d", 123456) + Name: + value: Taro Yamada + UserType: + value: 1 + isModifiedCond: m.UserType != 1 +imports: +- package: fmt diff --git a/test/structs/test/testdata-zero/goldie-common.go b/test/structs/test/testdata-zero/goldie-common.go new file mode 100644 index 0000000..9c73a01 --- /dev/null +++ b/test/structs/test/testdata-zero/goldie-common.go @@ -0,0 +1,8 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +func isModified[T comparable](v T) bool { + var zero T + return v != zero +} diff --git a/test/structs/test/testdata-zero/goldie-structs_common.go b/test/structs/test/testdata-zero/goldie-structs_common.go new file mode 100644 index 0000000..13caa89 --- /dev/null +++ b/test/structs/test/testdata-zero/goldie-structs_common.go @@ -0,0 +1,9 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import "context" + +type Inserter[M any] interface { + Insert(ctx context.Context, m M) (M, error) +} diff --git a/test/structs/test/testdata-zero/goldie-todo.go b/test/structs/test/testdata-zero/goldie-todo.go new file mode 100644 index 0000000..0e52958 --- /dev/null +++ b/test/structs/test/testdata-zero/goldie-todo.go @@ -0,0 +1,54 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import ( + "context" + structs_gen "structs/models" + "testing" + "time" +) + +func CreateTodo(t *testing.T, db Inserter[*structs_gen.Todo], m *structs_gen.Todo, opts ...func(*structs_gen.Todo)) *structs_gen.Todo { + t.Helper() + + tbl := &structs_gen.Todo{ + ID: 0, + Title: "", + Description: "", + // Tags is slice + CreatedAt: time.Time{}, + // UpdatedAt is unknown + // DoneAt is unknown + } + + if isModified(m.ID) { + tbl.ID = m.ID + } + tbl.Title = m.Title // must overwrite + if isModified(m.Description) { + tbl.Description = m.Description + } + if len(m.Tags) > 0 { + tbl.Tags = m.Tags + } + if isModified(m.CreatedAt) { + tbl.CreatedAt = m.CreatedAt + } + if isModified(m.UpdatedAt) { + tbl.UpdatedAt = m.UpdatedAt + } + if isModified(m.DoneAt) { + tbl.DoneAt = m.DoneAt + } + for _, opt := range opts { + opt(tbl) + } + + m, err := db.Insert(context.Background(), tbl) + if err != nil { + t.Fatal(err) + } + + return m +} diff --git a/test/structs/test/testdata-zero/goldie-user.go b/test/structs/test/testdata-zero/goldie-user.go new file mode 100644 index 0000000..cd0d126 --- /dev/null +++ b/test/structs/test/testdata-zero/goldie-user.go @@ -0,0 +1,53 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import ( + "context" + "fmt" + structs_gen "structs/models" + "testing" + "time" +) + +func CreateUser(t *testing.T, db Inserter[*structs_gen.User], m *structs_gen.User, opts ...func(*structs_gen.User)) *structs_gen.User { + t.Helper() + + tbl := &structs_gen.User{ + ID: 0, + Name: "Taro Yamada", // Name is overwritten + IconURL: fmt.Sprintf("http://example.com/%d", 123456), // IconURL is overwritten + UserType: 1, // UserType is overwritten + CreatedAt: time.Time{}, + // UpdatedAt is unknown + } + + if isModified(m.ID) { + tbl.ID = m.ID + } + if isModified(m.Name) { + tbl.Name = m.Name + } + if isModified(m.IconURL) { + tbl.IconURL = m.IconURL + } + if m.UserType != 1 { + tbl.UserType = m.UserType + } + if isModified(m.CreatedAt) { + tbl.CreatedAt = m.CreatedAt + } + if isModified(m.UpdatedAt) { + tbl.UpdatedAt = m.UpdatedAt + } + for _, opt := range opts { + opt(tbl) + } + + m, err := db.Insert(context.Background(), tbl) + if err != nil { + t.Fatal(err) + } + + return m +} diff --git a/test/structs/test/testdata/fixgen.yaml b/test/structs/test/testdata/fixgen.yaml new file mode 100644 index 0000000..7ebe424 --- /dev/null +++ b/test/structs/test/testdata/fixgen.yaml @@ -0,0 +1,16 @@ +structs: + Todo: + fields: + Title: + overwrite: true + User: + fields: + IconURL: + expr: fmt.Sprintf("http://example.com/%d", 123456) + Name: + value: Taro Yamada + UserType: + value: 1 + isModifiedCond: m.UserType != 1 +imports: +- package: fmt diff --git a/test/structs/test/testdata/goldie-common.go b/test/structs/test/testdata/goldie-common.go new file mode 100644 index 0000000..9c73a01 --- /dev/null +++ b/test/structs/test/testdata/goldie-common.go @@ -0,0 +1,8 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +func isModified[T comparable](v T) bool { + var zero T + return v != zero +} diff --git a/test/structs/test/testdata/goldie-structs_common.go b/test/structs/test/testdata/goldie-structs_common.go new file mode 100644 index 0000000..13caa89 --- /dev/null +++ b/test/structs/test/testdata/goldie-structs_common.go @@ -0,0 +1,9 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import "context" + +type Inserter[M any] interface { + Insert(ctx context.Context, m M) (M, error) +} diff --git a/test/structs/test/testdata/goldie-todo.go b/test/structs/test/testdata/goldie-todo.go new file mode 100644 index 0000000..33f2c2c --- /dev/null +++ b/test/structs/test/testdata/goldie-todo.go @@ -0,0 +1,57 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import ( + "context" + "math/rand/v2" + structs_gen "structs/models" + "testing" + "time" + + "github.com/samber/lo" +) + +func CreateTodo(t *testing.T, db Inserter[*structs_gen.Todo], m *structs_gen.Todo, opts ...func(*structs_gen.Todo)) *structs_gen.Todo { + t.Helper() + + tbl := &structs_gen.Todo{ + ID: rand.Int64(), + Title: lo.RandomString(32, lo.AlphanumericCharset), + Description: lo.RandomString(32, lo.AlphanumericCharset), + // Tags is slice + CreatedAt: time.Now(), + // UpdatedAt is unknown + // DoneAt is unknown + } + + if isModified(m.ID) { + tbl.ID = m.ID + } + tbl.Title = m.Title // must overwrite + if isModified(m.Description) { + tbl.Description = m.Description + } + if len(m.Tags) > 0 { + tbl.Tags = m.Tags + } + if isModified(m.CreatedAt) { + tbl.CreatedAt = m.CreatedAt + } + if isModified(m.UpdatedAt) { + tbl.UpdatedAt = m.UpdatedAt + } + if isModified(m.DoneAt) { + tbl.DoneAt = m.DoneAt + } + for _, opt := range opts { + opt(tbl) + } + + m, err := db.Insert(context.Background(), tbl) + if err != nil { + t.Fatal(err) + } + + return m +} diff --git a/test/structs/test/testdata/goldie-user.go b/test/structs/test/testdata/goldie-user.go new file mode 100644 index 0000000..233d878 --- /dev/null +++ b/test/structs/test/testdata/goldie-user.go @@ -0,0 +1,54 @@ +// Code generated by fixgen, DO NOT EDIT. + +package fixture + +import ( + "context" + "fmt" + "math/rand/v2" + structs_gen "structs/models" + "testing" + "time" +) + +func CreateUser(t *testing.T, db Inserter[*structs_gen.User], m *structs_gen.User, opts ...func(*structs_gen.User)) *structs_gen.User { + t.Helper() + + tbl := &structs_gen.User{ + ID: rand.Int64(), + Name: "Taro Yamada", // Name is overwritten + IconURL: fmt.Sprintf("http://example.com/%d", 123456), // IconURL is overwritten + UserType: 1, // UserType is overwritten + CreatedAt: time.Now(), + // UpdatedAt is unknown + } + + if isModified(m.ID) { + tbl.ID = m.ID + } + if isModified(m.Name) { + tbl.Name = m.Name + } + if isModified(m.IconURL) { + tbl.IconURL = m.IconURL + } + if m.UserType != 1 { + tbl.UserType = m.UserType + } + if isModified(m.CreatedAt) { + tbl.CreatedAt = m.CreatedAt + } + if isModified(m.UpdatedAt) { + tbl.UpdatedAt = m.UpdatedAt + } + for _, opt := range opts { + opt(tbl) + } + + m, err := db.Insert(context.Background(), tbl) + if err != nil { + t.Fatal(err) + } + + return m +}