123 lines
3.2 KiB
Go
123 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/token"
|
|
)
|
|
|
|
// Interface analysis types
|
|
type InterfaceInfo struct {
|
|
Name string `json:"name"`
|
|
Package string `json:"package"`
|
|
Position Position `json:"position"`
|
|
Methods []MethodInfo `json:"methods"`
|
|
Implementations []ImplementationType `json:"implementations,omitempty"`
|
|
}
|
|
|
|
type ImplementationType struct {
|
|
Type string `json:"type"`
|
|
Package string `json:"package"`
|
|
Position Position `json:"position"`
|
|
}
|
|
|
|
func extractInterfaces(dir string, interfaceName string) ([]InterfaceInfo, error) {
|
|
var interfaces []InterfaceInfo
|
|
interfaceMap := make(map[string]*InterfaceInfo)
|
|
|
|
// First pass: collect all interfaces
|
|
err := walkGoFiles(dir, func(path string, src []byte, file *ast.File, fset *token.FileSet) error {
|
|
ast.Inspect(file, func(n ast.Node) bool {
|
|
if genDecl, ok := n.(*ast.GenDecl); ok {
|
|
for _, spec := range genDecl.Specs {
|
|
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
|
if iface, ok := typeSpec.Type.(*ast.InterfaceType); ok {
|
|
name := typeSpec.Name.Name
|
|
if interfaceName == "" || name == interfaceName {
|
|
pos := fset.Position(typeSpec.Pos())
|
|
info := &InterfaceInfo{
|
|
Name: name,
|
|
Package: file.Name.Name,
|
|
Position: newPosition(pos),
|
|
Methods: extractInterfaceMethods(iface, fset),
|
|
}
|
|
interfaceMap[name] = info
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Second pass: find implementations
|
|
if interfaceName != "" {
|
|
iface, exists := interfaceMap[interfaceName]
|
|
if exists {
|
|
err = walkGoFiles(dir, func(path string, src []byte, file *ast.File, fset *token.FileSet) error {
|
|
// Collect all types with methods
|
|
types := make(map[string][]string)
|
|
|
|
for _, decl := range file.Decls {
|
|
if fn, ok := decl.(*ast.FuncDecl); ok && fn.Recv != nil {
|
|
for _, recv := range fn.Recv.List {
|
|
typeName := getTypeName(recv.Type)
|
|
types[typeName] = append(types[typeName], fn.Name.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if any type implements the interface
|
|
for typeName, methods := range types {
|
|
if implementsInterface(methods, iface.Methods) {
|
|
// Find type declaration
|
|
ast.Inspect(file, func(n ast.Node) bool {
|
|
if genDecl, ok := n.(*ast.GenDecl); ok {
|
|
for _, spec := range genDecl.Specs {
|
|
if typeSpec, ok := spec.(*ast.TypeSpec); ok && typeSpec.Name.Name == typeName {
|
|
pos := fset.Position(typeSpec.Pos())
|
|
iface.Implementations = append(iface.Implementations, ImplementationType{
|
|
Type: typeName,
|
|
Package: file.Name.Name,
|
|
Position: newPosition(pos),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
}
|
|
|
|
// Convert map to slice
|
|
for _, iface := range interfaceMap {
|
|
interfaces = append(interfaces, *iface)
|
|
}
|
|
|
|
return interfaces, err
|
|
}
|
|
|
|
func implementsInterface(methods []string, interfaceMethods []MethodInfo) bool {
|
|
for _, im := range interfaceMethods {
|
|
found := false
|
|
for _, m := range methods {
|
|
if m == im.Name {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
} |