Skip to content

Commit

Permalink
Merge pull request #89 from zcolleen/master
Browse files Browse the repository at this point in the history
Added ability to generate decorators from interface aliases
  • Loading branch information
hexdigest authored Jun 20, 2024
2 parents e5b68ed + 4a7b8ba commit 272d849
Show file tree
Hide file tree
Showing 2 changed files with 211 additions and 9 deletions.
87 changes: 78 additions & 9 deletions generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,23 +336,92 @@ func findTarget(input processInput) (output processOutput, err error) {

output.imports = imports
output.genericTypes = buildGenericTypesFromSpec(ts, types, input.astPackage.Name)
output.methods, err = findMethods(ts, targetProcessInput{
processInput: input,
types: types,
typesPrefix: input.astPackage.Name,
imports: output.imports,
genericTypes: output.genericTypes,
})
if err != nil {
return processOutput{}, err
}

return
}

if it, ok := ts.Type.(*ast.InterfaceType); ok {
output.methods, err = processInterface(it, targetProcessInput{
processInput: input,
types: types,
typesPrefix: input.astPackage.Name,
imports: output.imports,
genericTypes: output.genericTypes,
})
func findMethods(selectedType *ast.TypeSpec, input targetProcessInput) (methods methodsList, err error) {
switch t := selectedType.Type.(type) {
case *ast.InterfaceType:
methods, err = processInterface(t, input)
if err != nil {
return processOutput{}, err
return methodsList{}, err
}
case *ast.SelectorExpr:
ident, ok := t.X.(*ast.Ident)
if !ok {
return
}
srcPackagePath := findSourcePackage(ident, input.imports)

return getMethods(t, srcPackagePath)
case *ast.Ident:
if t.Obj == nil {
return
}
if ts, ok := t.Obj.Decl.(*ast.TypeSpec); ok {
return findMethods(ts, input)
}
}

return
}

func getMethods(sel *ast.SelectorExpr, srcPackagePath string) (methods methodsList, err error) {
srcPkg, err := pkg.Load(srcPackagePath)
if err != nil {
return nil, errors.Wrapf(err, "cant load %s package", srcPackagePath)
}

fs := token.NewFileSet()
srcAst, err := pkg.AST(fs, srcPkg)
if err != nil {
return nil, errors.Wrapf(err, "cant ast %s package", srcPackagePath)
}

out, err := findTarget(processInput{
fileSet: fs,
currentPackage: srcPkg,
astPackage: srcAst,
targetName: sel.Sel.Name,
})
if err != nil {
return nil, errors.Wrapf(err, "failed to find target in %s package", srcPackagePath)
}

return out.methods, nil
}

func findSourcePackage(ident *ast.Ident, imports []*ast.ImportSpec) string {
for _, imp := range imports {
cleanPath := strings.Trim(imp.Path.Value, "\"")
if imp.Name != nil {
if ident.Name == imp.Name.Name {
return cleanPath
}

continue
}

slash := strings.LastIndex(cleanPath, "/")
if ident.Name == cleanPath[slash+1:] {
return cleanPath
}
}

return ""
}

func iterateFiles(p *ast.Package, name string) (selectedType *ast.TypeSpec, imports []*ast.ImportSpec, types []*ast.TypeSpec) {
for _, f := range p.Files {
if f != nil {
Expand Down
133 changes: 133 additions & 0 deletions generator/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,139 @@ func Test_findTarget(t *testing.T) {
},
wantErr: false,
},
{
name: "found interface alias",
args: args{
input: processInput{
astPackage: &ast.Package{
Files: map[string]*ast.File{
"file.go": {
Decls: []ast.Decl{&ast.GenDecl{Tok: token.TYPE, Specs: []ast.Spec{&ast.TypeSpec{
Name: &ast.Ident{Name: "InterfaceAlias"},
Type: &ast.Ident{
Name: "Interface",
Obj: &ast.Object{
Decl: &ast.TypeSpec{
Type: &ast.InterfaceType{},
},
},
},
}}}},
}},
},
targetName: "InterfaceAlias",
},
},
wantErr: false,
},
{
name: "found interface alias on exported type",
args: args{
input: processInput{
astPackage: &ast.Package{
Files: map[string]*ast.File{
"file.go": {
Decls: []ast.Decl{&ast.GenDecl{Tok: token.TYPE, Specs: []ast.Spec{&ast.TypeSpec{
Name: &ast.Ident{Name: "InterfaceAlias"},
Type: &ast.SelectorExpr{
X: &ast.Ident{
Name: "io",
},
Sel: &ast.Ident{
Name: "Reader",
},
},
}}}},
Imports: []*ast.ImportSpec{{
Path: &ast.BasicLit{
Value: "io",
},
}},
}},
},
targetName: "InterfaceAlias",
},
},
want1: methodsList{
"Read": Method{
Name: "Read",
Params: ParamsSlice{
{
Name: "p",
Type: "[]byte",
},
},
Results: ParamsSlice{
{
Name: "n",
Type: "int",
},
{
Name: "err",
Type: "error",
},
},
ReturnsError: true,
},
},
wantErr: false,
},
{
name: "found interface alias on exported type with named package",
args: args{
input: processInput{
astPackage: &ast.Package{
Files: map[string]*ast.File{
"file.go": {
Decls: []ast.Decl{&ast.GenDecl{Tok: token.TYPE, Specs: []ast.Spec{&ast.TypeSpec{
Name: &ast.Ident{Name: "InterfaceAlias"},
Type: &ast.SelectorExpr{
X: &ast.Ident{
Name: "io_name",
},
Sel: &ast.Ident{
Name: "Reader",
},
},
}}}},
Imports: []*ast.ImportSpec{
{
Name: &ast.Ident{
Name: "io_name",
},
Path: &ast.BasicLit{
Value: "io",
},
}},
}},
},
targetName: "InterfaceAlias",
},
},
want1: methodsList{
"Read": Method{
Name: "Read",
Params: ParamsSlice{
{
Name: "p",
Type: "[]byte",
},
},
Results: ParamsSlice{
{
Name: "n",
Type: "int",
},
{
Name: "err",
Type: "error",
},
},
ReturnsError: true,
},
},
wantErr: false,
},
}

for _, tt := range tests {
Expand Down

0 comments on commit 272d849

Please sign in to comment.