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 }