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  }