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  }