Skip to content

Commit

Permalink
feat: partialstruct for custom struct
Browse files Browse the repository at this point in the history
  • Loading branch information
morlay committed May 10, 2024
1 parent aeffa54 commit 5b2d016
Show file tree
Hide file tree
Showing 14 changed files with 408 additions and 131 deletions.
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ cover:

fmt:
goimports -l -w .
gofmt -l -w .

dep:
go get -u ./...
108 changes: 11 additions & 97 deletions devpkg/deepcopygen/deepcopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package deepcopygen
import (
"go/types"

"github.com/octohelm/gengo/devpkg/deepcopygen/helper"

"github.com/octohelm/gengo/pkg/gengo"
)

Expand Down Expand Up @@ -99,106 +101,18 @@ func(in *@Type) DeepCopy() *@Type {
}
func(in *@Type) DeepCopyInto(out *@Type) {
@fieldCopies
@fieldsCopies
}
`,
"Type": gengo.ID(named.Obj()),
"fieldCopies": func(sw gengo.SnippetWriter) {
for i := 0; i < x.NumFields(); i++ {
f := x.Field(i)

ft := f.Type()

switch x := ft.(type) {
case *types.Named:
inSamePkg := x.Obj().Pkg().Path() == named.Obj().Pkg().Path()
hasDeepCopy := false
hasDeepCopyInto := false
ptr := true

for i := 0; i < x.NumMethods(); i++ {
m := x.Method(i)
fn := m.Type().(*types.Signature)

// FIXME better check
hasDeepCopy = m.Name() == "DeepCopy" && fn.Results().Len() == 1 && fn.Params().Len() == 0
hasDeepCopyInto = m.Name() == "DeepCopyInto" && fn.Params().Len() == 1 && fn.Results().Len() == 0

if hasDeepCopy {
if _, ok := fn.Results().At(0).Type().(*types.Pointer); !ok {
ptr = false
}
}

if hasDeepCopyInto {
if _, ok := fn.Params().At(0).Type().(*types.Pointer); !ok {
ptr = false
}
}
}

if inSamePkg {
defers = append(defers, x)

// always gen
hasDeepCopyInto = true
hasDeepCopy = true
}

if ptr && hasDeepCopyInto {
c.Render(gengo.Snippet{gengo.T: `[email protected](&out.@fieldName)
`,
"fieldName": gengo.ID(f.Name()),
})
} else if !ptr && hasDeepCopy {
c.Render(gengo.Snippet{gengo.T: `out.@fieldName = [email protected]()
`,
"fieldName": gengo.ID(f.Name()),
})
} else if ptr && hasDeepCopy {
c.Render(gengo.Snippet{gengo.T: `out.@fieldName = *[email protected]()
`,
"fieldName": gengo.ID(f.Name()),
})
} else {
c.Render(gengo.Snippet{gengo.T: `out.@fieldName = in.@fieldName
`,
"fieldName": gengo.ID(f.Name()),
})
}
case *types.Map:
c.Render(gengo.Snippet{gengo.T: `
if in.@fieldName != nil {
i, o := &in.@fieldName, &out.@fieldName
*o = make(@MapType, len(*i))
for key, val := range *i {
(*o)[key] = val
}
}
`,
"MapType": gengo.ID(x),
"fieldName": gengo.ID(f.Name()),
})
case *types.Slice:
c.Render(gengo.Snippet{gengo.T: `
if in.@fieldName != nil {
i, o := &in.@fieldName, &out.@fieldName
*o = make(@SliceType, len(*i))
copy(*o, *i)
}
`,
"SliceType": gengo.ID(x),
"fieldName": gengo.ID(f.Name()),
})
default:
c.Render(gengo.Snippet{gengo.T: `
out.@fieldName = in.@fieldName
`,
"fieldName": gengo.ID(f.Name()),
})
}
}
},
"fieldsCopies": (&helper.StructFieldsCopy{
Struct: x,
DeepCopyIntoName: "DeepCopyInto",
DeepCopyName: "DeepCopy",
OnLocalDep: func(named *types.Named) {
defers = append(defers, named)
},
}).Snippet(c, named.Obj().Pkg()),
})
default:
c.Render(gengo.Snippet{gengo.T: `
Expand Down
154 changes: 154 additions & 0 deletions devpkg/deepcopygen/helper/copy_fields.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package helper

import (
"fmt"
"go/types"

"github.com/octohelm/gengo/pkg/gengo"
)

