diff --git a/builder/assignto.go b/builder/assignto.go index 55bf52d1..33197e6c 100644 --- a/builder/assignto.go +++ b/builder/assignto.go @@ -16,19 +16,19 @@ func AssignOf(s *jen.Statement) *AssignTo { } func (a *AssignTo) WithIndex(s *jen.Statement) *AssignTo { - return &AssignTo{ - Stmt: a.Stmt.Clone().Index(s), - } + return &AssignTo{Stmt: a.Stmt.Clone().Index(s)} } func (a *AssignTo) MustAssign() *AssignTo { - a.Must = true - return a + return &AssignTo{Stmt: a.Stmt, Must: true, Update: a.Update} +} + +func (a *AssignTo) WithStmt(s *jen.Statement) *AssignTo { + return &AssignTo{Stmt: s, Must: a.Must, Update: a.Update} } -func (a *AssignTo) IsUpdate() *AssignTo { - a.Update = true - return a +func (a *AssignTo) WithUpdate(update bool) *AssignTo { + return &AssignTo{Stmt: a.Stmt, Must: a.Must, Update: update} } func ToAssignable(assignTo *AssignTo) func(stmt []jen.Code, nextID *xtype.JenID, err *Error) ([]jen.Code, *Error) { @@ -46,16 +46,16 @@ func AssignByBuild(b Builder, gen Generator, ctx *MethodContext, assignTo *Assig } func BuildByAssign(b Builder, gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, path ErrorPath) ([]jen.Code, *xtype.JenID, *Error) { - buildStmt, valueVar, err := buildTargetVar(gen, ctx, sourceID, source, target, path) + buildStmt, assignTo, err := buildTargetVar(gen, ctx, sourceID, source, target, path) if err != nil { return nil, nil, err } - stmt, err := b.Assign(gen, ctx, AssignOf(valueVar), sourceID, source, target, path) + stmt, err := b.Assign(gen, ctx, assignTo, sourceID, source, target, path) if err != nil { return nil, nil, err } buildStmt = append(buildStmt, stmt...) - return buildStmt, xtype.VariableID(valueVar), nil + return buildStmt, xtype.VariableID(assignTo.Stmt.Clone()), nil } diff --git a/builder/default.go b/builder/default.go index e13376e5..0d327fce 100644 --- a/builder/default.go +++ b/builder/default.go @@ -7,14 +7,14 @@ import ( "github.com/jmattheis/goverter/xtype" ) -func buildTargetVar(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, errPath ErrorPath) ([]jen.Code, *jen.Statement, *Error) { +func buildTargetVar(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, errPath ErrorPath) ([]jen.Code, *AssignTo, *Error) { if !ctx.UseConstructor || !types.Identical(ctx.Conf.Source.T, source.T) || !types.Identical(ctx.Conf.Target.T, target.T) { name := ctx.Name(target.ID()) variable := jen.Var().Id(name).Add(target.TypeAsJen()) ctx.SetErrorTargetVar(jen.Id(name)) - return []jen.Code{variable}, jen.Id(name), nil + return []jen.Code{variable}, AssignOf(jen.Id(name)), nil } ctx.UseConstructor = false @@ -37,10 +37,10 @@ func buildTargetVar(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, so if nextID.Variable { ctx.SetErrorTargetVar(nextID.Code.Clone()) - return stmt, nextID.Code, nil + return stmt, AssignOf(nextID.Code).WithUpdate(ctx.Conf.DefaultUpdate), nil } name := ctx.Name(target.ID()) stmt = append(stmt, jen.Id(name).Op(":=").Add(nextID.Code)) ctx.SetErrorTargetVar(jen.Id(name)) - return stmt, jen.Id(name), nil + return stmt, AssignOf(jen.Id(name)).WithUpdate(ctx.Conf.DefaultUpdate), nil } diff --git a/builder/enum.go b/builder/enum.go index c8f68393..1b2ca84c 100644 --- a/builder/enum.go +++ b/builder/enum.go @@ -25,10 +25,11 @@ func isEnum(ctx *MethodContext, source, target *xtype.Type) bool { // Build creates conversion source code for the given source and target type. func (*Enum) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, path ErrorPath) ([]jen.Code, *xtype.JenID, *Error) { - stmt, nameVar, err := buildTargetVar(gen, ctx, sourceID, source, target, path) + stmt, nameAssign, err := buildTargetVar(gen, ctx, sourceID, source, target, path) if err != nil { return nil, nil, err } + nameVar := nameAssign.Stmt var cases []jen.Code diff --git a/builder/pointer.go b/builder/pointer.go index ea6384fb..7b8c5abc 100644 --- a/builder/pointer.go +++ b/builder/pointer.go @@ -17,12 +17,12 @@ func (*Pointer) Matches(_ *MethodContext, source, target *xtype.Type) bool { func (p *Pointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, errPath ErrorPath) ([]jen.Code, *xtype.JenID, *Error) { ctx.SetErrorTargetVar(jen.Nil()) if ctx.UseConstructor && ctx.Conf.DefaultUpdate { - buildStmt, valueVar, err := buildTargetVar(gen, ctx, sourceID, source, target, errPath) + buildStmt, valueAssign, err := buildTargetVar(gen, ctx, sourceID, source, target, errPath) if err != nil { return nil, nil, err } - stmt, err := gen.Assign(ctx, AssignOf(jen.Parens(jen.Op("*").Add(valueVar))).IsUpdate(), sourceID.Deref(source), source.PointerInner, target.PointerInner, errPath) + stmt, err := gen.Assign(ctx, valueAssign.WithStmt(jen.Parens(jen.Op("*").Add(valueAssign.Stmt.Clone()))), sourceID.Deref(source), source.PointerInner, target.PointerInner, errPath) if err != nil { return nil, nil, err.Lift(&Path{ SourceID: "*", @@ -34,7 +34,7 @@ func (p *Pointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID buildStmt = append(buildStmt, jen.If(sourceID.Code.Clone().Op("!=").Nil()).Block(stmt...)) - return buildStmt, xtype.VariableID(valueVar), nil + return buildStmt, xtype.VariableID(valueAssign.Stmt), nil } return BuildByAssign(p, gen, ctx, sourceID, source, target, errPath) @@ -76,12 +76,12 @@ func (*SourcePointer) Matches(ctx *MethodContext, source, target *xtype.Type) bo // Build creates conversion source code for the given source and target type. func (s *SourcePointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, path ErrorPath) ([]jen.Code, *xtype.JenID, *Error) { if ctx.UseConstructor && ctx.Conf.DefaultUpdate { - buildStmt, valueVar, err := buildTargetVar(gen, ctx, sourceID, source, target, path) + buildStmt, targetAssign, err := buildTargetVar(gen, ctx, sourceID, source, target, path) if err != nil { return nil, nil, err } - stmt, err := gen.Assign(ctx, AssignOf(valueVar).IsUpdate(), sourceID.Deref(source), source.PointerInner, target, path) + stmt, err := gen.Assign(ctx, targetAssign, sourceID.Deref(source), source.PointerInner, target, path) if err != nil { return nil, nil, err.Lift(&Path{ SourceID: "*", @@ -91,7 +91,7 @@ func (s *SourcePointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype buildStmt = append(buildStmt, jen.If(sourceID.Code.Clone().Op("!=").Nil()).Block(stmt...)) - return buildStmt, xtype.VariableID(valueVar), nil + return buildStmt, xtype.VariableID(targetAssign.Stmt), nil } return BuildByAssign(s, gen, ctx, sourceID, source, target, path) @@ -127,12 +127,12 @@ func (*TargetPointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype.J ctx.SetErrorTargetVar(jen.Nil()) if ctx.UseConstructor { - buildStmt, valueVar, err := buildTargetVar(gen, ctx, sourceID, source, target, path) + buildStmt, targetAssign, err := buildTargetVar(gen, ctx, sourceID, source, target, path) if err != nil { return nil, nil, err } - stmt, err := gen.Assign(ctx, AssignOf(jen.Parens(jen.Op("*").Add(valueVar))).IsUpdate(), sourceID, source, target.PointerInner, path) + stmt, err := gen.Assign(ctx, targetAssign.WithStmt(jen.Parens(jen.Op("*").Add(targetAssign.Stmt.Clone()))), sourceID, source, target.PointerInner, path) if err != nil { return nil, nil, err.Lift(&Path{ TargetID: "*", @@ -142,7 +142,7 @@ func (*TargetPointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype.J buildStmt = append(buildStmt, stmt...) - return buildStmt, xtype.VariableID(valueVar), nil + return buildStmt, xtype.VariableID(targetAssign.Stmt), nil } stmt, id, err := gen.Build(ctx, sourceID, source, target.PointerInner, path) diff --git a/docs/changelog.md b/docs/changelog.md index edfb91a1..40cf3dab 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -6,6 +6,11 @@ import GH from './GH.vue'; ## unreleased +## v1.9.4 + +- Fix [`default:update`](./reference/default.md) not applying zero value checks + for non-pointer source and target types. + ## v1.9.3 - Allow unexported [`extend`](./reference/extend.md) functions with inferred diff --git a/scenario/default_update_non_pointer.yml b/scenario/default_update_non_pointer.yml new file mode 100644 index 00000000..847f6c82 --- /dev/null +++ b/scenario/default_update_non_pointer.yml @@ -0,0 +1,53 @@ +input: + input.go: | + package execution + + import "time" + + type Source struct { + Age int + Name *string + Start *time.Time + } + + type Dest struct { + Age int + Name *string + Start *time.Time + } + + // goverter:converter + // goverter:default:update + // goverter:update:ignoreZeroValueField yes + // goverter:skipCopySameType yes + type testConverter interface { + // goverter:default DefaultParams + ToDest(source Source) Dest + } + + func DefaultParams() Dest { + return Dest{Age: 10, Name: nil, Start: nil} + } +success: + - generated/generated.go: | + // Code generated by github.com/jmattheis/goverter, DO NOT EDIT. + + package generated + + import execution "github.com/jmattheis/goverter/execution" + + type testConverterImpl struct{} + + func (c *testConverterImpl) ToDest(source execution.Source) execution.Dest { + executionDest := execution.DefaultParams() + if source.Age != 0 { + executionDest.Age = source.Age + } + if source.Name != nil { + executionDest.Name = source.Name + } + if source.Start != nil { + executionDest.Start = source.Start + } + return executionDest + } diff --git a/scenario/default_update_non_ptr_to_ptr.yml b/scenario/default_update_non_ptr_to_ptr.yml new file mode 100644 index 00000000..bd1b87c2 --- /dev/null +++ b/scenario/default_update_non_ptr_to_ptr.yml @@ -0,0 +1,53 @@ +input: + input.go: | + package execution + + import "time" + + type Source struct { + Age int + Name *string + Start *time.Time + } + + type Dest struct { + Age int + Name *string + Start *time.Time + } + + // goverter:converter + // goverter:default:update + // goverter:update:ignoreZeroValueField yes + // goverter:skipCopySameType yes + type testConverter interface { + // goverter:default DefaultParams + ToDest(source Source) *Dest + } + + func DefaultParams() *Dest { + return &Dest{Age: 10, Name: nil, Start: nil} + } +success: + - generated/generated.go: | + // Code generated by github.com/jmattheis/goverter, DO NOT EDIT. + + package generated + + import execution "github.com/jmattheis/goverter/execution" + + type testConverterImpl struct{} + + func (c *testConverterImpl) ToDest(source execution.Source) *execution.Dest { + pExecutionDest := execution.DefaultParams() + if source.Age != 0 { + (*pExecutionDest).Age = source.Age + } + if source.Name != nil { + (*pExecutionDest).Name = source.Name + } + if source.Start != nil { + (*pExecutionDest).Start = source.Start + } + return pExecutionDest + } diff --git a/scenario/default_update_ptr_to_ptr.yml b/scenario/default_update_ptr_to_ptr.yml new file mode 100644 index 00000000..6f16f182 --- /dev/null +++ b/scenario/default_update_ptr_to_ptr.yml @@ -0,0 +1,55 @@ +input: + input.go: | + package execution + + import "time" + + type Source struct { + Age int + Name *string + Start *time.Time + } + + type Dest struct { + Age int + Name *string + Start *time.Time + } + + // goverter:converter + // goverter:default:update + // goverter:update:ignoreZeroValueField yes + // goverter:skipCopySameType yes + type testConverter interface { + // goverter:default DefaultParams + ToDest(source *Source) *Dest + } + + func DefaultParams() *Dest { + return &Dest{Age: 10, Name: nil, Start: nil} + } +success: + - generated/generated.go: | + // Code generated by github.com/jmattheis/goverter, DO NOT EDIT. + + package generated + + import execution "github.com/jmattheis/goverter/execution" + + type testConverterImpl struct{} + + func (c *testConverterImpl) ToDest(source *execution.Source) *execution.Dest { + pExecutionDest := execution.DefaultParams() + if source != nil { + if (*source).Age != 0 { + (*pExecutionDest).Age = (*source).Age + } + if (*source).Name != nil { + (*pExecutionDest).Name = (*source).Name + } + if (*source).Start != nil { + (*pExecutionDest).Start = (*source).Start + } + } + return pExecutionDest + }