Skip to content

Commit

Permalink
Expose internal parser in pkg (#37)
Browse files Browse the repository at this point in the history
* expose internal parser in pkg

Signed-off-by: Joffref <mariusjoffre@gmail.com>

* Resolve comments

Signed-off-by: Joffref <mariusjoffre@gmail.com>

---------

Signed-off-by: Joffref <mariusjoffre@gmail.com>
  • Loading branch information
Joffref authored Jan 16, 2024
1 parent b4bff35 commit 1d98301
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 37 deletions.
2 changes: 1 addition & 1 deletion cmd/genz/genz.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (c generateCommand) Run() error {
utils.LoadPackage(args, tags),
string(template),
*typeName,
parser.Parser,
parser.Parse,
)
if err != nil {
return err
Expand Down
6 changes: 3 additions & 3 deletions examples/4_mock/mock.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package {{ .PackageName }}
{{ range .PackageImports }}import "{{ . }}"{{ end }}

type {{.Type.InternalName}}Mock struct {
{{ range .Methods }}{{ .Name }}Func func({{ range $index, $element := .Params }} param{{$index}} {{ .Name }}{{ end }}) {{ range .Returns }}{{ .InternalName }}{{ end }}
{{ range .Methods }}{{ .Name }}Func func({{ range $name, $_ := .Params }}{{ $name }} {{ .Name }} {{ end }}) {{ range .Returns }}{{ .InternalName }}{{ end }}
{{ end }}
}

{{ range .Methods }}
func (m *{{ $.Type.InternalName }}Mock) {{ .Name }}({{ range $index, $element := .Params }}param{{$index}} {{ .Name }} {{ end }}) {{ range .Returns }}{{ .InternalName }}{{ end }} {
{{ if .Returns }}return {{ end }}m.{{ .Name }}Func({{ range $index, $element := .Params }}param{{$index}} {{ end }})
func (m *{{ $.Type.InternalName }}Mock) {{ .Name }}({{ range $name, $_ := .Params }}{{$name}} {{ .Name }} {{ end }}) {{ range .Returns }}{{ .InternalName }}{{ end }} {
{{ if .Returns }}return {{ end }}m.{{ .Name }}Func({{ range $name, $_ := .Params }}{{$name}} {{ end }})
}
{{ end }}
6 changes: 3 additions & 3 deletions examples/4_mock/test/expected.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package test

type HelloMock struct {
SayHelloToFunc func(param0 string) string
SayHelloToFunc func(name string) string
HelloFunc func() string
}

func (m *HelloMock) SayHelloTo(param0 string) string {
return m.SayHelloToFunc(param0)
func (m *HelloMock) SayHelloTo(name string) string {
return m.SayHelloToFunc(name)
}

func (m *HelloMock) Hello() string {
Expand Down
6 changes: 3 additions & 3 deletions examples/4_mock/test/hello_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ func TestHelloMock_SayHelloTo(t *testing.T) {
SayHelloToFunc func(param0 string) string
}
type args struct {
param0 string
name string
}
tests := []struct {
name string
Expand All @@ -23,7 +23,7 @@ func TestHelloMock_SayHelloTo(t *testing.T) {
},
},
args: args{
param0: "",
name: "",
},
want: "",
},
Expand All @@ -33,7 +33,7 @@ func TestHelloMock_SayHelloTo(t *testing.T) {
m := &HelloMock{
SayHelloToFunc: tt.fields.SayHelloToFunc,
}
if got := m.SayHelloTo(tt.args.param0); got != tt.want {
if got := m.SayHelloTo(tt.args.name); got != tt.want {
t.Errorf("SayHelloTo() = %v, want %v", got, tt.want)
}
})
Expand Down
46 changes: 34 additions & 12 deletions internal/parser/interface_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func TestParseInterfaceSuccess(t *testing.T) {
Methods: []models.Method{
{
Name: "Foo",
Params: []models.Type{},
Params: map[string]models.Type{},
Returns: []models.Type{},
IsPointerReceiver: false,
IsExported: true,
Expand All @@ -66,15 +66,15 @@ func TestParseInterfaceSuccess(t *testing.T) {
Methods: []models.Method{
{
Name: "Foo",
Params: []models.Type{},
Params: map[string]models.Type{},
Returns: []models.Type{},
IsPointerReceiver: false,
IsExported: true,
Comments: []string{},
},
{
Name: "Bar",
Params: []models.Type{},
Params: map[string]models.Type{},
Returns: []models.Type{},
IsPointerReceiver: false,
IsExported: true,
Expand All @@ -98,7 +98,7 @@ func TestParseInterfaceSuccess(t *testing.T) {
Methods: []models.Method{
{
Name: "Foo",
Params: []models.Type{},
Params: map[string]models.Type{},
Returns: []models.Type{},
IsPointerReceiver: false,
IsExported: true,
Expand All @@ -121,7 +121,7 @@ func TestParseInterfaceSuccess(t *testing.T) {
Methods: []models.Method{
{
Name: "Foo",
Params: []models.Type{{Name: "int", InternalName: "int"}, {Name: "string", InternalName: "string"}},
Params: map[string]models.Type{"a": {Name: "int", InternalName: "int"}, "b": {Name: "string", InternalName: "string"}},
Returns: []models.Type{},
IsPointerReceiver: false,
IsExported: true,
Expand All @@ -144,7 +144,7 @@ func TestParseInterfaceSuccess(t *testing.T) {
Methods: []models.Method{
{
Name: "Foo",
Params: []models.Type{},
Params: map[string]models.Type{},
Returns: []models.Type{{Name: "int", InternalName: "int"}, {Name: "string", InternalName: "string"}},
IsPointerReceiver: false,
IsExported: true,
Expand Down Expand Up @@ -173,15 +173,15 @@ func TestParseInterfaceSuccess(t *testing.T) {
Methods: []models.Method{
{
Name: "Foo",
Params: []models.Type{},
Params: map[string]models.Type{},
Returns: []models.Type{{Name: "int", InternalName: "int"}, {Name: "string", InternalName: "string"}},
IsPointerReceiver: false,
IsExported: true,
Comments: []string{" A is a sub interface"},
},
{
Name: "Bar",
Params: []models.Type{},
Params: map[string]models.Type{},
Returns: []models.Type{},
IsPointerReceiver: false,
IsExported: true,
Expand All @@ -203,7 +203,29 @@ func TestParseInterfaceSuccess(t *testing.T) {
Methods: []models.Method{
{
Name: "Foo",
Params: []models.Type{{Name: "int", InternalName: "int"}, {Name: "string", InternalName: "string"}},
Params: map[string]models.Type{"a": {Name: "int", InternalName: "int"}, "b": {Name: "string", InternalName: "string"}},
Returns: []models.Type{},
IsPointerReceiver: false,
IsExported: true,
Comments: []string{},
},
},
},
},
"interface with method with unnamed params": {
goCode: `
package main
type A interface {
Foo(int, string)
}`,
interfaceName: "A",
expectedInterface: models.Element{
Type: models.Type{Name: "main.A", InternalName: "A"},
Methods: []models.Method{
{
Name: "Foo",
Params: map[string]models.Type{"0": {Name: "int", InternalName: "int"}, "1": {Name: "string", InternalName: "string"}},
Returns: []models.Type{},
IsPointerReceiver: false,
IsExported: true,
Expand All @@ -227,7 +249,7 @@ func TestParseInterfaceSuccess(t *testing.T) {
Methods: []models.Method{
{
Name: "Foo",
Params: []models.Type{{Name: "uuid.UUID", InternalName: "UUID"}},
Params: map[string]models.Type{"a": {Name: "uuid.UUID", InternalName: "UUID"}},
Returns: []models.Type{},
IsPointerReceiver: false,
IsExported: true,
Expand All @@ -251,7 +273,7 @@ func TestParseInterfaceSuccess(t *testing.T) {
Methods: []models.Method{
{
Name: "Foo",
Params: []models.Type{},
Params: map[string]models.Type{},
Returns: []models.Type{},
IsPointerReceiver: false,
IsExported: true,
Expand All @@ -273,7 +295,7 @@ func TestParseInterfaceSuccess(t *testing.T) {
Methods: []models.Method{
{
Name: "foo",
Params: []models.Type{},
Params: map[string]models.Type{},
Returns: []models.Type{},
IsPointerReceiver: false,
IsExported: false,
Expand Down
11 changes: 8 additions & 3 deletions internal/parser/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"go/ast"
"go/doc"
"go/types"
"strconv"
"strings"

"github.com/Joffref/genz/pkg/models"
Expand All @@ -19,11 +20,15 @@ func parseMethodWithComments(doc *doc.Func, name string, signature *types.Signat
comments = strings.Split(strings.Trim(doc.Doc, "\n"), "\n")
}

params := []models.Type{}
params := map[string]models.Type{}
if signature.Params() != nil {
params = make([]models.Type, signature.Params().Len())
params = make(map[string]models.Type, signature.Params().Len())
for j := 0; j < signature.Params().Len(); j++ {
params[j] = parseType(signature.Params().At(j).Type())
if signature.Params().At(j).Name() == "" { // unnamed parameter
params[strconv.Itoa(j)] = parseType(signature.Params().At(j).Type())
continue
}
params[signature.Params().At(j).Name()] = parseType(signature.Params().At(j).Type())
}
}

Expand Down
4 changes: 2 additions & 2 deletions internal/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import (
"golang.org/x/tools/go/packages"
)

// Parser returns a models.ParsedElement from the given *packages.Package and type name.
// Parse returns a models.ParsedElement from the given *packages.Package and type name.
// It's the main entry point for the different parsers (struct, interface, etc).
func Parser(pkg *packages.Package, typeName string) (models.ParsedElement, error) {
func Parse(pkg *packages.Package, typeName string) (models.ParsedElement, error) {
parsedElement, err := parsePackage(pkg)
if err != nil {
return models.ParsedElement{}, err
Expand Down
14 changes: 7 additions & 7 deletions internal/parser/struct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func TestParseStructSuccess(t *testing.T) {
Name: "foo",
IsExported: false,
IsPointerReceiver: false,
Params: []models.Type{},
Params: map[string]models.Type{},
Returns: []models.Type{},
Comments: []string{},
},
Expand All @@ -267,7 +267,7 @@ func TestParseStructSuccess(t *testing.T) {
Name: "foo",
IsExported: false,
IsPointerReceiver: false,
Params: []models.Type{},
Params: map[string]models.Type{},
Returns: []models.Type{},
Comments: []string{"comment 1", "comment 2"},
},
Expand All @@ -291,7 +291,7 @@ func TestParseStructSuccess(t *testing.T) {
Name: "foo",
IsExported: false,
IsPointerReceiver: true,
Params: []models.Type{},
Params: map[string]models.Type{},
Returns: []models.Type{},
Comments: []string{},
},
Expand All @@ -317,7 +317,7 @@ func TestParseStructSuccess(t *testing.T) {
Name: "foo",
IsExported: false,
IsPointerReceiver: false,
Params: []models.Type{{Name: "string", InternalName: "string"}},
Params: map[string]models.Type{"a": {Name: "string", InternalName: "string"}},
Returns: []models.Type{{Name: "int", InternalName: "int"}},
Comments: []string{},
},
Expand All @@ -344,7 +344,7 @@ func TestParseStructSuccess(t *testing.T) {
Name: "foo",
IsExported: false,
IsPointerReceiver: false,
Params: []models.Type{{Name: "main.T", InternalName: "T"}},
Params: map[string]models.Type{"a": {Name: "main.T", InternalName: "T"}},
Returns: []models.Type{{Name: "main.T", InternalName: "T"}},
Comments: []string{},
},
Expand All @@ -371,7 +371,7 @@ func TestParseStructSuccess(t *testing.T) {
Name: "foo",
IsExported: false,
IsPointerReceiver: false,
Params: []models.Type{{Name: "map[main.T]main.T", InternalName: "map[T]T"}},
Params: map[string]models.Type{"a": {Name: "map[main.T]main.T", InternalName: "map[T]T"}},
Returns: []models.Type{{Name: "struct{name main.T}", InternalName: "struct{name T}"}},
Comments: []string{},
},
Expand All @@ -398,7 +398,7 @@ func TestParseStructSuccess(t *testing.T) {
Name: "Foo",
IsExported: true,
IsPointerReceiver: true,
Params: []models.Type{{Name: "string", InternalName: "string"}, {Name: "uint", InternalName: "uint"}},
Params: map[string]models.Type{"a": {Name: "string", InternalName: "string"}, "b": {Name: "uint", InternalName: "uint"}},
Returns: []models.Type{{Name: "int", InternalName: "int"}, {Name: "error", InternalName: "error"}},
Comments: []string{"comment"},
},
Expand Down
6 changes: 3 additions & 3 deletions pkg/models/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ type (
Method struct {
// Name of the method. e.g. "Foo" for "Foo() string"s
Name string
// List of the parameters of the method. Empty if the method has no parameter.
// Maps of the parameters of the method. Empty if the method has no parameter.
// The map key is the parameter name, the map value is the parameter type.
// See Type for more details.
Params []Type
Params map[string]Type
// List of the return values of the method. Empty if the method has no return value.
// See Type for more details.
Returns []Type
Expand All @@ -73,7 +74,6 @@ type (
// Type represents a generic type among structs, interfaces, attributes, methods, etc.
// It is used to represent various types such as "string", "time.Time", "uuid.UUID", etc.
Type struct {

// Name of the type from outside its package
// Example `uuid.UUID` or `time.Time`
// Use this variable if you generate code outside the package of that type
Expand Down
12 changes: 12 additions & 0 deletions pkg/parser/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package parser

import (
"github.com/Joffref/genz/internal/utils"
"golang.org/x/tools/go/packages"
)

// LoadPackage is a helper to load a package
// Note: patterns is a list of patterns to match packages (e.g . ; ./... ; github.com/Joffref/genz/...)
func LoadPackage(patterns []string, buildTags ...string) *packages.Package {
return utils.LoadPackage(patterns, buildTags)
}
13 changes: 13 additions & 0 deletions pkg/parser/parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package parser

import (
"github.com/Joffref/genz/internal/parser"
"github.com/Joffref/genz/pkg/models"
"golang.org/x/tools/go/packages"
)

// Parse returns a models.ParsedElement from the given *packages.Package and type name.
// It's the main entry point for the different underlying parsers (struct, interface, etc).
func Parse(pkg *packages.Package, typeName string) (models.ParsedElement, error) {
return parser.Parse(pkg, typeName)
}
19 changes: 19 additions & 0 deletions pkg/testutils/helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package testutils

import (
"github.com/Joffref/genz/internal/parser"
"github.com/Joffref/genz/internal/testutils"
"github.com/Joffref/genz/pkg/models"
"testing"
)

// ParseElement parses the given code looking for type and returns the parsed element
// If it fails, it fails the test
func ParseElement(t *testing.T, code string, typeName string) models.ParsedElement {
p := testutils.CreatePkgWithCode(t, code)
element, err := parser.Parse(p, typeName)
if err != nil {
t.Error(err)
}
return element
}

0 comments on commit 1d98301

Please sign in to comment.