diff --git a/README.md b/README.md index bc26ee92..5be0f2f5 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ use reflection. 1. Run `goverter`: ```bash - $ go run github.com/jmattheis/goverter/cmd/goverter@v0.17.2 ./ + $ go run github.com/jmattheis/goverter/cmd/goverter@v0.17.3 ./ ``` See [Installation](https://goverter.jmattheis.de/#/install) for more information. diff --git a/runner_test.go b/runner_test.go index ea3eeaba..9aec6fda 100644 --- a/runner_test.go +++ b/runner_test.go @@ -94,7 +94,9 @@ func TestScenario(t *testing.T) { require.Equal(t, scenario.Success, string(body)) require.NoError(t, compile(genFile), "generated converter doesn't build") }) - clearDir(execDir) + if os.Getenv("SKIP_CLEAN") != "true" { + clearDir(execDir) + } } } diff --git a/scenario/interface_complex.yml b/scenario/interface_complex.yml new file mode 100644 index 00000000..edc90f7f --- /dev/null +++ b/scenario/interface_complex.yml @@ -0,0 +1,55 @@ +input: + input.go: | + package structs + + import "io" + + // goverter:converter + // goverter:extend ConvertInterface + type Converter interface { + Convert(source map[string]interface { + io.Reader + Test() bool + }) map[string]interface { + io.Writer + Test() bool + } + } + + func ConvertInterface(x interface { + io.Reader + Test() bool + }) interface { + io.Writer + Test() bool + } { + return nil + } +success: | + // Code generated by github.com/jmattheis/goverter, DO NOT EDIT. + + package generated + + import ( + execution "github.com/jmattheis/goverter/execution" + "io" + ) + + type ConverterImpl struct{} + + func (c *ConverterImpl) Convert(source map[string]interface { + io.Reader + Test() bool + }) map[string]interface { + io.Writer + Test() bool + } { + mapStringUnknown := make(map[string]interface { + io.Writer + Test() bool + }, len(source)) + for key, value := range source { + mapStringUnknown[key] = execution.ConvertInterface(value) + } + return mapStringUnknown + } diff --git a/scenario/interface_embed.yml b/scenario/interface_embed.yml new file mode 100644 index 00000000..0c47810c --- /dev/null +++ b/scenario/interface_embed.yml @@ -0,0 +1,40 @@ +input: + input.go: | + package structs + + import "io" + + // goverter:converter + // goverter:extend ConvertInterface + type Converter interface { + Convert(source map[string]interface{ io.Reader }) map[string]interface{ io.Writer } + } + + func ConvertInterface(x interface{io.Reader}) interface{io.Writer} { + return nil + } +success: | + // Code generated by github.com/jmattheis/goverter, DO NOT EDIT. + + package generated + + import ( + execution "github.com/jmattheis/goverter/execution" + "io" + ) + + type ConverterImpl struct{} + + func (c *ConverterImpl) Convert(source map[string]interface { + io.Reader + }) map[string]interface { + io.Writer + } { + mapStringUnknown := make(map[string]interface { + io.Writer + }, len(source)) + for key, value := range source { + mapStringUnknown[key] = execution.ConvertInterface(value) + } + return mapStringUnknown + } diff --git a/scenario/interface_empty.yml b/scenario/interface_empty.yml new file mode 100644 index 00000000..7fe741a3 --- /dev/null +++ b/scenario/interface_empty.yml @@ -0,0 +1,29 @@ +input: + input.go: | + package structs + + // goverter:converter + // goverter:extend ConvertInterface + type Converter interface { + Convert(source map[string]interface{}) map[string]interface{} + } + + func ConvertInterface(x interface{}) interface{} { + return nil + } +success: | + // Code generated by github.com/jmattheis/goverter, DO NOT EDIT. + + package generated + + import execution "github.com/jmattheis/goverter/execution" + + type ConverterImpl struct{} + + func (c *ConverterImpl) Convert(source map[string]interface{}) map[string]interface{} { + mapStringUnknown := make(map[string]interface{}, len(source)) + for key, value := range source { + mapStringUnknown[key] = execution.ConvertInterface(value) + } + return mapStringUnknown + } diff --git a/scenario/interface_methods.yml b/scenario/interface_methods.yml new file mode 100644 index 00000000..7714048f --- /dev/null +++ b/scenario/interface_methods.yml @@ -0,0 +1,39 @@ +input: + input.go: | + package structs + + // goverter:converter + // goverter:extend ConvertInterface + type Converter interface { + Convert(source map[string]interface { + Test(x bool) (l string, err error) + }) map[string]interface{ Test2() } + } + + func ConvertInterface(x interface { + Test(x bool) (l string, err error) + }) interface{ Test2() } { + return nil + } +success: | + // Code generated by github.com/jmattheis/goverter, DO NOT EDIT. + + package generated + + import execution "github.com/jmattheis/goverter/execution" + + type ConverterImpl struct{} + + func (c *ConverterImpl) Convert(source map[string]interface { + Test(bool) (string, error) + }) map[string]interface { + Test2() + } { + mapStringUnknown := make(map[string]interface { + Test2() + }, len(source)) + for key, value := range source { + mapStringUnknown[key] = execution.ConvertInterface(value) + } + return mapStringUnknown + } diff --git a/xtype/type.go b/xtype/type.go index b12e7409..27215813 100644 --- a/xtype/type.go +++ b/xtype/type.go @@ -264,10 +264,46 @@ func toCode(t types.Type) *jen.Statement { return toCodeBasic(cast.Kind()) case *types.Struct: return toCodeStruct(cast) + case *types.Interface: + return toCodeInterface(cast) } panic("unsupported type " + t.String()) } +func toCodeInterface(t *types.Interface) *jen.Statement { + content := []jen.Code{} + for i := 0; i < t.NumEmbeddeds(); i++ { + content = append(content, toCode(t.EmbeddedType(i))) + } + + for i := 0; i < t.NumExplicitMethods(); i++ { + method := t.ExplicitMethod(i) + content = append(content, toCodeFunc(method)) + } + + return jen.Interface(content...) +} + +func toCodeFunc(t *types.Func) *jen.Statement { + sig := t.Type().(*types.Signature) + return toCodeSignature(t.Name(), sig) +} + +func toCodeSignature(name string, t *types.Signature) *jen.Statement { + jenParams := []jen.Code{} + params := t.Params() + for i := 0; i < params.Len(); i++ { + jenParams = append(jenParams, toCode(params.At(i).Type())) + } + + jenResults := []jen.Code{} + results := t.Results() + for i := 0; i < results.Len(); i++ { + jenResults = append(jenResults, toCode(results.At(i).Type())) + } + return jen.Id(name).Params(jenParams...).Params(jenResults...) +} + func toCodeNamed(t *types.Named) *jen.Statement { var name *jen.Statement if t.Obj().Pkg() == nil {