github.com/relnod/pegomock@v2.0.1+incompatible/modelgen/loader/loader.go (about)

     1  package loader
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"go/ast"
     7  	"go/types"
     8  
     9  	"github.com/petergtz/pegomock/model"
    10  	"golang.org/x/tools/go/loader"
    11  )
    12  
    13  func GenerateModel(importPath string, interfaceName string) (*model.Package, error) {
    14  	var conf loader.Config
    15  	conf.Import(importPath)
    16  	program, e := conf.Load()
    17  	if e != nil {
    18  		panic(e)
    19  	}
    20  	info := program.Imported[importPath]
    21  
    22  	for def := range info.Defs {
    23  		if def.Name == interfaceName && def.Obj.Kind == ast.Typ {
    24  			interfacetype, ok := def.Obj.Decl.(*ast.TypeSpec).Type.(*ast.InterfaceType)
    25  			if ok {
    26  				g := &modelGenerator{info: info}
    27  				iface := &model.Interface{
    28  					Name:    interfaceName,
    29  					Methods: g.modelMethodsFrom(interfacetype.Methods),
    30  				}
    31  				return &model.Package{
    32  					Name:       info.Pkg.Name(),
    33  					Interfaces: []*model.Interface{iface},
    34  				}, nil
    35  			}
    36  		}
    37  	}
    38  
    39  	return nil, errors.New("Did not find interface name \"" + interfaceName + "\"")
    40  }
    41  
    42  type modelGenerator struct {
    43  	info *loader.PackageInfo
    44  }
    45  
    46  func (g *modelGenerator) modelMethodsFrom(astMethods *ast.FieldList) (modelMethods []*model.Method) {
    47  	for _, astMethod := range astMethods.List {
    48  		modelMethods = append(modelMethods, g.modelMethodFrom(astMethod))
    49  	}
    50  	return
    51  }
    52  
    53  func (g *modelGenerator) modelMethodFrom(astMethod *ast.Field) *model.Method {
    54  	in, out, variadic := g.signatureFrom(astMethod.Type.(*ast.FuncType))
    55  	return &model.Method{Name: astMethod.Names[0].Name, In: in, Variadic: variadic, Out: out}
    56  }
    57  
    58  func (g *modelGenerator) signatureFrom(astFuncType *ast.FuncType) (in, out []*model.Parameter, variadic *model.Parameter) {
    59  	in, variadic = g.inParamsFrom(astFuncType.Params)
    60  	out = g.outParamsFrom(astFuncType.Results)
    61  	return
    62  }
    63  
    64  func (g *modelGenerator) inParamsFrom(params *ast.FieldList) (in []*model.Parameter, variadic *model.Parameter) {
    65  	for _, param := range params.List {
    66  		for _, name := range param.Names {
    67  			if ellipsisType, isEllipsisType := param.Type.(*ast.Ellipsis); isEllipsisType {
    68  				variadic = g.newParam(name.Name, ellipsisType.Elt)
    69  			} else {
    70  				in = append(in, g.newParam(name.Name, param.Type))
    71  			}
    72  		}
    73  		if len(param.Names) == 0 {
    74  			if ellipsisType, isEllipsisType := param.Type.(*ast.Ellipsis); isEllipsisType {
    75  				variadic = g.newParam("", ellipsisType.Elt)
    76  			} else {
    77  				in = append(in, g.newParam("", param.Type))
    78  			}
    79  		}
    80  	}
    81  	return
    82  }
    83  
    84  func (g *modelGenerator) outParamsFrom(results *ast.FieldList) (out []*model.Parameter) {
    85  	if results != nil {
    86  		for _, param := range results.List {
    87  			for _, name := range param.Names {
    88  				out = append(out, g.newParam(name.Name, param.Type))
    89  			}
    90  			if len(param.Names) == 0 {
    91  				out = append(out, g.newParam("", param.Type))
    92  			}
    93  		}
    94  	}
    95  	return
    96  }
    97  
    98  func (g *modelGenerator) newParam(name string, typ ast.Expr) *model.Parameter {
    99  	return &model.Parameter{
   100  		Name: name,
   101  		Type: g.modelTypeFrom(g.info.TypeOf(typ)),
   102  	}
   103  }
   104  
   105  func (g *modelGenerator) modelTypeFrom(typesType types.Type) model.Type {
   106  	switch typedTyp := typesType.(type) {
   107  	case *types.Basic:
   108  		if !predeclared(typedTyp.Kind()) {
   109  			panic(fmt.Sprintf("Unexpected Basic Type %v", typedTyp.Name()))
   110  		}
   111  		return model.PredeclaredType(typedTyp.Name())
   112  	case *types.Pointer:
   113  		return &model.PointerType{
   114  			Type: g.modelTypeFrom(typedTyp.Elem()),
   115  		}
   116  	case *types.Array:
   117  		return &model.ArrayType{
   118  			Len:  int(typedTyp.Len()),
   119  			Type: g.modelTypeFrom(typedTyp.Elem()),
   120  		}
   121  	case *types.Slice:
   122  		return &model.ArrayType{
   123  			Len:  -1,
   124  			Type: g.modelTypeFrom(typedTyp.Elem()),
   125  		}
   126  	case *types.Map:
   127  		return &model.MapType{
   128  			Key:   g.modelTypeFrom(typedTyp.Key()),
   129  			Value: g.modelTypeFrom(typedTyp.Elem()),
   130  		}
   131  	case *types.Chan:
   132  		return &model.ChanType{
   133  			Dir:  model.ChanDir(typedTyp.Dir()),
   134  			Type: g.modelTypeFrom(typedTyp.Elem()),
   135  		}
   136  	case *types.Named:
   137  		if typedTyp.Obj().Pkg() == nil {
   138  			return model.PredeclaredType(typedTyp.Obj().Name())
   139  		}
   140  		return &model.NamedType{
   141  			Package: typedTyp.Obj().Pkg().Path(),
   142  			Type:    typedTyp.Obj().Name(),
   143  		}
   144  	case *types.Interface:
   145  		return model.PredeclaredType(typedTyp.String())
   146  	case *types.Signature:
   147  		in, variadic := g.generateInParamsFrom(typedTyp.Params())
   148  		out := g.generateOutParamsFrom(typedTyp.Results())
   149  		return &model.FuncType{In: in, Out: out, Variadic: variadic}
   150  	default:
   151  		panic(fmt.Sprintf("Unknown types.Type: %v (%T)", typesType, typesType))
   152  	}
   153  }
   154  
   155  func (g *modelGenerator) generateInParamsFrom(params *types.Tuple) (in []*model.Parameter, variadic *model.Parameter) {
   156  	// TODO: variadic
   157  
   158  	for i := 0; i < params.Len(); i++ {
   159  		in = append(in, &model.Parameter{
   160  			Name: params.At(i).Name(),
   161  			Type: g.modelTypeFrom(params.At(i).Type()),
   162  		})
   163  	}
   164  	return
   165  }
   166  
   167  func (g *modelGenerator) generateOutParamsFrom(params *types.Tuple) (out []*model.Parameter) {
   168  	for i := 0; i < params.Len(); i++ {
   169  		out = append(out, &model.Parameter{
   170  			Name: params.At(i).Name(),
   171  			Type: g.modelTypeFrom(params.At(i).Type()),
   172  		})
   173  	}
   174  	return
   175  }
   176  
   177  func predeclared(basicKind types.BasicKind) bool {
   178  	return basicKind >= types.Bool && basicKind <= types.String
   179  }