-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WIP: Parsing interface Signed-off-by: Joffref <[email protected]> * Resolve comments and factorize code Signed-off-by: Joffref <[email protected]> * Resolve comments and improve documentation Signed-off-by: Joffref <[email protected]> * Resolve comments and add more tests Signed-off-by: Joffref <[email protected]> --------- Signed-off-by: Joffref <[email protected]>
- Loading branch information
Showing
20 changed files
with
977 additions
and
317 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
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 }} | ||
{{ 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 }}) | ||
} | ||
{{ end }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package test | ||
|
||
type HelloMock struct { | ||
SayHelloToFunc func(param0 string) string | ||
HelloFunc func() string | ||
} | ||
|
||
func (m *HelloMock) SayHelloTo(param0 string) string { | ||
return m.SayHelloToFunc(param0) | ||
} | ||
|
||
func (m *HelloMock) Hello() string { | ||
return m.HelloFunc() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package test | ||
|
||
//go:generate genz -template ../mock.tmpl -output hello.gen.go -type Hello | ||
type Hello interface { | ||
SayHelloTo(name string) string | ||
Hello() string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package test | ||
|
||
import "testing" | ||
|
||
func TestHelloMock_SayHelloTo(t *testing.T) { | ||
type fields struct { | ||
SayHelloToFunc func(param0 string) string | ||
} | ||
type args struct { | ||
param0 string | ||
} | ||
tests := []struct { | ||
name string | ||
fields fields | ||
args args | ||
want string | ||
}{ | ||
{ | ||
name: "dummy_test", | ||
fields: fields{ | ||
SayHelloToFunc: func(param0 string) string { | ||
return "" | ||
}, | ||
}, | ||
args: args{ | ||
param0: "", | ||
}, | ||
want: "", | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
m := &HelloMock{ | ||
SayHelloToFunc: tt.fields.SayHelloToFunc, | ||
} | ||
if got := m.SayHelloTo(tt.args.param0); got != tt.want { | ||
t.Errorf("SayHelloTo() = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package parser | ||
|
||
import ( | ||
"fmt" | ||
"github.com/Joffref/genz/pkg/models" | ||
"go/ast" | ||
"go/types" | ||
"golang.org/x/tools/go/packages" | ||
) | ||
|
||
// parseElementType initializes a models.Element with the given name and package. | ||
// It does not parse the element's methods or attributes. | ||
// It should be called first by every parse* function. | ||
func parseElementType(pkg *packages.Package, name string) (models.Element, error) { | ||
if pkg.Types == nil { | ||
return models.Element{}, fmt.Errorf("package %s has no types", pkg.Name) | ||
} | ||
return models.Element{ | ||
Type: models.Type{ | ||
Name: fmt.Sprintf("%s.%s", pkg.Name, name), | ||
InternalName: name, | ||
}, | ||
}, nil | ||
} | ||
|
||
// objectAsNamedType returns the given object as a *types.Named. | ||
// It returns an error if the object is not a *types.TypeName or if the type of the *types.TypeName is not a *types.Named. | ||
func objectAsNamedType(object types.Object) (*types.Named, error) { | ||
typeName, isTypeName := object.(*types.TypeName) | ||
if !isTypeName { | ||
return nil, fmt.Errorf("%s is not a TypeName", object.Name()) | ||
} | ||
namedType, isNamedType := typeName.Type().(*types.Named) | ||
if !isNamedType { | ||
return nil, fmt.Errorf("%s is not a named type", object.Name()) | ||
} | ||
|
||
return namedType, nil | ||
} | ||
|
||
// parseType returns a models.Type from the given types.Type. | ||
// It returns the type name with the package qualifier and without the package qualifier. | ||
func parseType(t types.Type) models.Type { | ||
// Remove every qualifier before the type name | ||
// transforming "github.com/google/uuid.UUID" into "UUID" | ||
noPackageQualifier := func(_ *types.Package) string { return "" } | ||
|
||
// Adds the package name qualifier before the type name | ||
// transforming "github.com/google/uuid.UUID" into "uuid.UUID" | ||
packageNameQualifier := func(pkg *types.Package) string { | ||
return pkg.Name() | ||
} | ||
|
||
return models.Type{ | ||
Name: types.TypeString(t, packageNameQualifier), // (e.g. "uuid.UUID") | ||
InternalName: types.TypeString(t, noPackageQualifier), // (e.g. "UUID") | ||
} | ||
|
||
} | ||
|
||
// loadAstExpr returns the ast.Expr of the given typeName in the given package. | ||
// It returns an error if the typeName is not found in the package. | ||
// Be aware that ast.Expr is an interface, so the returned value can be of any type. | ||
func loadAstExpr(pkg *packages.Package, typeName string) (ast.Expr, error) { | ||
for ident := range pkg.TypesInfo.Defs { | ||
if ident.Name == typeName { | ||
if ident.Obj == nil { // could be a name overlapping with a type. | ||
continue | ||
} | ||
switch ident.Obj.Decl.(type) { | ||
case *ast.TypeSpec: | ||
return ident.Obj.Decl.(*ast.TypeSpec).Type, nil | ||
default: // could be a name overlapping with a type. | ||
continue | ||
} | ||
} | ||
} | ||
return nil, fmt.Errorf("%s not found in package %s", typeName, pkg.Name) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package parser | ||
|
||
import ( | ||
"github.com/Joffref/genz/pkg/models" | ||
"go/ast" | ||
"go/types" | ||
"golang.org/x/tools/go/packages" | ||
) | ||
|
||
// parseInterface parses the given interface and returns a models.Element. | ||
// It also parses the subInterfaces of the given interface and the methods of the subInterfaces recursively. | ||
func parseInterface(pkg *packages.Package, interfaceName string, interfaceType *ast.InterfaceType) (models.Element, error) { | ||
|
||
parsedInterface, err := parseElementType(pkg, interfaceName) | ||
if err != nil { | ||
return models.Element{}, err | ||
} | ||
|
||
var methods []models.Method | ||
|
||
for _, method := range interfaceType.Methods.List { | ||
switch pkg.TypesInfo.TypeOf(method.Type).(type) { | ||
case *types.Signature: | ||
methodModel, err := parseMethod(method.Names[0].Name, pkg.TypesInfo.TypeOf(method.Type).(*types.Signature)) | ||
if err != nil { | ||
return models.Element{}, err | ||
} | ||
if method.Doc != nil { | ||
for _, comment := range method.Doc.List { | ||
methodModel.Comments = append(methodModel.Comments, comment.Text[2:]) | ||
} | ||
} | ||
methods = append(methods, methodModel) | ||
case *types.Named: // Embedded interface | ||
namedType := pkg.TypesInfo.TypeOf(method.Type).(*types.Named) | ||
iface := namedType.Origin().Underlying().(*types.Interface).Complete() | ||
for i := 0; i < iface.NumMethods(); i++ { | ||
methodModel, err := parseMethod(iface.Method(i).Name(), iface.Method(i).Type().(*types.Signature)) | ||
if err != nil { | ||
return models.Element{}, err | ||
} | ||
if method.Doc != nil { | ||
for _, comment := range method.Doc.List { | ||
methodModel.Comments = append(methodModel.Comments, comment.Text[2:]) | ||
} | ||
} | ||
methods = append(methods, methodModel) | ||
} | ||
} | ||
|
||
} | ||
parsedInterface.Methods = methods | ||
return parsedInterface, nil | ||
} |
Oops, something went wrong.