Skip to content

Commit

Permalink
Merge pull request #148 from jmattheis/assign-not-nil
Browse files Browse the repository at this point in the history
If possible assign directly to struct fileds, slices, maps
  • Loading branch information
jmattheis committed Jun 21, 2024
2 parents ae1b2c8 + c3a52d6 commit 6bcdb92
Show file tree
Hide file tree
Showing 47 changed files with 367 additions and 255 deletions.
55 changes: 55 additions & 0 deletions builder/assignto.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package builder

import (
"github.com/dave/jennifer/jen"
"github.com/jmattheis/goverter/xtype"
)

type AssignTo struct {
Must bool
Stmt *jen.Statement
}

func assignOf(s *jen.Statement) *AssignTo {
return &AssignTo{Stmt: s}
}

func (a *AssignTo) WithIndex(s *jen.Statement) *AssignTo {
return &AssignTo{
Stmt: a.Stmt.Clone().Index(s),
}
}

func (a *AssignTo) WithMust() *AssignTo {
a.Must = true
return a
}

func ToAssignable(assignTo *AssignTo) func(stmt []jen.Code, nextID *xtype.JenID, err *Error) ([]jen.Code, *Error) {
return func(stmt []jen.Code, nextID *xtype.JenID, err *Error) ([]jen.Code, *Error) {
if err != nil {
return nil, err
}
stmt = append(stmt, assignTo.Stmt.Clone().Op("=").Add(nextID.Code))
return stmt, nil
}
}

func AssignByBuild(b Builder, gen Generator, ctx *MethodContext, assignTo *AssignTo, sourceID *xtype.JenID, source, target *xtype.Type, errPath ErrorPath) ([]jen.Code, *Error) {
return ToAssignable(assignTo)(b.Build(gen, ctx, sourceID, source, target, errPath))
}

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)
if err != nil {
return nil, nil, err
}

stmt, err := b.Assign(gen, ctx, assignOf(valueVar), sourceID, source, target, path)
if err != nil {
return nil, nil, err
}

buildStmt = append(buildStmt, stmt...)
return buildStmt, xtype.VariableID(valueVar), nil
}
8 changes: 8 additions & 0 deletions builder/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ func (*Basic) Build(_ Generator, _ *MethodContext, sourceID *xtype.JenID, source
return nil, sourceID, nil
}

func (b *Basic) Assign(gen Generator, ctx *MethodContext, assignTo *AssignTo, sourceID *xtype.JenID, source, target *xtype.Type, errPath ErrorPath) ([]jen.Code, *Error) {
return AssignByBuild(b, gen, ctx, assignTo, sourceID, source, target, errPath)
}

// BasicTargetPointerRule handles edge conditions if the target type is a pointer.
type BasicTargetPointerRule struct{}

Expand Down Expand Up @@ -49,3 +53,7 @@ func (*BasicTargetPointerRule) Build(gen Generator, ctx *MethodContext, sourceID

return stmt, xtype.OtherID(newID), err
}

func (b *BasicTargetPointerRule) Assign(gen Generator, ctx *MethodContext, assignTo *AssignTo, sourceID *xtype.JenID, source, target *xtype.Type, errPath ErrorPath) ([]jen.Code, *Error) {
return AssignByBuild(b, gen, ctx, assignTo, sourceID, source, target, errPath)
}
13 changes: 13 additions & 0 deletions builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ type Builder interface {
sourceID *xtype.JenID,
source, target *xtype.Type,
path ErrorPath) ([]jen.Code, *xtype.JenID, *Error)

Assign(gen Generator,
ctx *MethodContext,
assignTo *AssignTo,
sourceID *xtype.JenID,
source, target *xtype.Type,
path ErrorPath) ([]jen.Code, *Error)
}

