Skip to content

Commit

Permalink
fix/array-parameter-handling-in-generator-commands (#22)
Browse files Browse the repository at this point in the history
* Removes direct file access from the NewTemplateModelBuilder method; requires an AstFileAccessor function instead. Adds AstFromFile and AstFromSource accessor functions.
* Fixes handling of array types
* Updates the init command to add a reference to the latest version of Parsley
  • Loading branch information
matzefriedrich authored Sep 9, 2024
1 parent 8533007 commit ca31b47
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 11 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v0.9.1] - 2024-09-09

### Changed

* Added improved testability for the `TemplateModelBuilder` by refactoring its constructor function to accept an `AstFileAccessor` function instead of a filename. This allows for greater flexibility in testing, as the AST can now be sourced either from a file or directly from a string, making it easier to test different code inputs without relying on file I/O.

### Fixed

* Fixed an issue in the `type_model_builder.go` module where parameters and result fields of type array were not correctly handled. This update ensures that array types are properly represented in the generated template models, allowing for accurate code generation in cases involving arrays.


## [v0.9.0] - 2024-09-08

Starting with this release, the project's license has been changed from AGPLv3 to Apache License 2.0. The move to the Apache 2.0 license reflects my desire to make the library more accessible and easier to adopt, especially in commercial and proprietary projects.
Expand Down
5 changes: 3 additions & 2 deletions internal/commands/init_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package commands

import (
"fmt"
"os"

"github.com/matzefriedrich/cobra-extensions/pkg"
"github.com/matzefriedrich/cobra-extensions/pkg/abstractions"
"github.com/matzefriedrich/parsley/internal/generator"
"github.com/spf13/cobra"
"os"
)

