diff --git a/config/method.go b/config/method.go index 9db813c..f175758 100644 --- a/config/method.go +++ b/config/method.go @@ -87,6 +87,7 @@ func parseMethodLine(ctx *context, c *Converter, m *Method, value string) (err e OutputPackagePath: c.OutputPackagePath, Converter: c.Type, Params: method.ParamsOptional, + AllowTypeParams: true, } f.Function, err = ctx.Loader.GetOne(c.Package, custom, opts) } @@ -129,6 +130,7 @@ func parseMethodLine(ctx *context, c *Converter, m *Method, value string) (err e OutputPackagePath: c.OutputPackagePath, Converter: c.Type, Params: method.ParamsOptional, + AllowTypeParams: true, } m.Constructor, err = ctx.Loader.GetOne(c.Package, rest, opts) default: diff --git a/generator/generator.go b/generator/generator.go index e42d4ef..5c8e17c 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -192,13 +192,13 @@ func (g *generator) CallMethod( if definition.Source != nil { params = append(params, sourceID.Code.Clone()) - if !source.AssignableTo(definition.Source) { + if !source.AssignableTo(definition.Source) && !definition.TypeParams { cause := fmt.Sprintf("Method source type mismatches with conversion source: %s != %s", definition.Source.String, source.String) return nil, nil, formatErr(cause) } } - if !definition.Target.AssignableTo(target) { + if !definition.Target.AssignableTo(target) && !definition.TypeParams { cause := fmt.Sprintf("Method return type mismatches with target: %s != %s", definition.Target.String, target.String) return nil, nil, formatErr(cause) } diff --git a/method/definition.go b/method/definition.go index 211c06d..a94e04d 100644 --- a/method/definition.go +++ b/method/definition.go @@ -20,6 +20,7 @@ func (m *Definition) Signature() xtype.Signature { type Parameters struct { ReturnError bool SelfAsFirstParameter bool + TypeParams bool Source *xtype.Type Target *xtype.Type } diff --git a/method/parse.go b/method/parse.go index bdf5142..142d095 100644 --- a/method/parse.go +++ b/method/parse.go @@ -20,9 +20,10 @@ type ParseOpts struct { Converter types.Type OutputPackagePath string - ErrorPrefix string - ConvFunction bool - Params ParamType + ErrorPrefix string + Params ParamType + ConvFunction bool + AllowTypeParams bool } // Parse parses an function into a Definition. @@ -62,10 +63,15 @@ func Parse(obj types.Object, opts *ParseOpts) (*Definition, error) { Parameters: Parameters{ ReturnError: returnError, Target: xtype.TypeOf(sig.Results().At(0).Type()), + TypeParams: sig.TypeParams().Len() > 0, }, Name: fn.Name(), } + if methodDef.TypeParams && !opts.AllowTypeParams { + return nil, formatErr("must not be generic") + } + if opts.ConvFunction { methodDef.Call = jen.Id(xtype.ThisVar).Dot(fn.Name()) } else { diff --git a/scenario/extend_generic.yml b/scenario/extend_generic.yml new file mode 100644 index 0000000..5161d25 --- /dev/null +++ b/scenario/extend_generic.yml @@ -0,0 +1,24 @@ +input: + input.go: | + package structs + + // goverter:converter + // goverter:extend Extend + type Converter interface { + Convert(source Input) (Output, error) + } + + func Extend[T any](T) int { + return 0 + } + type Input struct { Age string } + type Output struct { Age int } +error: |- + error parsing 'goverter:extend' at + @workdir/input.go:5:1 + github.com/jmattheis/goverter/execution.Converter + + error parsing type: + func github.com/jmattheis/goverter/execution.Extend[T any](T) int + + must not be generic diff --git a/scenario/map_custom_generic.yml b/scenario/map_custom_generic.yml new file mode 100644 index 0000000..06acdf5 --- /dev/null +++ b/scenario/map_custom_generic.yml @@ -0,0 +1,31 @@ +input: + input.go: | + package structs + + // goverter:converter + type Converter interface { + // goverter:map ID | ZeroIfNil + Convert(source Input) Output + } + + type Input struct { ID *int } + type Output struct { ID int } + func ZeroIfNil[T any](*T) T { + var t T + return t + } +success: + - generated/generated.go: | + // 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 execution.Input) execution.Output { + var structsOutput execution.Output + structsOutput.ID = execution.ZeroIfNil(source.ID) + return structsOutput + } diff --git a/xtype/type.go b/xtype/type.go index 05a0276..f552feb 100644 --- a/xtype/type.go +++ b/xtype/type.go @@ -245,6 +245,8 @@ func TypeOf(t types.Type) *Type { case *types.Signature: rt.Signature = true rt.SignatureType = value + case *types.TypeParam: + // ignore default: panic("unknown types.Type " + t.String()) }