// Generator checks all existing builders if they can create a conversion implementations for the given source and target type
Expand All @@ -31,6 +38,12 @@ type Generator interface {
source, target *xtype.Type,
path ErrorPath) ([]jen.Code, *xtype.JenID, *Error)

Assign(ctx *MethodContext,
assignTo *AssignTo,
sourceID *xtype.JenID,
source, target *xtype.Type,
path ErrorPath) ([]jen.Code, *Error)

CallMethod(
ctx *MethodContext,
method *method.Definition,
Expand Down
4 changes: 4 additions & 0 deletions builder/enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ func (*Enum) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, sou
return stmt, xtype.VariableID(nameVar), nil
}

func (s *Enum) Assign(gen Generator, ctx *MethodContext, assignTo *AssignTo, sourceID *xtype.JenID, source, target *xtype.Type, path ErrorPath) ([]jen.Code, *Error) {
return AssignByBuild(s, gen, ctx, assignTo, sourceID, source, target, path)
}

func caseAction(gen Generator, ctx *MethodContext, nameVar *jen.Statement, target *xtype.Type, targetEnum *xtype.Enum, targetName string, sourceID *xtype.JenID, errPath ErrorPath) (jen.Code, *Error) {
if config.IsEnumAction(targetName) {
switch targetName {
Expand Down
48 changes: 29 additions & 19 deletions builder/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,51 @@ func (*List) Matches(_ *MethodContext, source, target *xtype.Type) bool {
}

// Build creates conversion source code for the given source and target type.
func (*List) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, path ErrorPath) ([]jen.Code, *xtype.JenID, *Error) {
func (l *List) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, path ErrorPath) ([]jen.Code, *xtype.JenID, *Error) {
ctx.SetErrorTargetVar(jen.Nil())
targetSlice := ctx.Name(target.ID())

stmt, err := l.Assign(gen, ctx, assignOf(jen.Id(targetSlice)), sourceID, source, target, path)
if err != nil {
return nil, nil, err
}

var id jen.Code
if source.ListFixed {
id = jen.Id(targetSlice).Op(":=").Make(target.TypeAsJen(), jen.Len(sourceID.Code.Clone()))
} else {
id = jen.Var().Add(jen.Id(targetSlice), target.TypeAsJen())
}
stmt = append([]jen.Code{id}, stmt...)

return stmt, xtype.VariableID(jen.Id(targetSlice)), nil
}

func (*List) Assign(gen Generator, ctx *MethodContext, assignTo *AssignTo, sourceID *xtype.JenID, source, target *xtype.Type, path ErrorPath) ([]jen.Code, *Error) {
ctx.SetErrorTargetVar(jen.Nil())
index := ctx.Index()

indexedSource := xtype.VariableID(sourceID.Code.Clone().Index(jen.Id(index)))

forBlock, newID, err := gen.Build(ctx, indexedSource, source.ListInner, target.ListInner, path.Index(jen.Id(index)))
forBlock, err := gen.Assign(ctx, assignTo.WithIndex(jen.Id(index)), indexedSource, source.ListInner, target.ListInner, path.Index(jen.Id(index)))
if err != nil {
return nil, nil, err.Lift(&Path{
return nil, err.Lift(&Path{
SourceID: "[]",
SourceType: source.ListInner.String,
TargetID: "[]",
TargetType: target.ListInner.String,
})
}
forBlock = append(forBlock, jen.Id(targetSlice).Index(jen.Id(index)).Op("=").Add(newID.Code))
forStmt := jen.For(jen.Id(index).Op(":=").Lit(0), jen.Id(index).Op("<").Len(sourceID.Code.Clone()), jen.Id(index).Op("++")).
Block(forBlock...)

stmt := []jen.Code{}
if source.ListFixed {
stmt = []jen.Code{
jen.Id(targetSlice).Op(":=").Make(target.TypeAsJen(), jen.Len(sourceID.Code.Clone())),
forStmt,
}
} else {
stmt = []jen.Code{
jen.Var().Add(jen.Id(targetSlice), target.TypeAsJen()),
jen.If(sourceID.Code.Clone().Op("!=").Nil()).Block(
jen.Id(targetSlice).Op("=").Make(target.TypeAsJen(), jen.Len(sourceID.Code.Clone())),
forStmt,
),
}
return []jen.Code{forStmt}, nil
}

return stmt, xtype.VariableID(jen.Id(targetSlice)), nil
return []jen.Code{
jen.If(sourceID.Code.Clone().Op("!=").Nil()).Block(
assignTo.Stmt.Clone().Op("=").Make(target.TypeAsJen(), jen.Len(sourceID.Code.Clone())),
forStmt,
),
}, nil
}
24 changes: 13 additions & 11 deletions builder/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,43 +14,45 @@ func (*Map) Matches(_ *MethodContext, source, target *xtype.Type) bool {
}

// Build creates conversion source code for the given source and target type.
func (*Map) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, errPath ErrorPath) ([]jen.Code, *xtype.JenID, *Error) {
func (m *Map) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, errPath ErrorPath) ([]jen.Code, *xtype.JenID, *Error) {
ctx.SetErrorTargetVar(jen.Nil())
return BuildByAssign(m, gen, ctx, sourceID, source, target, errPath)
}

func (*Map) Assign(gen Generator, ctx *MethodContext, assignTo *AssignTo, sourceID *xtype.JenID, source, target *xtype.Type, errPath ErrorPath) ([]jen.Code, *Error) {
ctx.SetErrorTargetVar(jen.Nil())
targetMap := ctx.Name(target.ID())
key, value := ctx.Map()

errPath = errPath.Key(jen.Id(key))

block, newKey, err := gen.Build(ctx, xtype.VariableID(jen.Id(key)), source.MapKey, target.MapKey, errPath)
block, keyID, err := gen.Build(ctx, xtype.VariableID(jen.Id(key)), source.MapKey, target.MapKey, errPath)
if err != nil {
return nil, nil, err.Lift(&Path{
return nil, err.Lift(&Path{
SourceID: "[]",
SourceType: "<mapkey> " + source.MapKey.String,
TargetID: "[]",
TargetType: "<mapkey> " + target.MapKey.String,
})
}
valueStmt, valueKey, err := gen.Build(
ctx, xtype.VariableID(jen.Id(value)), source.MapValue, target.MapValue, errPath)
valueStmt, err := gen.Assign(
ctx, assignTo.WithIndex(keyID.Code).WithMust(), xtype.VariableID(jen.Id(value)), source.MapValue, target.MapValue, errPath)
if err != nil {
return nil, nil, err.Lift(&Path{
return nil, err.Lift(&Path{
SourceID: "[]",
SourceType: "<mapvalue> " + source.MapValue.String,
TargetID: "[]",
TargetType: "<mapvalue> " + target.MapValue.String,
})
}
block = append(block, valueStmt...)
block = append(block, jen.Id(targetMap).Index(newKey.Code).Op("=").Add(valueKey.Code))

stmt := []jen.Code{
jen.Var().Add(jen.Id(targetMap), target.TypeAsJen()),
jen.If(sourceID.Code.Clone().Op("!=").Nil()).Block(
jen.Id(targetMap).Op("=").Make(target.TypeAsJen(), jen.Len(sourceID.Code.Clone())),
assignTo.Stmt.Clone().Op("=").Make(target.TypeAsJen(), jen.Len(sourceID.Code.Clone())),
jen.For(jen.List(jen.Id(key), jen.Id(value)).Op(":=").Range().Add(sourceID.Code)).
Block(block...),
),
}

return stmt, xtype.VariableID(jen.Id(targetMap)), nil
return stmt, nil
}
52 changes: 29 additions & 23 deletions builder/pointer.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ func (*Pointer) Matches(_ *MethodContext, source, target *xtype.Type) bool {
}

// Build creates conversion source code for the given source and target type.
func (*Pointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, errPath ErrorPath) ([]jen.Code, *xtype.JenID, *Error) {
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())
return BuildByAssign(p, gen, ctx, sourceID, source, target, errPath)
}

stmt, outerVar, err := buildTargetVar(gen, ctx, sourceID, source, target, errPath)
if err != nil {
return nil, nil, err
}
func (*Pointer) Assign(gen Generator, ctx *MethodContext, assignTo *AssignTo, sourceID *xtype.JenID, source, target *xtype.Type, errPath ErrorPath) ([]jen.Code, *Error) {
ctx.SetErrorTargetVar(jen.Nil())

valueSourceID := jen.Op("*").Add(sourceID.Code.Clone())
if !source.PointerInner.Basic {
Expand All @@ -32,7 +32,7 @@ func (*Pointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID,
nextBlock, id, err := gen.Build(
ctx, innerID, source.PointerInner, target.PointerInner, errPath)
if err != nil {
return nil, nil, err.Lift(&Path{
return nil, err.Lift(&Path{
SourceID: "*",
SourceType: source.PointerInner.String,
TargetID: "*",
Expand All @@ -43,13 +43,18 @@ func (*Pointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID,
pstmt, tmpID := id.Pointer(target.PointerInner, ctx.Name)

ifBlock := append(nextBlock, pstmt...)
ifBlock = append(ifBlock, outerVar.Clone().Op("=").Add(tmpID.Code))
ifBlock = append(ifBlock, assignTo.Stmt.Clone().Op("=").Add(tmpID.Code))

var elseCase []jen.Code
if assignTo.Must {
elseCase = append(elseCase, jen.Else().Block(assignTo.Stmt.Clone().Op("=").Nil()))
}

stmt = append(stmt,
jen.If(sourceID.Code.Clone().Op("!=").Nil()).Block(ifBlock...),
)
stmt := []jen.Code{
jen.If(sourceID.Code.Clone().Op("!=").Nil()).Block(ifBlock...).Add(elseCase...),
}

return stmt, xtype.VariableID(outerVar), err
return stmt, err
}

// SourcePointer handles type were only the source is a pointer.
Expand All @@ -61,9 +66,11 @@ func (*SourcePointer) Matches(ctx *MethodContext, source, target *xtype.Type) bo
}

// Build creates conversion source code for the given source and target type.
func (*SourcePointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, path ErrorPath) ([]jen.Code, *xtype.JenID, *Error) {
ctx.SetErrorTargetVar(jen.Id(target.ID()))
func (s *SourcePointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, source, target *xtype.Type, path ErrorPath) ([]jen.Code, *xtype.JenID, *Error) {
return BuildByAssign(s, gen, ctx, sourceID, source, target, path)
}

func (*SourcePointer) Assign(gen Generator, ctx *MethodContext, assignTo *AssignTo, sourceID *xtype.JenID, source, target *xtype.Type, path ErrorPath) ([]jen.Code, *Error) {
valueSourceID := jen.Op("*").Add(sourceID.Code.Clone())
if !source.PointerInner.Basic {
valueSourceID = jen.Parens(valueSourceID)
Expand All @@ -72,26 +79,21 @@ func (*SourcePointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype.J
innerID := xtype.OtherID(valueSourceID)
innerID.ParentPointer = sourceID

stmt, valueVar, err := buildTargetVar(gen, ctx, sourceID, source, target, path)
if err != nil {
return nil, nil, err
}

nextInner, nextID, err := gen.Build(ctx, innerID, source.PointerInner, target, path)
if err != nil {
return nil, nil, err.Lift(&Path{
return nil, err.Lift(&Path{
SourceID: "*",
SourceType: source.PointerInner.String,
})
}

stmt = append(stmt,
stmt := []jen.Code{
jen.If(sourceID.Code.Clone().Op("!=").Nil()).Block(
append(nextInner, valueVar.Clone().Op("=").Add(nextID.Code))...,
append(nextInner, assignTo.Stmt.Clone().Op("=").Add(nextID.Code))...,
),
)
}

return stmt, xtype.VariableID(valueVar), nil
return stmt, nil
}

// TargetPointer handles type were only the target is a pointer.
Expand Down Expand Up @@ -119,3 +121,7 @@ func (*TargetPointer) Build(gen Generator, ctx *MethodContext, sourceID *xtype.J
stmt = append(stmt, pstmt...)
return stmt, nextID, nil
}

func (tp *TargetPointer) Assign(gen Generator, ctx *MethodContext, assignTo *AssignTo, sourceID *xtype.JenID, source, target *xtype.Type, path ErrorPath) ([]jen.Code, *Error) {
return AssignByBuild(tp, gen, ctx, assignTo, sourceID, source, target, path)
}
4 changes: 4 additions & 0 deletions builder/skipcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ func (*SkipCopy) Matches(ctx *MethodContext, source, target *xtype.Type) bool {
func (*SkipCopy) Build(_ Generator, _ *MethodContext, sourceID *xtype.JenID, _, _ *xtype.Type, _ ErrorPath) ([]jen.Code, *xtype.JenID, *Error) {
return nil, sourceID, nil
}

func (*SkipCopy) Assign(_ Generator, _ *MethodContext, assignTo *AssignTo, sourceID *xtype.JenID, _, _ *xtype.Type, _ ErrorPath) ([]jen.Code, *Error) {
return []jen.Code{assignTo.Stmt.Clone().Op("=").Add(sourceID.Code)}, nil
}
7 changes: 5 additions & 2 deletions builder/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,11 @@ func (*Struct) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, s
}
stmt = append(stmt, mapStmt...)

fieldStmt, fieldID, err := gen.Build(ctx, nextID, nextSource, targetFieldType, targetFieldPath)
fieldStmt, err := gen.Assign(ctx, assignOf(nameVar.Clone().Dot(targetField.Name())), nextID, nextSource, targetFieldType, targetFieldPath)
if err != nil {
return nil, nil, err.Lift(lift...)
}
stmt = append(stmt, fieldStmt...)
stmt = append(stmt, nameVar.Clone().Dot(targetField.Name()).Op("=").Add(fieldID.Code))
} else {
def := fieldMapping.Function

Expand Down Expand Up @@ -134,6 +133,10 @@ func (*Struct) Build(gen Generator, ctx *MethodContext, sourceID *xtype.JenID, s
return stmt, xtype.VariableID(nameVar), nil
}

func (s *Struct) Assign(gen Generator, ctx *MethodContext, assignTo *AssignTo, sourceID *xtype.JenID, source, target *xtype.Type, path ErrorPath) ([]jen.Code, *Error) {
return AssignByBuild(s, gen, ctx, assignTo, sourceID, source, target, path)
}

func mapField(
gen Generator,
ctx *MethodContext,
Expand Down
4 changes: 4 additions & 0 deletions builder/underlying.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ You have to disable enum or useUnderlyingTypeMethods to resolve the setting conf
return stmt, id, err
}

func (u *UseUnderlyingTypeMethods) Assign(gen Generator, ctx *MethodContext, assignTo *AssignTo, sourceID *xtype.JenID, source, target *xtype.Type, errPath ErrorPath) ([]jen.Code, *Error) {
return AssignByBuild(u, gen, ctx, assignTo, sourceID, source, target, errPath)
}

func findUnderlyingExtendMapping(ctx *MethodContext, source, target *xtype.Type) (underlyingSource, underlyingTarget bool) {
if source.Named {
if ctx.HasMethod(source.NamedType.Underlying(), target.NamedType) {
Expand Down
Loading

0 comments on commit 6bcdb92

Please sign in to comment.