Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/mocks generation #32

Merged
merged 5 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions internal/commands/generate_mocks_command.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package commands

import (
"errors"
"fmt"
"github.com/matzefriedrich/cobra-extensions/pkg"
"github.com/matzefriedrich/cobra-extensions/pkg/abstractions"
Expand Down Expand Up @@ -61,8 +62,9 @@ func (m *mocksGeneratorCommand) Execute() {
})

err := gen.GenerateCode()
if err != nil {
fmt.Println(err)
for err != nil {
fmt.Printf("%+v\n", err)
err = errors.Unwrap(err)
}
}

Expand Down
20 changes: 0 additions & 20 deletions internal/generator/generic_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"github.com/pkg/errors"
"go/format"
"io"
"os"
"reflect"
"text/template"
)
Expand Down Expand Up @@ -86,25 +85,6 @@ func (g *genericGenerator) Generate(templateName string, templateModel any, writ
return nil
}

func (g *genericGenerator) LoadTemplateFromFile(templateFile string) (string, error) {

if _, err := os.Stat(templateFile); errors.Is(err, os.ErrNotExist) {
return "", newGeneratorError(ErrorTemplateFileNotFound, types.WithCause(err))
}

f, err := os.OpenFile(templateFile, os.O_RDONLY, 400)
defer func(file *os.File) {
_ = file.Close()
}(f)

if err != nil {
return "", newGeneratorError(ErrorFailedToOpenTemplateFile, types.WithCause(err))
}

data, _ := io.ReadAll(f)
return string(data), nil
}

