From f090448303be63a76657764bd650235299ed4fb7 Mon Sep 17 00:00:00 2001 From: LandonTClipp <11232769+LandonTClipp@users.noreply.github.com> Date: Sun, 29 Dec 2024 00:15:59 -0600 Subject: [PATCH] Working on mockery mocks --- .mockery.yaml | 14 +- cmd/mockery.go | 10 - .../mockery/v2/pkg/fixtures/mockery_mock.go | 408 ++++++++++++++++++ pkg/registry/var.go | 14 + pkg/template/mockery.templ | 77 +++- pkg/template/template.go | 45 ++ pkg/template/template_data.go | 30 +- pkg/template_generator.go | 7 +- 8 files changed, 584 insertions(+), 21 deletions(-) diff --git a/.mockery.yaml b/.mockery.yaml index fe0803b1..a740c922 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -36,12 +36,14 @@ packages: # unroll-variadic: False # - mockname: RequesterVariadic # unroll-variadic: True - #Expecter: - # configs: - # - mockname: ExpecterAndRolledVariadic - # unroll-variadic: False - # - mockname: Expecter - # unroll-variadic: True + Expecter: + configs: + - mockname: ExpecterAndRolledVariadic + template-data: + unroll-variadic: False + - mockname: Expecter + template-data: + unroll-variadic: True #RequesterReturnElided: #VariadicNoReturnInterface: # config: diff --git a/cmd/mockery.go b/cmd/mockery.go index 873759dd..8edf9319 100644 --- a/cmd/mockery.go +++ b/cmd/mockery.go @@ -235,16 +235,6 @@ func (r *RootApp) Run() error { } buildTags := strings.Split(r.Config.BuildTags, " ") - // TODO: Fix boilerplate - //var boilerplate string - //if r.Config.BoilerplateFile != "" { - // data, err := os.ReadFile(r.Config.BoilerplateFile) - // if err != nil { - // log.Fatal().Msgf("Failed to read boilerplate file %s: %v", r.Config.BoilerplateFile, err) - // } - // boilerplate = string(data) - //} - configuredPackages, err := r.Config.GetPackages(ctx) if err != nil { return fmt.Errorf("failed to get package from config: %w", err) diff --git a/mocks/github.com/vektra/mockery/v2/pkg/fixtures/mockery_mock.go b/mocks/github.com/vektra/mockery/v2/pkg/fixtures/mockery_mock.go index 3c87a7d9..6ce6926c 100644 --- a/mocks/github.com/vektra/mockery/v2/pkg/fixtures/mockery_mock.go +++ b/mocks/github.com/vektra/mockery/v2/pkg/fixtures/mockery_mock.go @@ -14,6 +14,292 @@ import ( "github.com/vektra/mockery/v2/pkg/fixtures/constraints" ) +// NewExpecterAndRolledVariadic creates a new instance of ExpecterAndRolledVariadic. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewExpecterAndRolledVariadic(t interface { + mock.TestingT + Cleanup(func()) +}) *ExpecterAndRolledVariadic { + mock := &ExpecterAndRolledVariadic{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// ExpecterAndRolledVariadic is an autogenerated mock type for the Expecter type +type ExpecterAndRolledVariadic struct { + mock.Mock +} + +// ManyArgsReturns provides a mock function for the type ExpecterAndRolledVariadic +func (_mock *ExpecterAndRolledVariadic) ManyArgsReturns(str string, i int) ([]string, error) { + var retArgs mock.Arguments + + if retArgs == nil { + retArgs = _mock.Called(str, i) + } + + if len(retArgs) == 0 { + panic("no return value specified for ExpecterAndRolledVariadic.ManyArgsReturns") + } + + var ( + retVal0 []string + retVal1 error + ) + if returnFunc, ok := ret.Get(0).(func(string, int) ([]string, error)); ok { + return returnFunc(str, i) + } + if returnFunc, ok := retArgs.Get(0).(func(string, int) []string); ok { + retVal0 = returnFunc(str, i) + } else { + if retArgs.Get(0) != nil { + retVal0 = retArgs.Get(0).([]string) + } + } + if returnFunc, ok := retArgs.Get(1).(func(string, int) error); ok { + retVal1 = returnFunc(str, i) + } else { + retVal1 = retArgs.Error(1) + } + return retVal0, retVal1 +} + +// NoArg provides a mock function for the type ExpecterAndRolledVariadic +func (_mock *ExpecterAndRolledVariadic) NoArg() string { + var retArgs mock.Arguments + + if retArgs == nil { + retArgs = _mock.Called() + } + + if len(retArgs) == 0 { + panic("no return value specified for ExpecterAndRolledVariadic.NoArg") + } + + var ( + retVal0 string + ) + if returnFunc, ok := retArgs.Get(0).(func() string); ok { + retVal0 = returnFunc() + } else { + retVal0 = retArgs.Get(0).(string) + } + return retVal0 +} + +// NoReturn provides a mock function for the type ExpecterAndRolledVariadic +func (_mock *ExpecterAndRolledVariadic) NoReturn(str string) { + var retArgs mock.Arguments + + _mock.Called(str) + return +} + +// Variadic provides a mock function for the type ExpecterAndRolledVariadic +func (_mock *ExpecterAndRolledVariadic) Variadic(ints ...int) error { + var retArgs mock.Arguments + if len(ints) > 0 { + retArgs = _mock.Called(ints...) + } else { + retArgs = _mock.Called() + } + + if retArgs == nil { + retArgs = _mock.Called(ints...) + } + + if len(retArgs) == 0 { + panic("no return value specified for ExpecterAndRolledVariadic.Variadic") + } + + var ( + retVal0 error + ) + if returnFunc, ok := retArgs.Get(0).(func([]int) error); ok { + retVal0 = returnFunc(ints...) + } else { + retVal0 = retArgs.Error(0) + } + return retVal0 +} + +// VariadicMany provides a mock function for the type ExpecterAndRolledVariadic +func (_mock *ExpecterAndRolledVariadic) VariadicMany(i int, a string, intfs ...interface{}) error { + var retArgs mock.Arguments + if len(intfs) > 0 { + retArgs = _mock.Called(i, a, intfs...) + } else { + retArgs = _mock.Called(i, a) + } + + if retArgs == nil { + retArgs = _mock.Called(i, a, intfs...) + } + + if len(retArgs) == 0 { + panic("no return value specified for ExpecterAndRolledVariadic.VariadicMany") + } + + var ( + retVal0 error + ) + if returnFunc, ok := retArgs.Get(0).(func(int, string, []interface{}) error); ok { + retVal0 = returnFunc(i, a, intfs...) + } else { + retVal0 = retArgs.Error(0) + } + return retVal0 +} + +type ExpecterAndRolledVariadic_expecter struct { + mock *mock.Mock +} + +func (_m *ExpecterAndRolledVariadic) EXPECT() *ExpecterAndRolledVariadic_expecter { + return &ExpecterAndRolledVariadic_expecter{mock: &_m.Mock} +} + +// NewExpecter creates a new instance of Expecter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewExpecter(t interface { + mock.TestingT + Cleanup(func()) +}) *Expecter { + mock := &Expecter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} + +// Expecter is an autogenerated mock type for the Expecter type +type Expecter struct { + mock.Mock +} + +// ManyArgsReturns provides a mock function for the type Expecter +func (_mock *Expecter) ManyArgsReturns(str string, i int) ([]string, error) { + var retArgs mock.Arguments + + if retArgs == nil { + retArgs = _mock.Called(str, i) + } + + if len(retArgs) == 0 { + panic("no return value specified for Expecter.ManyArgsReturns") + } + + var ( + retVal0 []string + retVal1 error + ) + if returnFunc, ok := ret.Get(0).(func(string, int) ([]string, error)); ok { + return returnFunc(str, i) + } + if returnFunc, ok := retArgs.Get(0).(func(string, int) []string); ok { + retVal0 = returnFunc(str, i) + } else { + if retArgs.Get(0) != nil { + retVal0 = retArgs.Get(0).([]string) + } + } + if returnFunc, ok := retArgs.Get(1).(func(string, int) error); ok { + retVal1 = returnFunc(str, i) + } else { + retVal1 = retArgs.Error(1) + } + return retVal0, retVal1 +} + +// NoArg provides a mock function for the type Expecter +func (_mock *Expecter) NoArg() string { + var retArgs mock.Arguments + + if retArgs == nil { + retArgs = _mock.Called() + } + + if len(retArgs) == 0 { + panic("no return value specified for Expecter.NoArg") + } + + var ( + retVal0 string + ) + if returnFunc, ok := retArgs.Get(0).(func() string); ok { + retVal0 = returnFunc() + } else { + retVal0 = retArgs.Get(0).(string) + } + return retVal0 +} + +// NoReturn provides a mock function for the type Expecter +func (_mock *Expecter) NoReturn(str string) { + var retArgs mock.Arguments + + _mock.Called(str) + return +} + +// Variadic provides a mock function for the type Expecter +func (_mock *Expecter) Variadic(ints ...int) error { + var retArgs mock.Arguments + + if retArgs == nil { + retArgs = _mock.Called(ints...) + } + + if len(retArgs) == 0 { + panic("no return value specified for Expecter.Variadic") + } + + var ( + retVal0 error + ) + if returnFunc, ok := retArgs.Get(0).(func([]int) error); ok { + retVal0 = returnFunc(ints...) + } else { + retVal0 = retArgs.Error(0) + } + return retVal0 +} + +// VariadicMany provides a mock function for the type Expecter +func (_mock *Expecter) VariadicMany(i int, a string, intfs ...interface{}) error { + var retArgs mock.Arguments + + if retArgs == nil { + retArgs = _mock.Called(i, a, intfs...) + } + + if len(retArgs) == 0 { + panic("no return value specified for Expecter.VariadicMany") + } + + var ( + retVal0 error + ) + if returnFunc, ok := retArgs.Get(0).(func(int, string, []interface{}) error); ok { + retVal0 = returnFunc(i, a, intfs...) + } else { + retVal0 = retArgs.Error(0) + } + return retVal0 +} + +type Expecter_expecter struct { + mock *mock.Mock +} + +func (_m *Expecter) EXPECT() *Expecter_expecter { + return &Expecter_expecter{mock: &_m.Mock} +} + // NewMockRequesterGenerics creates a new instance of MockRequesterGenerics. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewMockRequesterGenerics[TAny any, TComparable comparable, TSigned constraints.Signed, TIntf test.GetInt, TExternalIntf io.Writer, TGenIntf test.GetGeneric[TSigned], TInlineType interface{ ~int | ~uint }, TInlineTypeGeneric interface { @@ -39,6 +325,96 @@ type MockRequesterGenerics[TAny any, TComparable comparable, TSigned constraints mock.Mock } +// GenericAnonymousStructs provides a mock function for the type MockRequesterGenerics +func (_mock *MockRequesterGenerics[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) GenericAnonymousStructs(val struct{ Type1 TExternalIntf }) struct { + Type2 test.GenericType[string, test.EmbeddedGet[int]] +} { + var retArgs mock.Arguments + + if retArgs == nil { + retArgs = _mock.Called(val) + } + + if len(retArgs) == 0 { + panic("no return value specified for MockRequesterGenerics.GenericAnonymousStructs") + } + + var ( + retVal0 struct { + Type2 test.GenericType[string, test.EmbeddedGet[int]] + } + ) + if returnFunc, ok := retArgs.Get(0).(func(struct{ Type1 TExternalIntf }) struct { + Type2 test.GenericType[string, test.EmbeddedGet[int]] + }); ok { + retVal0 = returnFunc(val) + } else { + retVal0 = retArgs.Get(0).(struct { + Type2 test.GenericType[string, test.EmbeddedGet[int]] + }) + } + return retVal0 +} + +// GenericArguments provides a mock function for the type MockRequesterGenerics +func (_mock *MockRequesterGenerics[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) GenericArguments(v1 TAny, v2 TComparable) (TSigned, TIntf) { + var retArgs mock.Arguments + + if retArgs == nil { + retArgs = _mock.Called(v1, v2) + } + + if len(retArgs) == 0 { + panic("no return value specified for MockRequesterGenerics.GenericArguments") + } + + var ( + retVal0 TSigned + retVal1 TIntf + ) + if returnFunc, ok := ret.Get(0).(func(TAny, TComparable) (TSigned, TIntf)); ok { + return returnFunc(v1, v2) + } + if returnFunc, ok := retArgs.Get(0).(func(TAny, TComparable) TSigned); ok { + retVal0 = returnFunc(v1, v2) + } else { + if retArgs.Get(0) != nil { + retVal0 = retArgs.Get(0).(TSigned) + } + } + if returnFunc, ok := retArgs.Get(1).(func(TAny, TComparable) TIntf); ok { + retVal1 = returnFunc(v1, v2) + } else { + if retArgs.Get(1) != nil { + retVal1 = retArgs.Get(1).(TIntf) + } + } + return retVal0, retVal1 +} + +// GenericStructs provides a mock function for the type MockRequesterGenerics +func (_mock *MockRequesterGenerics[TAny, TComparable, TSigned, TIntf, TExternalIntf, TGenIntf, TInlineType, TInlineTypeGeneric]) GenericStructs(genericType test.GenericType[TAny, TIntf]) test.GenericType[TSigned, TIntf] { + var retArgs mock.Arguments + + if retArgs == nil { + retArgs = _mock.Called(genericType) + } + + if len(retArgs) == 0 { + panic("no return value specified for MockRequesterGenerics.GenericStructs") + } + + var ( + retVal0 test.GenericType[TSigned, TIntf] + ) + if returnFunc, ok := retArgs.Get(0).(func(test.GenericType[TAny, TIntf]) test.GenericType[TSigned, TIntf]); ok { + retVal0 = returnFunc(genericType) + } else { + retVal0 = retArgs.Get(0).(test.GenericType[TSigned, TIntf]) + } + return retVal0 +} + type MockRequesterGenerics_expecter[TAny any, TComparable comparable, TSigned constraints.Signed, TIntf test.GetInt, TExternalIntf io.Writer, TGenIntf test.GetGeneric[TSigned], TInlineType interface{ ~int | ~uint }, TInlineTypeGeneric interface { ~int | test.GenericType[int, test.GetInt] comparable @@ -69,6 +445,38 @@ type MockA struct { mock.Mock } +// Call provides a mock function for the type MockA +func (_mock *MockA) Call() (test.B, error) { + var retArgs mock.Arguments + + if retArgs == nil { + retArgs = _mock.Called() + } + + if len(retArgs) == 0 { + panic("no return value specified for MockA.Call") + } + + var ( + retVal0 test.B + retVal1 error + ) + if returnFunc, ok := ret.Get(0).(func() (test.B, error)); ok { + return returnFunc() + } + if returnFunc, ok := retArgs.Get(0).(func() test.B); ok { + retVal0 = returnFunc() + } else { + retVal0 = retArgs.Get(0).(test.B) + } + if returnFunc, ok := retArgs.Get(1).(func() error); ok { + retVal1 = returnFunc() + } else { + retVal1 = retArgs.Error(1) + } + return retVal0, retVal1 +} + type MockA_expecter struct { mock *mock.Mock } diff --git a/pkg/registry/var.go b/pkg/registry/var.go index 4cc3163a..b84985b7 100644 --- a/pkg/registry/var.go +++ b/pkg/registry/var.go @@ -37,6 +37,20 @@ func (v Var) packageQualifier(pkg *types.Package) string { return v.imports[path].Qualifier() } +func nillable(typ types.Type) bool { + switch t := typ.(type) { + case *types.Pointer, *types.Array, *types.Map, *types.Interface, *types.Signature, *types.Chan, *types.Slice: + return true + case *types.Named, *types.Alias, *types.TypeParam: + return nillable(t.Underlying()) + } + return false +} + +func (v Var) Nillable() bool { + return nillable(v.vr.Type()) +} + func varName(vr *types.Var, suffix string) string { name := vr.Name() if name != "" && name != "_" { diff --git a/pkg/template/mockery.templ b/pkg/template/mockery.templ index 72120333..5602c56b 100644 --- a/pkg/template/mockery.templ +++ b/pkg/template/mockery.templ @@ -16,7 +16,7 @@ import ( mock "github.com/stretchr/testify/mock" ) -{{- range $i, $mock := .Mocks }} +{{- range $i, $mock := .Mocks }} {{/* START MOCK RANGE */}} // New{{ .MockName }} creates a new instance of {{ .MockName }}. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func New{{ .MockName }}{{ $mock | TypeConstraint }} (t interface { @@ -31,11 +31,84 @@ func New{{ .MockName }}{{ $mock | TypeConstraint }} (t interface { return mock } + // {{ .MockName }} is an autogenerated mock type for the {{ .InterfaceName }} type type {{ .MockName }}{{ $mock | TypeConstraint }} struct { mock.Mock } +{{- range $methodIdx, $method := .Methods }} {{/* START METHOD RANGE */}} + +// {{ $method.Name }} provides a mock function for the type {{ $mock.MockName }} +func (_mock *{{$mock.MockName}}{{ $mock | TypeInstantiation }}) {{$method.Name}}({{$method.ArgList}}) {{$method.ReturnArgTypeList}} { + var retArgs mock.Arguments +{{- if or + (eq (len $method.ArgList) 0) + (not $method.IsVariadic) + (not (index $mock.TemplateData "unroll-variadic")) +}} {{/* START PREAMBLE */}} + {{- if and + ($method.IsVariadic) + (not (index $mock.TemplateData "unroll-variadic")) + }} + {{- if gt (len $method.Returns) 0 }} + {{- end }} + {{- $lastParam := index $method.Params (len $method.Params | Add -1 )}} + if len({{ $lastParam.Var.Name }}) > 0 { + retArgs = _mock.Called({{ $method.ArgCallList }}) + } else { + retArgs = _mock.Called({{ $method.ArgCallListSlice 0 (len $method.Params | Add -1 )}}) + } + {{- end }} +{{- else }} +{{/* TODO: finish https://github.com/vektra/mockery/blob/ede4f2c90f854f51c8618c934930f7bf6cdb5a5d/pkg/generator.go#L1109-L1153 */}} +{{- end }} +{{/* END PREAMBLE */}} + {{- if eq (len $method.Returns) 0 }} + _mock.Called({{ $method.ArgCallList }}) + {{- else }} + if retArgs == nil { + retArgs = _mock.Called({{ $method.ArgCallList }}) + } + + + if len(retArgs) == 0 { + panic("no return value specified for {{$mock.MockName}}.{{$method.Name}}") + } + + var ( + {{- range $retIdx, $ret := $method.Returns }} + retVal{{ $retIdx }} {{ (index $method.Returns $retIdx).TypeString }} + {{- end }} + ) + + {{- if gt (len $method.Returns) 1 }} + if returnFunc, ok := ret.Get(0).(func({{ $method.ArgTypeList }}) {{ $method.ReturnArgTypeList }}); ok { + return returnFunc({{ $method.ArgCallList }}) + } + {{- end }} + + {{- range $retIdx, $ret := $method.Returns }} {{/* START RETURN RANGE */}} + if returnFunc, ok := retArgs.Get({{ $retIdx }}).(func({{$method.ArgTypeList }}) {{ (index $method.Returns $retIdx).TypeString }}); ok { + retVal{{ $retIdx }} = returnFunc({{ $method.ArgCallList }}) + } else { + {{- if eq "error" (index $method.Returns $retIdx).TypeString }} + retVal{{ $retIdx }} = retArgs.Error({{ $retIdx }}) + {{- else if (index $method.Returns $retIdx).Var.Nillable }} + if retArgs.Get({{ $retIdx }}) != nil { + retVal{{ $retIdx }} = retArgs.Get({{ $retIdx }}).({{ (index $method.Returns $retIdx).TypeString }}) + } + {{- else }} + retVal{{ $retIdx }} = retArgs.Get({{ $retIdx }}).({{ (index $method.Returns $retIdx).TypeString }}) + {{- end }} + } + {{- end }} {{/* END RETURN RANGE */}} + {{- end }} + return {{ range $retIdx, $ret := $method.Returns }}retVal{{ $retIdx }}{{ if ne $retIdx (len $method.Returns | Add -1) }}, {{ end }}{{ end }} +} +{{- end }} {{/* END METHOD RANGE */}} + + type {{.MockName}}_expecter{{ $mock | TypeConstraint }} struct { mock *mock.Mock } @@ -43,5 +116,5 @@ type {{.MockName}}_expecter{{ $mock | TypeConstraint }} struct { func (_m *{{.MockName}}{{ $mock | TypeInstantiation }}) EXPECT() *{{.MockName}}_expecter{{ $mock | TypeInstantiation }} { return &{{.MockName}}_expecter{{ $mock | TypeInstantiation }}{mock: &_m.Mock} } -{{- end }} +{{- end }} {{/* END MOCK RANGE */}} diff --git a/pkg/template/template.go b/pkg/template/template.go index 92da5a49..68d6ea8e 100644 --- a/pkg/template/template.go +++ b/pkg/template/template.go @@ -2,11 +2,15 @@ package template import ( "io" + "os" + "path/filepath" + "regexp" "strings" "text/template" _ "embed" + "github.com/huandu/xstrings" "github.com/vektra/mockery/v2/pkg/registry" "github.com/vektra/mockery/v2/pkg/stackerr" ) @@ -143,4 +147,45 @@ var templateFuncs = template.FuncMap{ s += "]" return s }, + // String inspection and manipulation. Note that the first argument is replaced + // as the last argument in some functions in order to support chained + // template pipelines. + "Contains": func(substr string, s string) bool { return strings.Contains(s, substr) }, + "HasPrefix": func(prefix string, s string) bool { return strings.HasPrefix(s, prefix) }, + "HasSuffix": func(suffix string, s string) bool { return strings.HasSuffix(s, suffix) }, + "Join": func(sep string, elems []string) string { return strings.Join(elems, sep) }, + "Replace": func(old string, new string, n int, s string) string { return strings.Replace(s, old, new, n) }, + "ReplaceAll": func(old string, new string, s string) string { return strings.ReplaceAll(s, old, new) }, + "Split": func(sep string, s string) []string { return strings.Split(s, sep) }, + "SplitAfter": func(sep string, s string) []string { return strings.SplitAfter(s, sep) }, + "SplitAfterN": func(sep string, n int, s string) []string { return strings.SplitAfterN(s, sep, n) }, + "Trim": func(cutset string, s string) string { return strings.Trim(s, cutset) }, + "TrimLeft": func(cutset string, s string) string { return strings.TrimLeft(s, cutset) }, + "TrimPrefix": func(prefix string, s string) string { return strings.TrimPrefix(s, prefix) }, + "TrimRight": func(cutset string, s string) string { return strings.TrimRight(s, cutset) }, + "TrimSpace": strings.TrimSpace, + "TrimSuffix": func(suffix string, s string) string { return strings.TrimSuffix(s, suffix) }, + "Lower": strings.ToLower, + "Upper": strings.ToUpper, + "Camelcase": xstrings.ToCamelCase, + "Snakecase": xstrings.ToSnakeCase, + "Kebabcase": xstrings.ToKebabCase, + "FirstLower": xstrings.FirstRuneToLower, + "FirstUpper": xstrings.FirstRuneToUpper, + + // Regular expression matching + "MatchString": regexp.MatchString, + "QuoteMeta": regexp.QuoteMeta, + + // Filepath manipulation + "Base": filepath.Base, + "Clean": filepath.Clean, + "Dir": filepath.Dir, + + // Basic access to reading environment variables + "ExpandEnv": os.ExpandEnv, + "Getenv": os.Getenv, + + // Arithmetic + "Add": func(i1, i2 int) int { return i1 + i2 }, } diff --git a/pkg/template/template_data.go b/pkg/template/template_data.go index 04eaee50..ace5fba8 100644 --- a/pkg/template/template_data.go +++ b/pkg/template/template_data.go @@ -60,12 +60,34 @@ func (m MethodData) ArgList() string { return strings.Join(params, ", ") } +// ArgTypeList returns the argument types in a comma-separated string, ex: +// `string, int, bar.Baz` +func (m MethodData) ArgTypeList() string { + params := make([]string, len(m.Params)) + for i, p := range m.Params { + params[i] = p.TypeString() + } + return strings.Join(params, ", ") +} + // ArgCallList is the string representation of method call parameters, // ex: 's, n, foo'. In case of a last variadic parameter, it will be of // the format 's, n, foos...' func (m MethodData) ArgCallList() string { - params := make([]string, len(m.Params)) - for i, p := range m.Params { + return m.ArgCallListSlice(0, -1) +} + +// ArgCallListSlice is similar to ArgCallList, but it allows specification of +// a slice range to use for the parameter lists. Specifying an integer less than +// 1 for end indicates to slice to the end of the parameters. As with regular +// Go slicing semantics, the end value is a non-inclusive index. +func (m MethodData) ArgCallListSlice(start, end int) string { + if end < 0 { + end = len(m.Params) + } + paramsSlice := m.Params[start:end] + params := make([]string, len(paramsSlice)) + for i, p := range paramsSlice { params[i] = p.CallName() } return strings.Join(params, ", ") @@ -94,6 +116,10 @@ func (m MethodData) ReturnArgNameList() string { return strings.Join(params, ", ") } +func (m MethodData) IsVariadic() bool { + return len(m.Params) > 0 && m.Params[len(m.Params)-1].Variadic +} + type TypeParamData struct { ParamData Constraint types.Type diff --git a/pkg/template_generator.go b/pkg/template_generator.go index 18ce301a..e94573ff 100644 --- a/pkg/template_generator.go +++ b/pkg/template_generator.go @@ -1,12 +1,14 @@ package pkg import ( + "bufio" "bytes" "context" "fmt" "go/format" "go/token" "go/types" + "strings" "github.com/chigopher/pathlib" "github.com/rs/zerolog" @@ -198,7 +200,10 @@ func (g *TemplateGenerator) Generate( // grab the formatter as specified in the topmost interface-level config. formatted, err := g.format(buf.Bytes()) if err != nil { - fmt.Print(buf.String()) + scanner := bufio.NewScanner(strings.NewReader(buf.String())) + for i := 1; scanner.Scan(); i++ { + fmt.Printf("%d:\t%s\n", i, scanner.Text()) + } log.Err(err).Msg("can't format mock file") return []byte{}, fmt.Errorf("formatting mock file: %w", err) }