type initCommand struct {
Expand All @@ -22,7 +23,7 @@ func (g *initCommand) Execute() {
return
}

const minVersion = "v0.8.1"
const minVersion = "v0.9.1"
const packageName = "github.com/matzefriedrich/parsley"
dependencyErr := p.AddDependency(packageName, minVersion)
if dependencyErr != nil {
Expand Down
25 changes: 25 additions & 0 deletions internal/generator/ast_file_accessor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package generator

import (
"go/ast"
"go/parser"
"go/token"
)

type AstFileAccessor func() (*ast.File, error)

// AstFromFile Creates an AstFileAccessor object for the given Golang source file.
func AstFromFile(sourceFilePath string) AstFileAccessor {
return func() (*ast.File, error) {
fileSet := token.NewFileSet()
return parser.ParseFile(fileSet, sourceFilePath, nil, parser.ParseComments)
}
}

// AstFromSource Creates an AstFileAccessor object for the given source code.
func AstFromSource(code []byte) AstFileAccessor {
return func() (*ast.File, error) {
fileSet := token.NewFileSet()
return parser.ParseFile(fileSet, "", code, parser.ParseComments)
}
}
2 changes: 1 addition & 1 deletion internal/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (g *codeFileGenerator) GenerateCode() error {
return err
}

builder, err := NewTemplateModelBuilder(goFilePath)
builder, err := NewTemplateModelBuilder(AstFromFile(goFilePath))
if err != nil {
return err
}
Expand Down
18 changes: 12 additions & 6 deletions internal/generator/type_model_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,17 @@ package generator
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
)

type TemplateModelBuilder struct {
node *ast.File
}

func NewTemplateModelBuilder(sourceFilePath string) (*TemplateModelBuilder, error) {
fileSet := token.NewFileSet()
node, err := parser.ParseFile(fileSet, sourceFilePath, nil, parser.ParseComments)
func NewTemplateModelBuilder(accessor AstFileAccessor) (*TemplateModelBuilder, error) {
node, err := accessor()
if err != nil {
return nil, err
}

return &TemplateModelBuilder{
node: node,
}, nil
Expand Down Expand Up @@ -72,11 +68,16 @@ func (b *TemplateModelBuilder) collectParametersFor(funcType *ast.FuncType) []Pa
parameters := make([]Parameter, 0)
for _, param := range funcType.Params.List {
paramType := param.Type
paramArrayType, isArrayType := paramType.(*ast.ArrayType)
if isArrayType {
paramType = paramArrayType.Elt
}
paramTypeIdentifier, _ := paramType.(*ast.Ident)
for _, paramName := range param.Names {
parameters = append(parameters, Parameter{
Name: paramName.Name,
TypeName: paramTypeIdentifier.Name,
IsArray: isArrayType,
})
}
}
Expand All @@ -90,11 +91,16 @@ func (b *TemplateModelBuilder) collectResultFieldsFor(funcType *ast.FuncType) []
}
for index, field := range funcType.Results.List {
fieldType := field.Type
fieldArrayType, isArrayType := fieldType.(*ast.ArrayType)
if isArrayType {
fieldType = fieldArrayType.Elt
}
fieldTypeIdentifier, ok := fieldType.(*ast.Ident)
if ok {
parameters = append(parameters, Parameter{
Name: fmt.Sprintf("result%d", index),
TypeName: fieldTypeIdentifier.Name,
IsArray: isArrayType,
})
}
}
Expand Down
12 changes: 10 additions & 2 deletions internal/generator/type_model_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ func HasResults(m Method) bool {
func FormattedParameters(m Method) string {
formattedParameters := make([]string, len(m.Parameters))
for i, parameter := range m.Parameters {
formattedParameters[i] = fmt.Sprintf("%s %s", parameter.Name, parameter.TypeName)
typeName := parameter.TypeName
if parameter.IsArray {
typeName = "[]" + typeName
}
formattedParameters[i] = fmt.Sprintf("%s %s", parameter.Name, typeName)
}
return strings.Join(formattedParameters, ", ")
}
Expand All @@ -46,7 +50,11 @@ func FormattedResultParameters(m Method) string {
func FormattedResultTypes(m Method) string {
formattedResults := make([]string, len(m.Results))
for i, result := range m.Results {
formattedResults[i] = fmt.Sprintf("%s", result.TypeName)
typeName := result.TypeName
if result.IsArray {
typeName = "[]" + typeName
}
formattedResults[i] = fmt.Sprintf("%s", typeName)
}
if len(formattedResults) == 0 {
return ""
Expand Down
1 change: 1 addition & 0 deletions internal/generator/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
type Parameter struct {
Name string
TypeName string
IsArray bool
}

func (p Parameter) MatchesType(name string) bool {
Expand Down
31 changes: 31 additions & 0 deletions internal/tests/generator/ast_file_accessor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package generator

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

func Test_AstFileAccessor_create_accessor_from_source(t *testing.T) {

// Arrange
source := []byte("package types\n" +
"\n" +
"type Service0 interface {\n" +
" Method0()\n" +
"}\n" +
"\n" +
"type Service1 interface {\n" +
" Method1() string\n" +
" Method3() (string, error)\n" +
"}\n")

accessor := generator.AstFromSource(source)

// Act
actual, err := accessor()

// Assert
assert.NoError(t, err)
assert.NotNil(t, actual)
}
64 changes: 64 additions & 0 deletions internal/tests/generator/type_model_builder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package generator

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

func Test_NewTemplateModelBuilder_from_empty_source_file_returns_error(t *testing.T) {

// Arrange
source := []byte("")

// Act
sut, err := generator.NewTemplateModelBuilder(generator.AstFromSource(source))

// Assert
assert.Error(t, err)
assert.Nil(t, sut)
}

func Test_NewTemplateModelBuilder_from_minimal_source_file(t *testing.T) {

// Arrange
source := []byte("package main")

// Act
sut, err := generator.NewTemplateModelBuilder(generator.AstFromSource(source))

// Assert
assert.NoError(t, err)
assert.NotNil(t, sut)
}

func Test_NewTemplateModelBuilder_Build_multiple_interface_definitions(t *testing.T) {

// Arrange
source := []byte("package types\n" + "\n" +
"type Service0 interface {\n" + " Method0(s string)\n" + "}\n" + "\n" +
"type Service1 interface {\n" + " Method1() string\n" + " Method2(data []bytes) (string, error)\n" +
"}\n")

accessor := generator.AstFromSource(source)
sut, _ := generator.NewTemplateModelBuilder(accessor)

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

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

serviceInterface0 := actual.Interfaces[0]
assert.Equal(t, "Service0", serviceInterface0.Name)

serviceInterface1 := actual.Interfaces[1]
assert.Equal(t, "Service1", serviceInterface1.Name)

method1 := serviceInterface1.Methods[0]
assert.Equal(t, "Method1", method1.Name)

method2 := serviceInterface1.Methods[1]
assert.Equal(t, "Method2", method2.Name)
}

0 comments on commit ca31b47

Please sign in to comment.