github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/pkg/plugins/pluginrpc-gen/parser.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"go/ast"
     7  	"go/parser"
     8  	"go/token"
     9  	"path"
    10  	"reflect"
    11  	"strings"
    12  )
    13  
    14  var errBadReturn = errors.New("found return arg with no name: all args must be named")
    15  
    16  type errUnexpectedType struct {
    17  	expected string
    18  	actual   interface{}
    19  }
    20  
    21  func (e errUnexpectedType) Error() string {
    22  	return fmt.Sprintf("got wrong type expecting %s, got: %v", e.expected, reflect.TypeOf(e.actual))
    23  }
    24  
    25  // ParsedPkg holds information about a package that has been parsed,
    26  // its name and the list of functions.
    27  type ParsedPkg struct {
    28  	Name      string
    29  	Functions []function
    30  	Imports   []importSpec
    31  }
    32  
    33  type function struct {
    34  	Name    string
    35  	Args    []arg
    36  	Returns []arg
    37  	Doc     string
    38  }
    39  
    40  type arg struct {
    41  	Name            string
    42  	ArgType         string
    43  	PackageSelector string
    44  }
    45  
    46  func (a *arg) String() string {
    47  	return a.Name + " " + a.ArgType
    48  }
    49  
    50  type importSpec struct {
    51  	Name string
    52  	Path string
    53  }
    54  
    55  func (s *importSpec) String() string {
    56  	var ss string
    57  	if len(s.Name) != 0 {
    58  		ss += s.Name
    59  	}
    60  	ss += s.Path
    61  	return ss
    62  }
    63  
    64  // Parse parses the given file for an interface definition with the given name.
    65  func Parse(filePath string, objName string) (*ParsedPkg, error) {
    66  	fs := token.NewFileSet()
    67  	pkg, err := parser.ParseFile(fs, filePath, nil, parser.AllErrors)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	p := &ParsedPkg{}
    72  	p.Name = pkg.Name.Name
    73  	obj, exists := pkg.Scope.Objects[objName]
    74  	if !exists {
    75  		return nil, fmt.Errorf("could not find object %s in %s", objName, filePath)
    76  	}
    77  	if obj.Kind != ast.Typ {
    78  		return nil, fmt.Errorf("exected type, got %s", obj.Kind)
    79  	}
    80  	spec, ok := obj.Decl.(*ast.TypeSpec)
    81  	if !ok {
    82  		return nil, errUnexpectedType{"*ast.TypeSpec", obj.Decl}
    83  	}
    84  	iface, ok := spec.Type.(*ast.InterfaceType)
    85  	if !ok {
    86  		return nil, errUnexpectedType{"*ast.InterfaceType", spec.Type}
    87  	}
    88  
    89  	p.Functions, err = parseInterface(iface)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	// figure out what imports will be needed
    95  	imports := make(map[string]importSpec)
    96  	for _, f := range p.Functions {
    97  		args := append(f.Args, f.Returns...)
    98  		for _, arg := range args {
    99  			if len(arg.PackageSelector) == 0 {
   100  				continue
   101  			}
   102  
   103  			for _, i := range pkg.Imports {
   104  				if i.Name != nil {
   105  					if i.Name.Name != arg.PackageSelector {
   106  						continue
   107  					}
   108  					imports[i.Path.Value] = importSpec{Name: arg.PackageSelector, Path: i.Path.Value}
   109  					break
   110  				}
   111  
   112  				_, name := path.Split(i.Path.Value)
   113  				splitName := strings.Split(name, "-")
   114  				if len(splitName) > 1 {
   115  					name = splitName[len(splitName)-1]
   116  				}
   117  				// import paths have quotes already added in, so need to remove them for name comparison
   118  				name = strings.TrimPrefix(name, `"`)
   119  				name = strings.TrimSuffix(name, `"`)
   120  				if name == arg.PackageSelector {
   121  					imports[i.Path.Value] = importSpec{Path: i.Path.Value}
   122  					break
   123  				}
   124  			}
   125  		}
   126  	}
   127  
   128  	for _, spec := range imports {
   129  		p.Imports = append(p.Imports, spec)
   130  	}
   131  
   132  	return p, nil
   133  }
   134  
   135  func parseInterface(iface *ast.InterfaceType) ([]function, error) {
   136  	var functions []function
   137  	for _, field := range iface.Methods.List {
   138  		switch f := field.Type.(type) {
   139  		case *ast.FuncType:
   140  			method, err := parseFunc(field)
   141  			if err != nil {
   142  				return nil, err
   143  			}
   144  			if method == nil {
   145  				continue
   146  			}
   147  			functions = append(functions, *method)
   148  		case *ast.Ident:
   149  			spec, ok := f.Obj.Decl.(*ast.TypeSpec)
   150  			if !ok {
   151  				return nil, errUnexpectedType{"*ast.TypeSpec", f.Obj.Decl}
   152  			}
   153  			iface, ok := spec.Type.(*ast.InterfaceType)
   154  			if !ok {
   155  				return nil, errUnexpectedType{"*ast.TypeSpec", spec.Type}
   156  			}
   157  			funcs, err := parseInterface(iface)
   158  			if err != nil {
   159  				fmt.Println(err)
   160  				continue
   161  			}
   162  			functions = append(functions, funcs...)
   163  		default:
   164  			return nil, errUnexpectedType{"*astFuncType or *ast.Ident", f}
   165  		}
   166  	}
   167  	return functions, nil
   168  }
   169  
   170  func parseFunc(field *ast.Field) (*function, error) {
   171  	f := field.Type.(*ast.FuncType)
   172  	method := &function{Name: field.Names[0].Name}
   173  	if _, exists := skipFuncs[method.Name]; exists {
   174  		fmt.Println("skipping:", method.Name)
   175  		return nil, nil
   176  	}
   177  	if f.Params != nil {
   178  		args, err := parseArgs(f.Params.List)
   179  		if err != nil {
   180  			return nil, err
   181  		}
   182  		method.Args = args
   183  	}
   184  	if f.Results != nil {
   185  		returns, err := parseArgs(f.Results.List)
   186  		if err != nil {
   187  			return nil, fmt.Errorf("error parsing function returns for %q: %v", method.Name, err)
   188  		}
   189  		method.Returns = returns
   190  	}
   191  	return method, nil
   192  }
   193  
   194  func parseArgs(fields []*ast.Field) ([]arg, error) {
   195  	var args []arg
   196  	for _, f := range fields {
   197  		if len(f.Names) == 0 {
   198  			return nil, errBadReturn
   199  		}
   200  		for _, name := range f.Names {
   201  			p, err := parseExpr(f.Type)
   202  			if err != nil {
   203  				return nil, err
   204  			}
   205  			args = append(args, arg{name.Name, p.value, p.pkg})
   206  		}
   207  	}
   208  	return args, nil
   209  }
   210  
   211  type parsedExpr struct {
   212  	value string
   213  	pkg   string
   214  }
   215  
   216  func parseExpr(e ast.Expr) (parsedExpr, error) {
   217  	var parsed parsedExpr
   218  	switch i := e.(type) {
   219  	case *ast.Ident:
   220  		parsed.value += i.Name
   221  	case *ast.StarExpr:
   222  		p, err := parseExpr(i.X)
   223  		if err != nil {
   224  			return parsed, err
   225  		}
   226  		parsed.value += "*"
   227  		parsed.value += p.value
   228  		parsed.pkg = p.pkg
   229  	case *ast.SelectorExpr:
   230  		p, err := parseExpr(i.X)
   231  		if err != nil {
   232  			return parsed, err
   233  		}
   234  		parsed.pkg = p.value
   235  		parsed.value += p.value + "."
   236  		parsed.value += i.Sel.Name
   237  	case *ast.MapType:
   238  		parsed.value += "map["
   239  		p, err := parseExpr(i.Key)
   240  		if err != nil {
   241  			return parsed, err
   242  		}
   243  		parsed.value += p.value
   244  		parsed.value += "]"
   245  		p, err = parseExpr(i.Value)
   246  		if err != nil {
   247  			return parsed, err
   248  		}
   249  		parsed.value += p.value
   250  		parsed.pkg = p.pkg
   251  	case *ast.ArrayType:
   252  		parsed.value += "[]"
   253  		p, err := parseExpr(i.Elt)
   254  		if err != nil {
   255  			return parsed, err
   256  		}
   257  		parsed.value += p.value
   258  		parsed.pkg = p.pkg
   259  	default:
   260  		return parsed, errUnexpectedType{"*ast.Ident or *ast.StarExpr", i}
   261  	}
   262  	return parsed, nil
   263  }