func RegisterTemplateFunctions(g GenericCodeGenerator, functions ...func(generator GenericCodeGenerator) error) error {
for _, function := range functions {
err := function(g)
Expand Down
40 changes: 37 additions & 3 deletions internal/generator/type_model_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"
)

// RegisterTypeModelFunctions registers a series of functions for type model processing in the given code generator.
func RegisterTypeModelFunctions(generator GenericCodeGenerator) error {
return generator.AddTemplateFunc(
NamedFunc("FormatType", FormatType),
Expand All @@ -19,10 +20,12 @@ func RegisterTypeModelFunctions(generator GenericCodeGenerator) error {
)
}

// HasResults checks if the given reflection.Method has any result parameters.
func HasResults(m reflection.Method) bool {
return len(m.Results) > 0
}

// FormattedParameters formats the parameters of the given reflection.Method into a comma-separated string with type info.
func FormattedParameters(m reflection.Method) string {
formattedParameters := make([]string, len(m.Parameters))
for i, parameter := range m.Parameters {
Expand All @@ -32,23 +35,36 @@ func FormattedParameters(m reflection.Method) string {
return strings.Join(formattedParameters, ", ")
}

// FormattedCallParameters formats the call parameters of the given reflection.Method into a comma-separated string.
func FormattedCallParameters(m reflection.Method) string {
formattedParameters := make([]string, len(m.Parameters))
for i, parameter := range m.Parameters {
formattedParameters[i] = fmt.Sprintf("%s", parameter.Name)
name := parameter.Name
if parameter.IsEllipsis() {
name = fmt.Sprintf("%s...", name)
}
formattedParameters[i] = fmt.Sprintf("%s", name)
}
return strings.Join(formattedParameters, ", ")
}

// FormattedResultParameters formats the result parameters of the given reflection.Method into a comma-separated string.
func FormattedResultParameters(m reflection.Method) string {
if m.Results == nil {
return ""
}
formattedResults := make([]string, len(m.Results))
for i, result := range m.Results {
formattedResults[i] = fmt.Sprintf("%s", result.Name)
}
return strings.Join(formattedResults, ", ")
}

// FormattedResultTypes formats the result types of the given reflection.Method into a string representation.
func FormattedResultTypes(m reflection.Method) string {
if m.Results == nil {
return ""
}
formattedResults := make([]string, len(m.Results))
for i, result := range m.Results {
typeName := FormatType(result)
Expand All @@ -60,6 +76,7 @@ func FormattedResultTypes(m reflection.Method) string {
return "(" + strings.Join(formattedResults, ", ") + ")"
}

// Signature generates the signature string of a given method.
func Signature(m reflection.Method) string {
buffer := strings.Builder{}
buffer.WriteString(fmt.Sprintf("%s", m.Name))
Expand All @@ -70,8 +87,19 @@ func Signature(m reflection.Method) string {
return buffer.String()
}

const (
ellipsis = "..."
star = "*"
array = "[]"
)

// FormatType formats the given reflection.Parameter's type information into a string representation.
func FormatType(parameter reflection.Parameter) string {

if parameter.Type == nil {
return "any"
}

segments := make([]string, 0)

s := internal.MakeStack[*reflection.ParameterType]()
Expand All @@ -86,13 +114,19 @@ func FormatType(parameter reflection.Parameter) string {
typeName = fmt.Sprintf("%s.%s", t.SelectorName, typeName)
}

if t.IsInterface {
typeName = fmt.Sprintf("interface{}")
}

if t.IsEllipsis {
typeName = ellipsis + typeName
}

if t.IsPointer {
star := "*"
typeName = star + typeName
}

if t.IsArray {
array := "[]"
typeName = array + typeName
}

Expand Down
6 changes: 5 additions & 1 deletion internal/reflection/type_syntax_walker.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ func NewTypeWalker(typeVisitor AstTypeVisitor) AstTypeSpecWalker {

func (t *typeWalker) WalkInterface(interfaceType *ast.InterfaceType) {
for _, method := range interfaceType.Methods.List {
name := method.Names[0].Name
methodNames := method.Names
if methodNames == nil || len(methodNames) == 0 {
continue
}
name := methodNames[0].Name
if funcType, ok := method.Type.(*ast.FuncType); ok {
t.WalkFunc(name, funcType)
}
Expand Down
14 changes: 8 additions & 6 deletions internal/reflection/type_visitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ import (
"go/ast"
)

type fieldTypeInfo struct {
Name string
IsArray bool
IsPointer bool
}

type interfaceMethodsCollector struct {
model *Interface
}
Expand Down Expand Up @@ -78,11 +72,19 @@ func getFieldTypeInfo(param *ast.Field) *ParameterType {
next := expressionStack.Pop()

switch next.(type) {
case *ast.Ellipsis:
ellipsis, _ := next.(*ast.Ellipsis)
typeStack.Push(ParameterType{IsEllipsis: true})
expressionStack.Push(ellipsis.Elt)

case *ast.Ident:
ident, _ := next.(*ast.Ident)
paramTypeName = ident.Name
typeStack.Push(ParameterType{Name: paramTypeName})

case *ast.InterfaceType:
typeStack.Push(ParameterType{IsInterface: true})

case *ast.SelectorExpr:
selector, _ := next.(*ast.SelectorExpr)
ident, _ := selector.X.(*ast.Ident)
Expand Down
14 changes: 14 additions & 0 deletions internal/reflection/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,24 @@ type Parameter struct {
Type *ParameterType
}

// IsEllipsis determines if the parameter type is an ellipsis (`...`) parameter.
func (p Parameter) IsEllipsis() bool {
pt := p.Type
for pt != nil {
if pt.IsEllipsis {
return true
}
pt = pt.Next
}
return false
}

type ParameterType struct {
Name string
SelectorName string
IsArray bool
IsEllipsis bool
IsInterface bool
IsPointer bool
Next *ParameterType
}
Expand Down
3 changes: 2 additions & 1 deletion internal/tests/commands/generate_mocks_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package commands
import (
"github.com/matzefriedrich/parsley/internal/commands"
"github.com/matzefriedrich/parsley/internal/reflection"
"github.com/matzefriedrich/parsley/internal/tests/mocks"
"github.com/stretchr/testify/assert"
"io"
"testing"
Expand All @@ -17,7 +18,7 @@ func Test_GenerateMocksCommand_Execute(t *testing.T) {
" SayHello(name string)" + "\n" +
"}")

buffer := newMemoryFile()
buffer := mocks.NewMemoryFile()
outputWriterFactory := func(kind string, source *reflection.AstFileSource) (io.WriteCloser, error) {
return buffer, nil
}
Expand Down
3 changes: 2 additions & 1 deletion internal/tests/commands/generate_proxy_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package commands
import (
"github.com/matzefriedrich/parsley/internal/commands"
"github.com/matzefriedrich/parsley/internal/reflection"
"github.com/matzefriedrich/parsley/internal/tests/mocks"
"github.com/stretchr/testify/assert"
"io"
"testing"
Expand All @@ -16,7 +17,7 @@ func Test_GenerateProxyCommand_Execute(t *testing.T) {
" SayHello(name string)" + "\n" +
"}")

buffer := newMemoryFile()
buffer := mocks.NewMemoryFile()
outputWriterFactory := func(kind string, source *reflection.AstFileSource) (io.WriteCloser, error) {
return buffer, nil
}
Expand Down
31 changes: 31 additions & 0 deletions internal/tests/generator/generic_generator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package generator

import (
"fmt"
"github.com/matzefriedrich/parsley/internal/generator"
"github.com/matzefriedrich/parsley/internal/tests/mocks"
"github.com/stretchr/testify/assert"
"testing"
)

func Test_GenericGenerator_Generate_load_requested_template_via_loader_and_generates_output(t *testing.T) {
// Arrange
sut := generator.NewGenericCodeGenerator(func(name string) (string, error) {
switch name {
case "template":
return "{{ .Msg }}", nil
}
return "", fmt.Errorf("template not found")
})

target := mocks.NewMemoryFile()

// Act
err := sut.Generate("template", struct{ Msg string }{Msg: "Hello"}, target)

// Assert
assert.NoError(t, err)

actual := target.String()
assert.Equal(t, "Hello", actual)
}
56 changes: 50 additions & 6 deletions internal/tests/generator/type_model_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,12 +172,6 @@ func Test_NewTemplateModelBuilder_Build_interface_method_with_pointer_parameters

reqParameter := doMethod.Parameters[0]
assert.Equal(t, "req", reqParameter.Name)
// assert.Equal(t, "http.Request", reqParameter.TypeName)
// assert.True(t, reqParameter.IsPointer)

// responseResult := doMethod.Results[0]
// assert.Equal(t, "http.Response", responseResult.TypeName)
// assert.True(t, responseResult.IsPointer)
}

func Test_NewTemplateModelBuilder_Build_interface_method_array_pointer_parameter(t *testing.T) {
Expand Down Expand Up @@ -207,3 +201,53 @@ func Test_NewTemplateModelBuilder_Build_interface_method_array_pointer_parameter
signature := generator.Signature(method)
assert.Equal(t, "Method0(args *[]*types.Arg)", signature)
}

func Test_NewTemplateModelBuilder_Build_interface_method_ellipsis_parameter(t *testing.T) {

// Arrange
source := []byte("package types\n" + "\n" +
"type Service interface {\n" +
" Method0(args ...any)" + "\n" +
"}")

accessor := reflection.AstFromSource(source)
file, _ := accessor()

sut := generator.NewTemplateModelBuilder(file.File)

// Act
actual, err := sut.Build()

// Assert
assert.NoError(t, err)
assert.NotNil(t, actual)

method := actual.Interfaces[0].Methods[0]
signature := generator.Signature(method)
assert.Equal(t, "Method0(args ...any)", signature)
}

func Test_NewTemplateModelBuilder_Build_interface_method_interface_array_parameter(t *testing.T) {

// Arrange
source := []byte("package types\n" + "\n" +
"type Service interface {\n" +
" Method0(args []interface{})" + "\n" +
"}")

accessor := reflection.AstFromSource(source)
file, _ := accessor()

sut := generator.NewTemplateModelBuilder(file.File)

// Act
actual, err := sut.Build()

// Assert
assert.NoError(t, err)
assert.NotNil(t, actual)

method := actual.Interfaces[0].Methods[0]
signature := generator.Signature(method)
assert.Equal(t, "Method0(args []interface{})", signature)
}
Loading
Loading