2024-03-18 07:01:47 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"go/ast"
|
|
|
|
"go/constant"
|
|
|
|
"go/importer"
|
|
|
|
"go/parser"
|
|
|
|
"go/token"
|
|
|
|
"go/types"
|
|
|
|
"io/fs"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
if _, err := os.Stat("./go.mod"); errors.Is(err, os.ErrNotExist) {
|
|
|
|
log.Fatalln("go.mod not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := generateYAML("./internal/domain"); err != nil {
|
|
|
|
log.Fatalln(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func generateYAML(root string) error {
|
|
|
|
m, err := getErrorCodesFromFolder(root)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var description strings.Builder
|
|
|
|
enum := make([]string, 0, len(m))
|
|
|
|
|
|
|
|
for c, d := range m {
|
|
|
|
if d != "" {
|
|
|
|
if description.Len() > 0 {
|
|
|
|
description.WriteRune('\n')
|
|
|
|
}
|
|
|
|
description.WriteString("* `")
|
|
|
|
description.WriteString(c)
|
|
|
|
description.WriteString("` - ")
|
|
|
|
description.WriteString(d)
|
|
|
|
}
|
|
|
|
enum = append(enum, c)
|
|
|
|
}
|
|
|
|
|
|
|
|
return yaml.NewEncoder(os.Stdout).Encode(struct {
|
|
|
|
Type string `yaml:"type"`
|
|
|
|
Description string `yaml:"description"`
|
|
|
|
Enum []string `yaml:"enum"`
|
|
|
|
}{
|
|
|
|
Type: "string",
|
|
|
|
Description: description.String(),
|
|
|
|
Enum: enum,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
//nolint:gocyclo
|
|
|
|
func getErrorCodesFromFolder(root string) (map[string]string, error) {
|
|
|
|
fset := token.NewFileSet()
|
|
|
|
|
|
|
|
astDomainPkg, err := parseDomainPackage(fset, root)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
conf := types.Config{Importer: importer.Default()}
|
|
|
|
|
|
|
|
files := make([]*ast.File, 0, len(astDomainPkg.Files))
|
|
|
|
for _, f := range astDomainPkg.Files {
|
|
|
|
files = append(files, f)
|
|
|
|
}
|
|
|
|
|
|
|
|
domainPkg, err := conf.Check(astDomainPkg.Name, fset, files, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
scope := domainPkg.Scope()
|
|
|
|
|
|
|
|
m := make(map[string]string)
|
|
|
|
|
|
|
|
for _, n := range scope.Names() {
|
|
|
|
if !strings.HasPrefix(n, "errorCode") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
obj := scope.Lookup(n)
|
|
|
|
|
|
|
|
c, ok := obj.(*types.Const)
|
|
|
|
if !ok || c.Val().Kind() != constant.String {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
f := astDomainPkg.Files[fset.File(c.Pos()).Name()]
|
|
|
|
position := fset.Position(c.Pos())
|
|
|
|
commentMap := ast.NewCommentMap(fset, f, f.Comments)
|
|
|
|
|
|
|
|
var desc string
|
|
|
|
|
|
|
|
for _, decl := range f.Decls {
|
|
|
|
declPosition := fset.Position(decl.Pos())
|
|
|
|
if declPosition.Filename != position.Filename || declPosition.Line != position.Line {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if comments := commentMap.Filter(decl).Comments(); len(comments) > 0 {
|
|
|
|
desc = strings.TrimSpace(comments[0].Text())
|
|
|
|
}
|
|
|
|
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2024-03-20 05:49:24 +00:00
|
|
|
if strings.Contains(desc, "errorcode:ignore") {
|
2024-03-18 07:01:47 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
m[constant.StringVal(c.Val())] = desc
|
|
|
|
}
|
|
|
|
|
|
|
|
return m, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseDomainPackage(fset *token.FileSet, root string) (*ast.Package, error) {
|
|
|
|
pkgs, err := parser.ParseDir(fset, root, func(info fs.FileInfo) bool {
|
|
|
|
if info.IsDir() && info.Name() != root {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if info.IsDir() || strings.HasSuffix(info.Name(), "_test.go") {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}, parser.AllErrors|parser.ParseComments)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
astDomainPkg, ok := pkgs["domain"]
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("domain pkg not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
return astDomainPkg, nil
|
|
|
|
}
|