type FieldContext struct {
InSamePkg bool
HasDeepCopy bool
HasDeepCopyInto bool
PtrResultOrParam bool
}

type StructFieldsCopy struct {
DeepCopyName string
DeepCopyIntoName string
Struct *types.Struct
Skip func(f *types.Var) bool
OnLocalDep func(named *types.Named)
FieldContext func(f *types.Var) *FieldContext
}

func (sfc *StructFieldsCopy) Snippet(c gengo.Context, pkg *types.Package) func(sw gengo.SnippetWriter) {
if sfc.DeepCopyName == "" {
sfc.DeepCopyName = "DeepCopy"
}

if sfc.DeepCopyIntoName == "" {
sfc.DeepCopyIntoName = "DeepCopyInto"
}

return func(sw gengo.SnippetWriter) {
for i := 0; i < sfc.Struct.NumFields(); i++ {
f := sfc.Struct.Field(i)

if sfc.Skip != nil && sfc.Skip(f) {
continue
}

fieldType := f.Type()

switch x := fieldType.(type) {
case *types.Named:
if f.Name() == "Template" {
fmt.Println(x)
}

var fc *FieldContext

if sfc.FieldContext != nil {
if fcc := sfc.FieldContext(f); fcc != nil {
fc = fcc
}
}

if fc == nil {
fc = &FieldContext{}

fc.InSamePkg = x.Obj().Pkg().Path() == pkg.Path()
fc.PtrResultOrParam = true

for i := 0; i < x.NumMethods(); i++ {
m := x.Method(i)
fn := m.Type().(*types.Signature)

// FIXME better check
fc.HasDeepCopy = m.Name() == sfc.DeepCopyName && fn.Results().Len() == 1 && fn.Params().Len() == 0
fc.HasDeepCopyInto = m.Name() == sfc.DeepCopyIntoName && fn.Params().Len() == 1 && fn.Results().Len() == 0

if fc.HasDeepCopy {
if _, ok := fn.Results().At(0).Type().(*types.Pointer); !ok {
fc.PtrResultOrParam = false
}
}

if fc.HasDeepCopyInto {
if _, ok := fn.Params().At(0).Type().(*types.Pointer); !ok {
fc.PtrResultOrParam = false
}
}
}
}

if fc.InSamePkg {
if sfc.OnLocalDep != nil {
sfc.OnLocalDep(x)
}

// always gen
fc.HasDeepCopyInto = true
fc.HasDeepCopy = true
}

if fc.PtrResultOrParam && fc.HasDeepCopyInto {
c.Render(gengo.Snippet{gengo.T: `in.@fieldName.@DeepCopyInto(&out.@fieldName)
`,
"fieldName": gengo.ID(f.Name()),
"DeepCopyInto": gengo.ID(sfc.DeepCopyIntoName),
})
} else if !fc.PtrResultOrParam && fc.HasDeepCopy {
c.Render(gengo.Snippet{gengo.T: `out.@fieldName = in.@fieldName.@DeepCopy()
`,
"fieldName": gengo.ID(f.Name()),
"DeepCopy": gengo.ID(sfc.DeepCopyName),
})
} else if fc.PtrResultOrParam && fc.HasDeepCopy {
c.Render(gengo.Snippet{gengo.T: `out.@fieldName = *in.@fieldName.@DeepCopy()
`,
"fieldName": gengo.ID(f.Name()),
"DeepCopy": gengo.ID(sfc.DeepCopyName),
})
} else {
c.Render(gengo.Snippet{gengo.T: `out.@fieldName = in.@fieldName
`,
"fieldName": gengo.ID(f.Name()),
})
}
case *types.Map:
c.Render(gengo.Snippet{gengo.T: `
if in.@fieldName != nil {
i, o := &in.@fieldName, &out.@fieldName
*o = make(@MapType, len(*i))
for key, val := range *i {
(*o)[key] = val
}
}
`,
"MapType": gengo.ID(x),
"fieldName": gengo.ID(f.Name()),
})
case *types.Slice:
c.Render(gengo.Snippet{gengo.T: `
if in.@fieldName != nil {
i, o := &in.@fieldName, &out.@fieldName
*o = make(@SliceType, len(*i))
copy(*o, *i)
}
`,
"SliceType": gengo.ID(x),
"fieldName": gengo.ID(f.Name()),
})
default:
c.Render(gengo.Snippet{gengo.T: `
out.@fieldName = in.@fieldName
`,
"fieldName": gengo.ID(f.Name()),
})
}
}
}
}
Loading

0 comments on commit 5b2d016

Please sign in to comment.