mirror of
https://github.com/wavetermdev/wails.git
synced 2026-08-05 13:53:43 -07:00
Parse Models
Support recursive fields Add loads of tests
This commit is contained in:
@@ -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
|
||||
|
||||
+134
-33
@@ -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 ""
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
}
|
||||
|
||||
+30
-1
@@ -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{}{
|
||||
|
||||
Reference in New Issue
Block a user