mirror of
https://github.com/wailsapp/wails.git
synced 2026-03-14 14:45:49 +01:00
Parse Models
Support recursive fields Add loads of tests
This commit is contained in:
parent
95bb15eef4
commit
d0769ecd1c
5 changed files with 447 additions and 851 deletions
|
|
@ -7,12 +7,46 @@ This package contains the static analyser used for parsing Wails projects so tha
|
|||
|
||||
## Implemented
|
||||
|
||||
- [x] Parsing of structs
|
||||
- [x] Generation of models
|
||||
- [x] Scalars
|
||||
- [x] Arrays
|
||||
- [ ] Parsing of bound methods
|
||||
- [x] Method names
|
||||
- [ ] Method parameters
|
||||
- [x] Zero parameters
|
||||
- [x] Single input parameter
|
||||
- [x] Single output parameter
|
||||
- [x] Multiple input parameters
|
||||
- [x] Multiple output parameters
|
||||
- [x] Named output parameters
|
||||
- [x] int
|
||||
- [x] Pointer
|
||||
- [x] uint
|
||||
- [ ] Pointer
|
||||
- [x] float
|
||||
- [ ] Pointer
|
||||
- [x] string
|
||||
- [ ] Pointer
|
||||
- [x] bool
|
||||
- [ ] Pointer
|
||||
- [ ] Struct
|
||||
- [x] Pointer
|
||||
- [x] Slices
|
||||
- [ ] Pointer
|
||||
- [x] Recursive
|
||||
- [x] Maps
|
||||
- [ ] Pointer
|
||||
- [ ] Model Parsing
|
||||
- [x] In same package
|
||||
- [ ] In different package
|
||||
- [ ] Multiple named fields, e.g. one,two,three string
|
||||
- [ ] Scalars
|
||||
- [ ] Arrays
|
||||
- [ ] Maps
|
||||
- [x] Structs
|
||||
- [ ] Structs
|
||||
- [ ] Anonymous structs
|
||||
- [ ] Generation of models
|
||||
- [ ] Scalars
|
||||
- [ ] Arrays
|
||||
- [ ] Maps
|
||||
- [ ] Structs
|
||||
- [ ] Generation of bindings
|
||||
|
||||
## Limitations
|
||||
|
|
|
|||
|
|
@ -6,19 +6,33 @@ import (
|
|||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
var packageCache = make(map[string]*ParsedPackage)
|
||||
var structCache = make(map[string]map[structName]*StructDef)
|
||||
|
||||
type packageName = string
|
||||
type structName = string
|
||||
|
||||
type Parameter struct {
|
||||
type StructDef struct {
|
||||
Name string
|
||||
DocComment string
|
||||
Fields []*Field
|
||||
}
|
||||
|
||||
type ParameterType struct {
|
||||
Name string
|
||||
Type string
|
||||
IsStruct bool
|
||||
IsSlice bool
|
||||
IsPointer bool
|
||||
MapKey *ParameterType
|
||||
MapValue *ParameterType
|
||||
}
|
||||
|
||||
type Parameter struct {
|
||||
Name string
|
||||
Type *ParameterType
|
||||
}
|
||||
|
||||
type BoundMethod struct {
|
||||
|
|
@ -28,32 +42,25 @@ type BoundMethod struct {
|
|||
Outputs []*Parameter
|
||||
}
|
||||
|
||||
type Model struct {
|
||||
Name string
|
||||
Fields []*Field
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Name string
|
||||
Type string
|
||||
IsStruct bool
|
||||
IsSlice bool
|
||||
Name string
|
||||
Type *ParameterType
|
||||
}
|
||||
|
||||
type ParsedPackage struct {
|
||||
Pkg *ast.Package
|
||||
Pkg *ast.Package
|
||||
Name string
|
||||
Dir string
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
Path string
|
||||
BoundMethods map[packageName]map[structName][]*BoundMethod
|
||||
Models map[packageName]map[structName]*Model
|
||||
}
|
||||
|
||||
func ParseProject(projectPath string) (*Project, error) {
|
||||
result := &Project{
|
||||
BoundMethods: make(map[packageName]map[structName][]*BoundMethod),
|
||||
Models: make(map[packageName]map[structName]*Model),
|
||||
}
|
||||
pkgs, err := result.parseDirectory(projectPath)
|
||||
if err != nil {
|
||||
|
|
@ -88,9 +95,13 @@ func (p *Project) parseDirectory(dir string) (map[string]*ParsedPackage, error)
|
|||
}
|
||||
var result = make(map[string]*ParsedPackage)
|
||||
for packageName, pkg := range pkgs {
|
||||
parsedPackage := &ParsedPackage{Pkg: pkg}
|
||||
parsedPackage := &ParsedPackage{
|
||||
Pkg: pkg,
|
||||
Dir: getDirectoryForPackage(pkg),
|
||||
}
|
||||
packageCache[dir] = parsedPackage
|
||||
result[packageName] = parsedPackage
|
||||
structCache[packageName] = make(map[structName]*StructDef)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
|
@ -287,8 +298,12 @@ func (p *Project) parseBoundStructMethods(name string, pkg *ast.Package) error {
|
|||
Outputs: make([]*Parameter, 0),
|
||||
}
|
||||
|
||||
method.Inputs = p.parseParameters(funcDecl.Type.Params)
|
||||
method.Outputs = p.parseParameters(funcDecl.Type.Results)
|
||||
if funcDecl.Type.Params != nil {
|
||||
method.Inputs = p.parseParameters(funcDecl.Type.Params, pkg)
|
||||
}
|
||||
if funcDecl.Type.Results != nil {
|
||||
method.Outputs = p.parseParameters(funcDecl.Type.Results, pkg)
|
||||
}
|
||||
|
||||
methods = append(methods, method)
|
||||
}
|
||||
|
|
@ -300,7 +315,7 @@ func (p *Project) parseBoundStructMethods(name string, pkg *ast.Package) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Project) parseParameters(params *ast.FieldList) []*Parameter {
|
||||
func (p *Project) parseParameters(params *ast.FieldList, pkg *ast.Package) []*Parameter {
|
||||
var result []*Parameter
|
||||
for _, field := range params.List {
|
||||
var theseFields []*Parameter
|
||||
|
|
@ -317,20 +332,99 @@ func (p *Project) parseParameters(params *ast.FieldList) []*Parameter {
|
|||
}
|
||||
// loop over fields
|
||||
for _, thisField := range theseFields {
|
||||
thisField.Type = getTypeString(field.Type)
|
||||
switch t := field.Type.(type) {
|
||||
case *ast.StarExpr:
|
||||
thisField.IsStruct = isStructType(t.X)
|
||||
thisField.IsPointer = true
|
||||
case *ast.StructType:
|
||||
thisField.IsStruct = true
|
||||
case *ast.ArrayType:
|
||||
thisField.IsSlice = true
|
||||
thisField.IsStruct = isStructType(t.Elt)
|
||||
case *ast.MapType:
|
||||
thisField.IsSlice = true
|
||||
thisField.IsStruct = isStructType(t.Value)
|
||||
thisField.Type = p.parseParameterType(field, pkg)
|
||||
result = append(result, thisField)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (p *Project) parseParameterType(field *ast.Field, pkg *ast.Package) *ParameterType {
|
||||
var result ParameterType
|
||||
result.Name = getTypeString(field.Type)
|
||||
switch t := field.Type.(type) {
|
||||
case *ast.StarExpr:
|
||||
result.IsStruct = isStructType(t.X)
|
||||
result.IsPointer = true
|
||||
case *ast.StructType:
|
||||
result.IsStruct = true
|
||||
case *ast.ArrayType:
|
||||
result.IsSlice = true
|
||||
result.IsStruct = isStructType(t.Elt)
|
||||
case *ast.MapType:
|
||||
tempfield := &ast.Field{Type: t.Key}
|
||||
result.MapKey = p.parseParameterType(tempfield, pkg)
|
||||
tempfield.Type = t.Value
|
||||
result.MapValue = p.parseParameterType(tempfield, pkg)
|
||||
default:
|
||||
}
|
||||
if result.IsStruct {
|
||||
_, ok := structCache[pkg.Name][result.Name]
|
||||
if !ok {
|
||||
p.getStructDef(result.Name, pkg)
|
||||
}
|
||||
}
|
||||
return &result
|
||||
}
|
||||
|
||||
func (p *Project) getStructDef(name string, pkg *ast.Package) {
|
||||
_, ok := structCache[pkg.Name][name]
|
||||
if ok {
|
||||
return
|
||||
}
|
||||
// Iterate over all files in the package
|
||||
for _, file := range pkg.Files {
|
||||
// Iterate over all declarations in the file
|
||||
for _, decl := range file.Decls {
|
||||
// Check if the declaration is a type declaration
|
||||
if typeDecl, ok := decl.(*ast.GenDecl); ok {
|
||||
// Check if the type declaration is a struct type
|
||||
if typeDecl.Tok == token.TYPE {
|
||||
for _, spec := range typeDecl.Specs {
|
||||
if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
||||
if structType, ok := typeSpec.Type.(*ast.StructType); ok {
|
||||
if typeSpec.Name.Name == name {
|
||||
result := &StructDef{
|
||||
Name: name,
|
||||
DocComment: typeDecl.Doc.Text(),
|
||||
}
|
||||
structCache[pkg.Name][name] = result
|
||||
result.Fields = p.parseStructFields(structType, pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Project) parseStructFields(structType *ast.StructType, pkg *ast.Package) []*Field {
|
||||
var result []*Field
|
||||
for _, field := range structType.Fields.List {
|
||||
var theseFields []*Field
|
||||
if len(field.Names) > 0 {
|
||||
for _, name := range field.Names {
|
||||
theseFields = append(theseFields, &Field{
|
||||
Name: name.Name,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
theseFields = append(theseFields, &Field{
|
||||
Name: "",
|
||||
})
|
||||
}
|
||||
// loop over fields
|
||||
for _, thisField := range theseFields {
|
||||
paramType := p.parseParameterType(field, pkg)
|
||||
if paramType.IsStruct {
|
||||
_, ok := structCache[pkg.Name][paramType.Name]
|
||||
if !ok {
|
||||
p.getStructDef(paramType.Name, pkg)
|
||||
}
|
||||
}
|
||||
thisField.Type = paramType
|
||||
result = append(result, thisField)
|
||||
}
|
||||
}
|
||||
|
|
@ -345,8 +439,8 @@ func getTypeString(expr ast.Expr) string {
|
|||
return getTypeString(t.X)
|
||||
case *ast.ArrayType:
|
||||
return getTypeString(t.Elt)
|
||||
//case *ast.MapType:
|
||||
// return "map[" + getTypeString(t.Key) + "]" + getTypeString(t.Value)
|
||||
case *ast.MapType:
|
||||
return "map"
|
||||
default:
|
||||
return "any"
|
||||
}
|
||||
|
|
@ -370,3 +464,10 @@ func isStructType(expr ast.Expr) bool {
|
|||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func getDirectoryForPackage(pkg *ast.Package) string {
|
||||
for _, file := range pkg.Files {
|
||||
return filepath.Dir(file.Name.Name)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,758 +0,0 @@
|
|||
package parser
|
||||
|
||||
//
|
||||
//import (
|
||||
// "fmt"
|
||||
// "go/ast"
|
||||
// "go/build"
|
||||
// "go/parser"
|
||||
// "go/token"
|
||||
// "os"
|
||||
// "path/filepath"
|
||||
// "strconv"
|
||||
// "strings"
|
||||
//
|
||||
// "github.com/samber/lo"
|
||||
//)
|
||||
//
|
||||
//var Debug = false
|
||||
//
|
||||
//func debug(msg string, args ...interface{}) {
|
||||
// if Debug {
|
||||
// println(fmt.Sprintf(msg, args...))
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//type BoundStruct struct {
|
||||
// Name string
|
||||
// MethodDecls []Method
|
||||
//}
|
||||
//
|
||||
//type parsedPackage struct {
|
||||
// name string
|
||||
// pkg *ast.Package
|
||||
// boundStructs map[string]*BoundStruct
|
||||
// boundStructMethods map[string][]Method
|
||||
//}
|
||||
//
|
||||
//type Context struct {
|
||||
// packages map[string]*parsedPackage
|
||||
// dir string
|
||||
//}
|
||||
//
|
||||
//func (c *Context) findImportPackage(pkgName string, pkg *ast.Package) (*ast.Package, error) {
|
||||
// for _, file := range pkg.Files {
|
||||
// for _, imp := range file.Imports {
|
||||
// path, err := strconv.Unquote(imp.Path.Value)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// if imp.Name != nil && imp.Name.Name == pkgName {
|
||||
// return c.getPackageFromPath(path)
|
||||
// } else {
|
||||
// _, pkgName := filepath.Split(path)
|
||||
// if pkgName == pkgName {
|
||||
// return c.getPackageFromPath(path)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return nil, fmt.Errorf("Package %s not found in %s", pkgName, pkg.Name)
|
||||
//}
|
||||
//
|
||||
//func (c *Context) getPackageFromPath(path string) (*ast.Package, error) {
|
||||
// dir, err := filepath.Abs(c.dir)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// if !filepath.IsAbs(path) {
|
||||
// dir = filepath.Join(dir, path)
|
||||
// } else {
|
||||
// impPkgDir, err := build.Import(path, dir, build.ImportMode(0))
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// dir = impPkgDir.Dir
|
||||
// }
|
||||
// impPkg, err := parser.ParseDir(token.NewFileSet(), dir, nil, parser.AllErrors)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// for impName, impPkg := range impPkg {
|
||||
// if impName == "main" {
|
||||
// continue
|
||||
// }
|
||||
// return impPkg, nil
|
||||
// }
|
||||
// return nil, fmt.Errorf("Package not found in imported package %s", path)
|
||||
//}
|
||||
//
|
||||
//func ParseDirectory(dir string) (*Context, error) {
|
||||
// // Parse the directory
|
||||
// fset := token.NewFileSet()
|
||||
// if dir == "." || dir == "" {
|
||||
// cwd, err := os.Getwd()
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// dir = cwd
|
||||
// }
|
||||
// println("Parsing directory " + dir)
|
||||
// pkgs, err := parser.ParseDir(fset, dir, nil, parser.AllErrors)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// context := &Context{
|
||||
// dir: dir,
|
||||
// packages: make(map[string]*parsedPackage),
|
||||
// }
|
||||
//
|
||||
// // Iterate through the packages
|
||||
// for _, pkg := range pkgs {
|
||||
// context.packages[pkg.Name] = &parsedPackage{
|
||||
// name: pkg.Name,
|
||||
// pkg: pkg,
|
||||
// boundStructs: make(map[string]*BoundStruct),
|
||||
// boundStructMethods: make(map[string][]Method),
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// findApplicationNewCalls(context)
|
||||
// err = findStructDefinitions(context)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// return context, nil
|
||||
//}
|
||||
//
|
||||
//func findStructDefinitions(context *Context) error {
|
||||
// // iterate over the packages
|
||||
// for _, pkg := range context.packages {
|
||||
// // iterate the struct names
|
||||
// for structName, _ := range pkg.boundStructs {
|
||||
// structSpec, methods := getStructTypeSpec(pkg.pkg, structName)
|
||||
// if structSpec == nil {
|
||||
// return fmt.Errorf("unable to find struct %s in package %s", structName, pkg.name)
|
||||
// }
|
||||
// pkg.boundStructs[structName] = &BoundStruct{
|
||||
// Name: structName,
|
||||
// MethodDecls: methods,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//func findApplicationNewCalls(context *Context) {
|
||||
// println("Finding application.New calls")
|
||||
// // Iterate through the packages
|
||||
// currentPackages := lo.Keys(context.packages)
|
||||
//
|
||||
// for _, packageName := range currentPackages {
|
||||
// thisPackage := context.packages[packageName]
|
||||
// debug("Parsing package: %s", packageName)
|
||||
// // Iterate through the package's files
|
||||
// for _, file := range thisPackage.pkg.Files {
|
||||
// // Use an ast.Inspector to find the calls to application.New
|
||||
// ast.Inspect(file, func(n ast.Node) bool {
|
||||
// // Check if the node is a call expression
|
||||
// callExpr, ok := n.(*ast.CallExpr)
|
||||
// if !ok {
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// // Check if the function being called is "application.New"
|
||||
// selExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
|
||||
// if !ok {
|
||||
// return true
|
||||
// }
|
||||
// if selExpr.Sel.Name != "New" {
|
||||
// return true
|
||||
// }
|
||||
// if id, ok := selExpr.X.(*ast.Ident); !ok || id.Name != "application" {
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// // Check there is only 1 argument
|
||||
// if len(callExpr.Args) != 1 {
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// // Check argument 1 is a struct literal
|
||||
// structLit, ok := callExpr.Args[0].(*ast.CompositeLit)
|
||||
// if !ok {
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// // Check struct literal is of type "application.Options"
|
||||
// selectorExpr, ok := structLit.Type.(*ast.SelectorExpr)
|
||||
// if !ok {
|
||||
// return true
|
||||
// }
|
||||
// if selectorExpr.Sel.Name != "Options" {
|
||||
// return true
|
||||
// }
|
||||
// if id, ok := selectorExpr.X.(*ast.Ident); !ok || id.Name != "application" {
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// for _, elt := range structLit.Elts {
|
||||
// // Find the "Bind" field
|
||||
// kvExpr, ok := elt.(*ast.KeyValueExpr)
|
||||
// if !ok {
|
||||
// continue
|
||||
// }
|
||||
// if id, ok := kvExpr.Key.(*ast.Ident); !ok || id.Name != "Bind" {
|
||||
// continue
|
||||
// }
|
||||
// // Check the value is a slice of interfaces
|
||||
// sliceExpr, ok := kvExpr.Value.(*ast.CompositeLit)
|
||||
// if !ok {
|
||||
// continue
|
||||
// }
|
||||
// var arrayType *ast.ArrayType
|
||||
// if arrayType, ok = sliceExpr.Type.(*ast.ArrayType); !ok {
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// // Check array type is of type "interface{}"
|
||||
// if _, ok := arrayType.Elt.(*ast.InterfaceType); !ok {
|
||||
// continue
|
||||
// }
|
||||
// // Iterate through the slice elements
|
||||
// for _, elt := range sliceExpr.Elts {
|
||||
// // Check the element is a unary expression
|
||||
// unaryExpr, ok := elt.(*ast.UnaryExpr)
|
||||
// if ok {
|
||||
// // Check the unary expression is a composite lit
|
||||
// boundStructLit, ok := unaryExpr.X.(*ast.CompositeLit)
|
||||
// if !ok {
|
||||
// continue
|
||||
// }
|
||||
// // Check if the composite lit is a struct
|
||||
// if _, ok := boundStructLit.Type.(*ast.StructType); ok {
|
||||
// // Parse struct
|
||||
// continue
|
||||
// }
|
||||
// // Check if the lit is an ident
|
||||
// ident, ok := boundStructLit.Type.(*ast.Ident)
|
||||
// if ok {
|
||||
// if ident.Obj == nil {
|
||||
// thisPackage.boundStructs[ident.Name] = &BoundStruct{
|
||||
// Name: ident.Name,
|
||||
// }
|
||||
// continue
|
||||
// }
|
||||
// // Check if the ident is a struct type
|
||||
// if _, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
||||
// thisPackage.boundStructs[ident.Name] = &BoundStruct{
|
||||
// Name: ident.Name,
|
||||
// }
|
||||
// continue
|
||||
// }
|
||||
// // Check the typespec decl is a struct
|
||||
// if _, ok := ident.Obj.Decl.(*ast.StructType); ok {
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// // Check if the lit is a selector
|
||||
// selector, ok := boundStructLit.Type.(*ast.SelectorExpr)
|
||||
// if ok {
|
||||
// // Check if the selector is an ident
|
||||
// if ident, ok := selector.X.(*ast.Ident); ok {
|
||||
// // Check if the ident is a package
|
||||
// if _, ok := context.packages[ident.Name]; !ok {
|
||||
// externalPackage, err := context.getPackageFromPath(ident.Name)
|
||||
// if err != nil {
|
||||
// println("Error getting package from path: " + err.Error())
|
||||
// return true
|
||||
// }
|
||||
// context.packages[ident.Name] = &parsedPackage{
|
||||
// name: ident.Name,
|
||||
// pkg: externalPackage,
|
||||
// boundStructs: make(map[string]*BoundStruct),
|
||||
// }
|
||||
// }
|
||||
// context.packages[ident.Name].boundStructs[selector.Sel.Name] = &BoundStruct{
|
||||
// Name: selector.Sel.Name,
|
||||
// }
|
||||
// }
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return true
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//type Method struct {
|
||||
// Name string
|
||||
// Type *ast.FuncType
|
||||
//}
|
||||
//
|
||||
|
||||
//
|
||||
////func findApplicationNewCalls(context *Context) {
|
||||
//// println("Finding application.New calls")
|
||||
//// // Iterate through the packages
|
||||
//// currentPackages := lo.Keys(context.packages)
|
||||
////
|
||||
//// for _, packageName := range currentPackages {
|
||||
//// thisPackage := context.packages[packageName]
|
||||
//// debug("Parsing package: %s", packageName)
|
||||
//// // Iterate through the package's files
|
||||
//// for _, file := range thisPackage.pkg.Files {
|
||||
//// // Use an ast.Inspector to find the calls to application.New
|
||||
//// ast.Inspect(file, func(n ast.Node) bool {
|
||||
//// // Check if the node is a call expression
|
||||
//// callExpr, ok := n.(*ast.CallExpr)
|
||||
//// if !ok {
|
||||
//// return true
|
||||
//// }
|
||||
////
|
||||
//// // Check if the function being called is "application.New"
|
||||
//// selExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
|
||||
//// if !ok {
|
||||
//// return true
|
||||
//// }
|
||||
//// if selExpr.Sel.Name != "New" {
|
||||
//// return true
|
||||
//// }
|
||||
//// if id, ok := selExpr.X.(*ast.Ident); !ok || id.Name != "application" {
|
||||
//// return true
|
||||
//// }
|
||||
////
|
||||
//// // Check there is only 1 argument
|
||||
//// if len(callExpr.Args) != 1 {
|
||||
//// return true
|
||||
//// }
|
||||
////
|
||||
//// // Check argument 1 is a struct literal
|
||||
//// structLit, ok := callExpr.Args[0].(*ast.CompositeLit)
|
||||
//// if !ok {
|
||||
//// return true
|
||||
//// }
|
||||
////
|
||||
//// // Check struct literal is of type "application.Options"
|
||||
//// selectorExpr, ok := structLit.Type.(*ast.SelectorExpr)
|
||||
//// if !ok {
|
||||
//// return true
|
||||
//// }
|
||||
//// if selectorExpr.Sel.Name != "Application" {
|
||||
//// return true
|
||||
//// }
|
||||
//// if id, ok := selectorExpr.X.(*ast.Ident); !ok || id.Name != "options" {
|
||||
//// return true
|
||||
//// }
|
||||
////
|
||||
//// for _, elt := range structLit.Elts {
|
||||
//// // Find the "Bind" field
|
||||
//// kvExpr, ok := elt.(*ast.KeyValueExpr)
|
||||
//// if !ok {
|
||||
//// continue
|
||||
//// }
|
||||
//// if id, ok := kvExpr.Key.(*ast.Ident); !ok || id.Name != "Bind" {
|
||||
//// continue
|
||||
//// }
|
||||
//// // Check the value is a slice of interfaces
|
||||
//// sliceExpr, ok := kvExpr.Value.(*ast.CompositeLit)
|
||||
//// if !ok {
|
||||
//// continue
|
||||
//// }
|
||||
//// var arrayType *ast.ArrayType
|
||||
//// if arrayType, ok = sliceExpr.Type.(*ast.ArrayType); !ok {
|
||||
//// continue
|
||||
//// }
|
||||
////
|
||||
//// // Check array type is of type "interface{}"
|
||||
//// if _, ok := arrayType.Elt.(*ast.InterfaceType); !ok {
|
||||
//// continue
|
||||
//// }
|
||||
//// // Iterate through the slice elements
|
||||
//// for _, elt := range sliceExpr.Elts {
|
||||
//// // Check the element is a unary expression
|
||||
//// unaryExpr, ok := elt.(*ast.UnaryExpr)
|
||||
//// if ok {
|
||||
//// // Check the unary expression is a composite lit
|
||||
//// boundStructLit, ok := unaryExpr.X.(*ast.CompositeLit)
|
||||
//// if !ok {
|
||||
//// continue
|
||||
//// }
|
||||
//// // Check if the composite lit is a struct
|
||||
//// if _, ok := boundStructLit.Type.(*ast.StructType); ok {
|
||||
//// // Parse struct
|
||||
//// continue
|
||||
//// }
|
||||
//// // Check if the lit is an ident
|
||||
//// ident, ok := boundStructLit.Type.(*ast.Ident)
|
||||
//// if ok {
|
||||
//// if ident.Obj == nil {
|
||||
//// structTypeSpec := findStructInPackage(thisPackage.pkg, ident.Name)
|
||||
//// thisPackage.boundStructs[ident.Name] = structTypeSpec
|
||||
//// findNestedStructs(structTypeSpec, file, packageName, context)
|
||||
//// continue
|
||||
//// }
|
||||
//// // Check if the ident is a struct type
|
||||
//// if t, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
||||
//// thisPackage.boundStructs[ident.Name] = t
|
||||
//// findNestedStructs(t, file, packageName, context)
|
||||
//// continue
|
||||
//// }
|
||||
//// // Check the typespec decl is a struct
|
||||
//// if _, ok := ident.Obj.Decl.(*ast.StructType); ok {
|
||||
//// continue
|
||||
//// }
|
||||
////
|
||||
//// }
|
||||
//// // Check if the lit is a selector
|
||||
//// selector, ok := boundStructLit.Type.(*ast.SelectorExpr)
|
||||
//// if ok {
|
||||
//// getStructsFromSelector(selector, file, context)
|
||||
//// continue
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
////
|
||||
//// return true
|
||||
//// })
|
||||
//// }
|
||||
//// }
|
||||
////}
|
||||
//
|
||||
////func getStructsFromSelector(selector *ast.SelectorExpr, file *ast.File, context *Context) {
|
||||
//// debug("getStructsFromSelector called with selector '%s' on file '%s.go'", selector.Sel.Name, file.Name.Name)
|
||||
////
|
||||
//// // extract package name from selector
|
||||
//// packageName := selector.X.(*ast.Ident).Name
|
||||
////
|
||||
//// if context.packages[packageName] == nil {
|
||||
//// context.packages[packageName] = &parsedPackage{
|
||||
//// name: packageName,
|
||||
//// boundStructs: make(map[string]*BoundStruct),
|
||||
//// }
|
||||
//// }
|
||||
////
|
||||
//// // extract struct name from selector
|
||||
//// structName := selector.Sel.Name
|
||||
////
|
||||
//// // Find the package name from the imports
|
||||
//// for _, imp := range file.Imports {
|
||||
//// var match bool
|
||||
//// if imp.Name == nil || imp.Name.Name == packageName {
|
||||
//// match = true
|
||||
//// }
|
||||
//// if match == false {
|
||||
//// pathSplit := strings.Split(imp.Path.Value, "/")
|
||||
//// endPath := strings.Trim(pathSplit[len(pathSplit)-1], `"`)
|
||||
//// match = endPath == packageName
|
||||
//// }
|
||||
////
|
||||
//// if match {
|
||||
//// // We have the import
|
||||
//// cfg := &packages.Config{
|
||||
//// Mode: packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo | packages.NeedModule,
|
||||
//// }
|
||||
//// pkgs, err := packages.Load(cfg, strings.Trim(imp.Path.Value, `"`))
|
||||
//// if err != nil {
|
||||
//// panic(err)
|
||||
//// }
|
||||
//// foundPackage := pkgs[0]
|
||||
////
|
||||
//// // Iterate the files in the package and find struct types
|
||||
//// for _, parsedFile := range foundPackage.Syntax {
|
||||
//// ast.Inspect(parsedFile, func(n ast.Node) bool {
|
||||
//// if n == nil {
|
||||
//// return false
|
||||
//// }
|
||||
//// switch n.(type) {
|
||||
//// case *ast.TypeSpec:
|
||||
//// typeSpec := n.(*ast.TypeSpec)
|
||||
//// if typeSpec.Name.Name == structName {
|
||||
//// if _, ok := context.packages[packageName].boundStructs[structName]; !ok {
|
||||
//// debug("Adding struct '%s' in package '%s'", structName, packageName)
|
||||
//// context.packages[packageName].boundStructs[typeSpec.Name.Name] = &BoundStruct{
|
||||
//// Name: typeSpec.Name.Name,
|
||||
//// }
|
||||
//// findNestedStructs(typeSpec, parsedFile, packageName, context)
|
||||
//// }
|
||||
//// return false
|
||||
//// }
|
||||
//// }
|
||||
//// return true
|
||||
//// })
|
||||
//// }
|
||||
////
|
||||
//// continue
|
||||
//// }
|
||||
//// }
|
||||
////
|
||||
////}
|
||||
////
|
||||
////func findNestedStructs(t *ast.TypeSpec, parsedFile *ast.File, pkgName string, context *Context) {
|
||||
//// debug("findNestedStructs called with type '%s' on file '%s.go'", t.Name.Name, parsedFile.Name.Name)
|
||||
//// structType, ok := t.Type.(*ast.StructType)
|
||||
//// if !ok {
|
||||
//// return
|
||||
//// }
|
||||
//// for _, field := range structType.Fields.List {
|
||||
//// for _, ident := range field.Names {
|
||||
//// switch t := ident.Obj.Decl.(*ast.Field).Type.(type) {
|
||||
//// case *ast.Ident:
|
||||
//// if t.Obj == nil {
|
||||
//// continue
|
||||
//// }
|
||||
//// if t.Obj.Kind == ast.Typ {
|
||||
//// if _, ok := t.Obj.Decl.(*ast.TypeSpec); ok {
|
||||
//// if _, ok := context.packages[pkgName].boundStructs[t.Name]; !ok {
|
||||
//// debug("Adding nested struct '%s' to package '%s'", t.Name, pkgName)
|
||||
//// context.packages[pkgName].boundStructs[t.Name] = t.Obj.Decl.(*ast.TypeSpec)
|
||||
//// findNestedStructs(t.Obj.Decl.(*ast.TypeSpec), parsedFile, pkgName, context)
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// case *ast.SelectorExpr:
|
||||
//// if ident, ok := t.X.(*ast.Ident); ok {
|
||||
//// if ident.IsExported() {
|
||||
//// getStructsFromSelector(t, parsedFile, context)
|
||||
//// }
|
||||
//// }
|
||||
//// case *ast.StarExpr:
|
||||
//// if sel, ok := t.X.(*ast.SelectorExpr); ok {
|
||||
//// if _, ok := sel.X.(*ast.Ident); ok {
|
||||
//// if ident.IsExported() {
|
||||
//// getStructsFromSelector(sel, parsedFile, context)
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// findStructsInMethods(t.Name.Name, parsedFile, pkgName, context)
|
||||
////
|
||||
////}
|
||||
////
|
||||
////func findStructsInMethods(name string, parsedFile *ast.File, pkgName string, context *Context) {
|
||||
//// debug("findStructsInMethods called with type '%s' on file '%s.go'", name, parsedFile.Name.Name)
|
||||
//// // Find the struct declaration for the given name
|
||||
//// var structDecl *ast.TypeSpec
|
||||
//// for _, decl := range parsedFile.Decls {
|
||||
//// if fn, ok := decl.(*ast.FuncDecl); ok {
|
||||
//// // check the receiver name is the same as the name given
|
||||
//// if fn.Recv == nil {
|
||||
//// continue
|
||||
//// }
|
||||
//// // Check if the receiver is a pointer
|
||||
//// if starExpr, ok := fn.Recv.List[0].Type.(*ast.StarExpr); ok {
|
||||
//// if ident, ok := starExpr.X.(*ast.Ident); ok {
|
||||
//// if ident.Name != name {
|
||||
//// continue
|
||||
//// }
|
||||
//// }
|
||||
//// } else {
|
||||
//// if ident, ok := fn.Recv.List[0].Type.(*ast.Ident); ok {
|
||||
//// if ident.Name != name {
|
||||
//// continue
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// findStructsInMethodParams(fn, parsedFile, pkgName, context)
|
||||
//// }
|
||||
//// }
|
||||
//// if structDecl == nil {
|
||||
//// return
|
||||
//// }
|
||||
//// // Iterate the methods in the struct
|
||||
////
|
||||
////}
|
||||
////
|
||||
////func findStructsInMethodParams(f *ast.FuncDecl, parsedFile *ast.File, pkgName string, context *Context) {
|
||||
//// debug("findStructsInMethodParams called with type '%s' on file '%s.go'", f.Name.Name, parsedFile.Name.Name)
|
||||
//// if f.Type.Params == nil {
|
||||
//// for _, field := range f.Type.Params.List {
|
||||
//// parseField(field, parsedFile, pkgName, context)
|
||||
//// }
|
||||
//// }
|
||||
//// if f.Type.Results != nil {
|
||||
//// for _, field := range f.Type.Results.List {
|
||||
//// parseField(field, parsedFile, pkgName, context)
|
||||
//// }
|
||||
//// }
|
||||
////}
|
||||
//
|
||||
////func parseField(field *ast.Field, parsedFile *ast.File, pkgName string, context *Context) {
|
||||
//// if se, ok := field.Type.(*ast.StarExpr); ok {
|
||||
//// // Check if the star expr is a struct
|
||||
//// if selExp, ok := se.X.(*ast.SelectorExpr); ok {
|
||||
//// getStructsFromSelector(selExp, parsedFile, context)
|
||||
//// return
|
||||
//// }
|
||||
//// if ident, ok := se.X.(*ast.Ident); ok {
|
||||
//// if ident.Obj == nil {
|
||||
//// return
|
||||
//// }
|
||||
//// if ident.Obj.Kind == ast.Typ {
|
||||
//// if _, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
||||
//// if _, ok := context.packages[pkgName].boundStructs[ident.Name]; !ok {
|
||||
//// debug("Adding field struct '%s' to package '%s'", ident.Name, pkgName)
|
||||
//// context.packages[pkgName].boundStructs[ident.Name] = ident.Obj.Decl.(*ast.TypeSpec)
|
||||
//// findNestedStructs(ident.Obj.Decl.(*ast.TypeSpec), parsedFile, pkgName, context)
|
||||
//// } else {
|
||||
//// debug("Struct %s already bound", ident.Name)
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// if selExp, ok := field.Type.(*ast.SelectorExpr); ok {
|
||||
//// getStructsFromSelector(selExp, parsedFile, context)
|
||||
//// return
|
||||
//// }
|
||||
//// if ident, ok := field.Type.(*ast.Ident); ok {
|
||||
//// if ident.Obj == nil {
|
||||
//// return
|
||||
//// }
|
||||
//// if ident.Obj.Kind == ast.Typ {
|
||||
//// if _, ok := ident.Obj.Decl.(*ast.TypeSpec); ok {
|
||||
//// if _, ok := context.packages[pkgName].boundStructs[ident.Name]; !ok {
|
||||
//// debug("Adding field struct '%s' to package '%s'", ident.Name, pkgName)
|
||||
//// context.packages[pkgName].boundStructs[ident.Name] = ident.Obj.Decl.(*ast.TypeSpec)
|
||||
//// findNestedStructs(ident.Obj.Decl.(*ast.TypeSpec), parsedFile, pkgName, context)
|
||||
//// } else {
|
||||
//// debug("Struct %s already bound", ident.Name)
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
////}
|
||||
////
|
||||
////func findStructInPackage(pkg *ast.Package, name string) *ast.TypeSpec {
|
||||
//// for _, file := range pkg.Files {
|
||||
//// for _, decl := range file.Decls {
|
||||
//// if gen, ok := decl.(*ast.GenDecl); ok && gen.Tok == token.TYPE {
|
||||
//// for _, spec := range gen.Specs {
|
||||
//// if typeSpec, ok := spec.(*ast.TypeSpec); ok {
|
||||
//// if typeSpec.Name.Name == name {
|
||||
//// if _, ok := typeSpec.Type.(*ast.StructType); ok {
|
||||
//// return typeSpec
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
//// return nil
|
||||
////}
|
||||
////
|
||||
////type Package struct {
|
||||
//// Name string
|
||||
//// Specs []*ast.TypeSpec
|
||||
////}
|
||||
////
|
||||
////var goToTS = map[string]string{
|
||||
//// "int": "number",
|
||||
//// "int8": "number",
|
||||
//// "int16": "number",
|
||||
//// "int32": "number",
|
||||
//// "int64": "number",
|
||||
//// "uint": "number",
|
||||
//// "uint8": "number",
|
||||
//// "uint16": "number",
|
||||
//// "uint32": "number",
|
||||
//// "uint64": "number",
|
||||
//// "float32": "number",
|
||||
//// "float64": "number",
|
||||
//// "string": "string",
|
||||
//// "bool": "boolean",
|
||||
////}
|
||||
//
|
||||
////func GenerateModels(specs map[string][]*ast.TypeSpec) ([]byte, error) {
|
||||
//// var buf bytes.Buffer
|
||||
//// var packages []Package
|
||||
//// for pkg, pkgSpecs := range specs {
|
||||
//// packages = append(packages, Package{Name: pkg, Specs: pkgSpecs})
|
||||
//// }
|
||||
//// sort.Slice(packages, func(i, j int) bool { return packages[i].Name < packages[j].Name })
|
||||
//// for _, pkg := range packages {
|
||||
//// if _, err := fmt.Fprintf(&buf, "namespace %s {\n", pkg.Name); err != nil {
|
||||
//// return nil, err
|
||||
//// }
|
||||
//// sort.Slice(pkg.Specs, func(i, j int) bool { return pkg.Specs[i].Name.Name < pkg.Specs[j].Name.Name })
|
||||
//// for _, spec := range pkg.Specs {
|
||||
//// if structType, ok := spec.Type.(*ast.StructType); ok {
|
||||
//// if _, err := fmt.Fprintf(&buf, " class %s {\n", spec.Name.Name); err != nil {
|
||||
//// return nil, err
|
||||
//// }
|
||||
////
|
||||
//// for _, field := range structType.Fields.List {
|
||||
////
|
||||
//// // Get the Go type of the field
|
||||
//// goType := types.ExprString(field.Type)
|
||||
//// // Look up the corresponding TypeScript type
|
||||
//// tsType, ok := goToTS[goType]
|
||||
//// if !ok {
|
||||
//// tsType = goType
|
||||
//// }
|
||||
////
|
||||
//// if _, err := fmt.Fprintf(&buf, " %s: %s;\n", field.Names[0].Name, tsType); err != nil {
|
||||
//// return nil, err
|
||||
//// }
|
||||
//// }
|
||||
////
|
||||
//// if _, err := fmt.Fprintf(&buf, " }\n"); err != nil {
|
||||
//// return nil, err
|
||||
//// }
|
||||
//// if _, err := fmt.Fprintf(&buf, " }\n"); err != nil {
|
||||
//// return nil, err
|
||||
//// }
|
||||
//// }
|
||||
//// }
|
||||
////
|
||||
//// if _, err := fmt.Fprintf(&buf, "}\n"); err != nil {
|
||||
//// return nil, err
|
||||
//// }
|
||||
//// }
|
||||
//// return buf.Bytes(), nil
|
||||
////}
|
||||
//
|
||||
//type allModels struct {
|
||||
// known map[string]map[string]struct{}
|
||||
//}
|
||||
//
|
||||
//func newAllModels(models map[string][]*ast.TypeSpec) *allModels {
|
||||
// result := &allModels{known: make(map[string]map[string]struct{})}
|
||||
// // iterate over all models
|
||||
// for pkg, pkgSpecs := range models {
|
||||
// for _, spec := range pkgSpecs {
|
||||
// result.known[pkg] = make(map[string]struct{})
|
||||
// result.known[pkg][spec.Name.Name] = struct{}{}
|
||||
// }
|
||||
// }
|
||||
// return result
|
||||
//}
|
||||
//
|
||||
//func (k *allModels) exists(name string) bool {
|
||||
// // Split the name into package and type
|
||||
// parts := strings.Split(name, ".")
|
||||
// typ := parts[0]
|
||||
// pkg := "main"
|
||||
// if len(parts) == 2 {
|
||||
// pkg = parts[0]
|
||||
// typ = parts[1]
|
||||
// }
|
||||
//
|
||||
// knownPkg, ok := k.known[pkg]
|
||||
// if !ok {
|
||||
// return false
|
||||
// }
|
||||
// _, ok = knownPkg[typ]
|
||||
// return ok
|
||||
//}
|
||||
|
|
@ -25,18 +25,18 @@ func TestParseDirectory(t *testing.T) {
|
|||
DocComment: "Greet someone\n",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "name",
|
||||
Type: "string",
|
||||
IsStruct: false,
|
||||
IsSlice: false,
|
||||
Name: "name",
|
||||
Type: &ParameterType{
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: "string",
|
||||
IsStruct: false,
|
||||
IsSlice: false,
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -46,10 +46,10 @@ func TestParseDirectory(t *testing.T) {
|
|||
Inputs: nil,
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "",
|
||||
Type: "string",
|
||||
IsStruct: false,
|
||||
IsSlice: false,
|
||||
Name: "",
|
||||
Type: &ParameterType{
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -57,14 +57,18 @@ func TestParseDirectory(t *testing.T) {
|
|||
Name: "StringArrayInputStringOut",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: "string",
|
||||
IsSlice: true,
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "string",
|
||||
IsSlice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: "string",
|
||||
Type: &ParameterType{
|
||||
Name: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -72,15 +76,19 @@ func TestParseDirectory(t *testing.T) {
|
|||
Name: "StringArrayInputStringArrayOut",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: "string",
|
||||
IsSlice: true,
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "string",
|
||||
IsSlice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: "string",
|
||||
IsSlice: true,
|
||||
Type: &ParameterType{
|
||||
Name: "string",
|
||||
IsSlice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -88,16 +96,20 @@ func TestParseDirectory(t *testing.T) {
|
|||
Name: "StringArrayInputNamedOutput",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: "string",
|
||||
IsSlice: true,
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "string",
|
||||
IsSlice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "output",
|
||||
Type: "string",
|
||||
IsSlice: true,
|
||||
Name: "output",
|
||||
Type: &ParameterType{
|
||||
Name: "string",
|
||||
IsSlice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -105,20 +117,26 @@ func TestParseDirectory(t *testing.T) {
|
|||
Name: "StringArrayInputNamedOutputs",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: "string",
|
||||
IsSlice: true,
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "string",
|
||||
IsSlice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "output",
|
||||
Type: "string",
|
||||
IsSlice: true,
|
||||
Name: "output",
|
||||
Type: &ParameterType{
|
||||
Name: "string",
|
||||
IsSlice: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "err",
|
||||
Type: "error",
|
||||
Type: &ParameterType{
|
||||
Name: "error",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -126,20 +144,115 @@ func TestParseDirectory(t *testing.T) {
|
|||
Name: "IntPointerInputNamedOutputs",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: "int",
|
||||
IsPointer: true,
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "int",
|
||||
IsPointer: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "output",
|
||||
Type: "int",
|
||||
IsPointer: true,
|
||||
Name: "output",
|
||||
Type: &ParameterType{
|
||||
Name: "int",
|
||||
IsPointer: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "err",
|
||||
Type: "error",
|
||||
Type: &ParameterType{
|
||||
Name: "error",
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "IntInIntOut",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "int",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
Name: "int",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "UIntInUIntOut",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "uint",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
Name: "uint",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Float32InFloat32Out",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "float32",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
Name: "float32",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Float64InFloat64Out",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "float64",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
Name: "float64",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "BoolInBoolOut",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "bool",
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: &ParameterType{
|
||||
Name: "bool",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -147,15 +260,19 @@ func TestParseDirectory(t *testing.T) {
|
|||
Name: "StructPointerInputErrorOutput",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: "Person",
|
||||
IsStruct: true,
|
||||
IsPointer: true,
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "Person",
|
||||
IsPointer: true,
|
||||
IsStruct: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: "error",
|
||||
Type: &ParameterType{
|
||||
Name: "error",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -163,17 +280,91 @@ func TestParseDirectory(t *testing.T) {
|
|||
Name: "StructPointerInputStructPointerOutput",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: "Person",
|
||||
IsStruct: true,
|
||||
IsPointer: true,
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "Person",
|
||||
IsPointer: true,
|
||||
IsStruct: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Type: "Person",
|
||||
IsPointer: true,
|
||||
IsStruct: true,
|
||||
Type: &ParameterType{
|
||||
Name: "Person",
|
||||
IsPointer: true,
|
||||
IsStruct: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "MapIntInt",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "map",
|
||||
MapKey: &ParameterType{
|
||||
Name: "int",
|
||||
},
|
||||
MapValue: &ParameterType{
|
||||
Name: "int",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{},
|
||||
},
|
||||
{
|
||||
Name: "MapIntSliceInt",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "map",
|
||||
MapKey: &ParameterType{
|
||||
Name: "int",
|
||||
},
|
||||
MapValue: &ParameterType{
|
||||
Name: "int",
|
||||
IsSlice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{},
|
||||
},
|
||||
{
|
||||
Name: "MapIntSliceIntInMapIntSliceIntOut",
|
||||
Inputs: []*Parameter{
|
||||
{
|
||||
Name: "in",
|
||||
Type: &ParameterType{
|
||||
Name: "map",
|
||||
MapKey: &ParameterType{
|
||||
Name: "int",
|
||||
},
|
||||
MapValue: &ParameterType{
|
||||
Name: "int",
|
||||
IsSlice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Outputs: []*Parameter{
|
||||
{
|
||||
Name: "out",
|
||||
Type: &ParameterType{
|
||||
Name: "map",
|
||||
MapKey: &ParameterType{
|
||||
Name: "int",
|
||||
},
|
||||
MapValue: &ParameterType{
|
||||
Name: "int",
|
||||
IsSlice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -208,7 +399,6 @@ func TestParseDirectory(t *testing.T) {
|
|||
t.Errorf("ParseDirectory() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(tt.wantBoundMethods, got.BoundMethods); diff != "" {
|
||||
t.Errorf("ParseDirectory() failed:\n" + diff)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import (
|
|||
)
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
Name string
|
||||
Parent *Person
|
||||
}
|
||||
|
||||
// GreetService is great
|
||||
|
|
@ -47,6 +48,24 @@ func (*GreetService) IntPointerInputNamedOutputs(in *int) (output *int, err erro
|
|||
return in, nil
|
||||
}
|
||||
|
||||
func (*GreetService) IntInIntOut(in int) int {
|
||||
return in
|
||||
}
|
||||
|
||||
func (*GreetService) UIntInUIntOut(in uint) uint {
|
||||
return in
|
||||
}
|
||||
func (*GreetService) Float32InFloat32Out(in float32) float32 {
|
||||
return in
|
||||
}
|
||||
func (*GreetService) Float64InFloat64Out(in float64) float64 {
|
||||
return in
|
||||
}
|
||||
|
||||
func (*GreetService) BoolInBoolOut(in bool) bool {
|
||||
return in
|
||||
}
|
||||
|
||||
func (*GreetService) StructPointerInputErrorOutput(in *Person) error {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -55,6 +74,16 @@ func (*GreetService) StructPointerInputStructPointerOutput(in *Person) *Person {
|
|||
return in
|
||||
}
|
||||
|
||||
func (*GreetService) MapIntInt(in map[int]int) {
|
||||
}
|
||||
|
||||
func (*GreetService) MapIntSliceInt(in map[int][]int) {
|
||||
}
|
||||
|
||||
func (*GreetService) MapIntSliceIntInMapIntSliceIntOut(in map[int][]int) (out map[int][]int) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := application.New(application.Options{
|
||||
Bind: []interface{}{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue