github.com/johnnyeven/libtools@v0.0.0-20191126065708-61829c1adf46/courier/swagger/gen/status_error_scanner.go (about) 1 package gen 2 3 import ( 4 "go/ast" 5 "go/types" 6 "reflect" 7 "strconv" 8 "strings" 9 10 "golang.org/x/tools/go/loader" 11 12 "github.com/johnnyeven/libtools/codegen/loaderx" 13 "github.com/johnnyeven/libtools/courier/status_error" 14 "github.com/johnnyeven/libtools/courier/status_error/gen" 15 ) 16 17 var ( 18 statusErrorTypeString = reflectTypeString(reflect.TypeOf(new(status_error.StatusError))) 19 statusErrorCodeTypeString = reflectTypeString(reflect.TypeOf(new(status_error.StatusErrorCode))) 20 ) 21 22 func NewStatusErrorScanner(program *loader.Program) *StatusErrorScanner { 23 statusErrorScanner := &StatusErrorScanner{ 24 program: program, 25 statusErrors: map[*types.Const]status_error.StatusError{}, 26 errorsUsed: map[*types.Func]status_error.StatusErrorCodeMap{}, 27 } 28 29 statusErrorScanner.init() 30 31 return statusErrorScanner 32 } 33 34 type StatusErrorScanner struct { 35 program *loader.Program 36 statusErrors map[*types.Const]status_error.StatusError 37 errorsUsed map[*types.Func]status_error.StatusErrorCodeMap 38 } 39 40 func (scanner *StatusErrorScanner) StatusErrorsInFunc(typeFunc *types.Func) status_error.StatusErrorCodeMap { 41 if typeFunc == nil { 42 return nil 43 } 44 45 if statusErrorCodeMap, ok := scanner.errorsUsed[typeFunc]; ok { 46 return statusErrorCodeMap 47 } 48 49 // force initial to fix recursion 50 scanner.errorsUsed[typeFunc] = status_error.StatusErrorCodeMap{} 51 52 pkgInfo := scanner.program.Package(typeFunc.Pkg().Path()) 53 funcDecl := loaderx.FuncDeclOfTypeFunc(pkgInfo, typeFunc) 54 55 if funcDecl != nil { 56 ast.Inspect(funcDecl, func(node ast.Node) bool { 57 switch node.(type) { 58 case *ast.CallExpr: 59 identList := loaderx.GetIdentChainCallOfCallFun(node.(*ast.CallExpr).Fun) 60 if len(identList) > 0 { 61 callIdent := identList[len(identList)-1] 62 obj := pkgInfo.ObjectOf(callIdent) 63 64 if nextTypeFunc, ok := obj.(*types.Func); ok && nextTypeFunc != typeFunc && nextTypeFunc.Pkg() != nil { 65 statusErrorCodeMap := scanner.StatusErrorsInFunc(nextTypeFunc) 66 scanner.mayMergeStateError(typeFunc, statusErrorCodeMap) 67 } 68 } 69 case *ast.Ident: 70 scanner.mayAddStateErrorByObject(typeFunc, pkgInfo.ObjectOf(node.(*ast.Ident))) 71 } 72 return true 73 }) 74 75 doc := loaderx.StringifyCommentGroup(funcDecl.Doc) 76 scanner.mayMergeStateError(typeFunc, pickStatusErrorsFromDoc(doc)) 77 } 78 79 return scanner.errorsUsed[typeFunc] 80 } 81 82 func (scanner *StatusErrorScanner) mayAddStateErrorByObject(typeFunc *types.Func, obj types.Object) { 83 if obj == nil { 84 return 85 } 86 if typeConst, ok := obj.(*types.Const); ok { 87 scanner.mayAddStateError(typeFunc, typeConst) 88 } 89 } 90 91 func (scanner *StatusErrorScanner) mayMergeStateError(typeFunc *types.Func, statusErrorCodeMap status_error.StatusErrorCodeMap) { 92 if scanner.errorsUsed[typeFunc] == nil { 93 scanner.errorsUsed[typeFunc] = status_error.StatusErrorCodeMap{} 94 } 95 scanner.errorsUsed[typeFunc].Merge(statusErrorCodeMap) 96 } 97 98 func (scanner *StatusErrorScanner) mayAddStateError(typeFunc *types.Func, typeConst *types.Const) { 99 if statusError, ok := scanner.statusErrors[typeConst]; ok { 100 if scanner.errorsUsed[typeFunc] == nil { 101 scanner.errorsUsed[typeFunc] = status_error.StatusErrorCodeMap{} 102 } 103 scanner.errorsUsed[typeFunc][int64(statusError.Code)] = statusError 104 } 105 } 106 107 func (scanner *StatusErrorScanner) init() { 108 for _, pkgInfo := range scanner.program.AllPackages { 109 for ident, obj := range pkgInfo.Defs { 110 if constObj, ok := obj.(*types.Const); ok { 111 if constObj.Type().String() == statusErrorCodeTypeString { 112 key := constObj.Name() 113 if key == "_" { 114 continue 115 } 116 117 doc := loaderx.CommentsOf(scanner.program.Fset, ident, pkgInfo.Files...) 118 code, _ := strconv.ParseInt(constObj.Val().String(), 10, 64) 119 msg, desc, canBeErrTalk := gen.ParseStatusErrorDesc(doc) 120 121 scanner.statusErrors[constObj] = status_error.StatusError{ 122 Key: key, 123 Code: code, 124 Msg: msg, 125 Desc: desc, 126 CanBeErrorTalk: canBeErrTalk, 127 } 128 } 129 } 130 } 131 } 132 } 133 134 func pickStatusErrorsFromDoc(doc string) status_error.StatusErrorCodeMap { 135 statusErrorCodeMap := status_error.StatusErrorCodeMap{} 136 137 lines := strings.Split(doc, "\n") 138 139 for _, line := range lines { 140 if line != "" { 141 if statusErr := status_error.ParseString(line); statusErr != nil { 142 statusErrorCodeMap[statusErr.Code] = *statusErr 143 } 144 } 145 } 146 return statusErrorCodeMap 147 }