github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/statusxgen/scanner.go (about) 1 package statusxgen 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/types" 7 "sort" 8 "strconv" 9 "strings" 10 11 "github.com/machinefi/w3bstream/pkg/depends/kit/statusx" 12 "github.com/machinefi/w3bstream/pkg/depends/x/pkgx" 13 "github.com/machinefi/w3bstream/pkg/depends/x/typesx" 14 ) 15 16 func NewScanner(pkg *pkgx.Pkg) *Scanner { 17 return &Scanner{ 18 pkg: pkg, 19 } 20 } 21 22 type Scanner struct { 23 pkg *pkgx.Pkg 24 StatusErrors map[*types.TypeName][]*statusx.StatusErr 25 } 26 27 func sortedStatusErrList(list []*statusx.StatusErr) []*statusx.StatusErr { 28 sort.Slice(list, func(i, j int) bool { 29 return list[i].Code < list[j].Code 30 }) 31 return list 32 } 33 34 func (s *Scanner) StatusError(tn *types.TypeName) []*statusx.StatusErr { 35 if tn == nil { 36 return nil 37 } 38 39 if es, ok := s.StatusErrors[tn]; ok { 40 sort.Slice(es, func(i, j int) bool { 41 return es[i].Code < es[j].Code 42 }) 43 return es 44 } 45 46 if !strings.Contains(tn.Type().Underlying().String(), "int") { 47 panic(fmt.Errorf("status error type underlying must be an int or uint, but got %s", tn.String())) 48 } 49 50 pkg := s.pkg.PkgByPath(tn.Pkg().Path()) 51 if pkg == nil { 52 return nil 53 } 54 55 serviceCode := 0 56 57 method, ok := typesx.FromGoType(tn.Type()).MethodByName("ServiceCode") 58 if ok { 59 results, n := s.pkg.FuncResultsOf(method.(*typesx.GoMethod).Func) 60 if n == 1 { 61 ret := results[0][0] 62 if ret.IsValue() { 63 if i, err := strconv.ParseInt(ret.Value.String(), 10, 64); err == nil { 64 serviceCode = int(i) 65 } 66 } 67 } 68 } 69 70 for ident, def := range pkg.TypesInfo.Defs { 71 typeConst, ok := def.(*types.Const) 72 if !ok { 73 continue 74 } 75 if typeConst.Type() != tn.Type() { 76 continue 77 } 78 79 key := typeConst.Name() 80 code, _ := strconv.ParseInt(typeConst.Val().String(), 10, 64) 81 82 msg, canBeTalkError := ParseStatusErrMsg(ident.Obj.Decl.(*ast.ValueSpec).Doc.Text()) 83 84 s.add(tn, key, msg, int(code)+serviceCode, canBeTalkError) 85 } 86 87 lst := s.StatusErrors[tn] 88 sort.Slice(lst, func(i, j int) bool { 89 return lst[i].Code < lst[j].Code 90 }) 91 return lst 92 } 93 94 func ParseStatusErrMsg(s string) (string, bool) { 95 firstLine := strings.Split(strings.TrimSpace(s), "\n")[0] 96 97 prefix := "@errTalk " 98 if strings.HasPrefix(firstLine, prefix) { 99 return firstLine[len(prefix):], true 100 } 101 return firstLine, false 102 } 103 104 func (s *Scanner) add(tn *types.TypeName, key, msg string, code int, canBeTalk bool) { 105 if s.StatusErrors == nil { 106 s.StatusErrors = map[*types.TypeName][]*statusx.StatusErr{} 107 } 108 109 se := statusx.NewStatusErr(key, code, msg) 110 if canBeTalk { 111 se = se.EnableErrTalk() 112 } 113 s.StatusErrors[tn] = append(s.StatusErrors[tn], se) 114 }