func/pkg/scaffolding/detectors.go

95 lines
2.4 KiB
Go

package scaffolding
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"path/filepath"
"strings"
)
// detector of method signatures. Each instance is for a given runtime.
type detector interface {
Detect(dir string) (static, instanced bool, err error)
}
// newDetector returns a deector instance for the given runtime.
func newDetector(runtime string) (detector, error) {
switch runtime {
case "go":
return &goDetector{}, nil
case "python":
return &pythonDetector{}, nil
case "rust":
return nil, ErrDetectorNotImplemented{runtime}
case "node":
return nil, ErrDetectorNotImplemented{runtime}
case "typescript":
return nil, ErrDetectorNotImplemented{runtime}
case "quarkus":
return nil, ErrDetectorNotImplemented{runtime}
case "java":
return nil, ErrDetectorNotImplemented{runtime}
default:
return nil, ErrRuntimeNotRecognized{runtime}
}
}
// GO
type goDetector struct{}
func (d goDetector) Detect(dir string) (static, instanced bool, err error) {
files, err := os.ReadDir(dir)
if err != nil {
return static, instanced, fmt.Errorf("signature detector encountered an error when scanning the function's source code. %w", err)
}
for _, file := range files {
filename := filepath.Join(dir, file.Name())
if file.IsDir() || !strings.HasSuffix(filename, ".go") {
continue
}
if d.hasFunctionDeclaration(filename, "New") {
instanced = true
}
if d.hasFunctionDeclaration(filename, "Handle") {
static = true
}
}
return
}
func (d goDetector) hasFunctionDeclaration(filename, function string) bool {
astFile, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.SkipObjectResolution)
if err != nil {
return false
}
for _, decl := range astFile.Decls {
if funcDecl, ok := decl.(*ast.FuncDecl); ok {
// Name matches and it has no reciver. I.e. a package level function
if funcDecl.Name.Name == function && funcDecl.Recv == nil {
return true
}
}
}
return false
}
// PYTHON
type pythonDetector struct{}
func (d pythonDetector) Detect(dir string) (static, instanced bool, err error) {
// The python middleware handles detecting if the Function has the static
// and/or instanced method signatures, so we can presume the suggested
// default of "instanced" here.
// Ideally we'd refactor this to support in-code the idea of "detection
// not necessary for this runtime".
instanced = true
return
}
// TODO: Other Detectors