Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: create ERRORS.md for x/module #1059

Merged
merged 11 commits into from
Aug 9, 2023
Merged
37 changes: 37 additions & 0 deletions .github/workflows/check-generated.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Verify that generated code is up-to-date.

name: Check generated code
on:
workflow_dispatch:
pull_request:
branches:
- '*'

permissions:
contents: read

jobs:
check-error-doc:
runs-on: ubuntu-latest
steps:
- name: Setup Golang
uses: actions/setup-go@v4
with:
go-version: '1.20'

- uses: actions/checkout@v3
with:
fetch-depth: 1

- name: Check generated error docs
run: |
cd ./tools/error_doc
go run ./
170210 marked this conversation as resolved.
Show resolved Hide resolved
if ! git diff --stat --exit-code ; then
echo ">> ERROR:"
echo ">>"
echo ">> Error documents require update (source files in x folder may have changed)."
echo ">> Ensure your tools are up-to-date, re-run 'go run ./' in tools/error_doc and update this PR."
echo ">>"
exit 1
fi
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,4 @@ Ref: https://keepachangelog.com/en/1.0.0/

### Document Updates
* (readme) [\#997](https://github.com/finschia/finschia-sdk/pull/997) fix swagger url
* (x/ERRORS.md) [\#1059](https://github.com/Finschia/finschia-sdk/pull/1059) create ERRORS.md for x/module
170210 marked this conversation as resolved.
Show resolved Hide resolved
17 changes: 17 additions & 0 deletions tools/error_doc/const_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

import (
"errors"
"strings"
)

func getConst(line string) (string, string, error) {
line = strings.Replace(line, "const", "", 1)
parts := strings.Split(line, "=")
if len(parts) == 2 {
i := strings.TrimSpace(parts[0])
val := strings.Trim(strings.TrimSpace(parts[1]), `"`)
return i, val, nil
}
return "", "", errors.New("failed to get the value in: " + line)
}
93 changes: 93 additions & 0 deletions tools/error_doc/error_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package main

import (
"bufio"
"errors"
"fmt"
"os"
"regexp"
"strings"
)

type errorInfo struct {
errorName string
codeSpace string
code string
description string
}

func (err errorInfo) toString(moduleName string) (string, error) {
errorInfoTemplate := "|%s|%s|%s|%s|\n"
if err.codeSpace == "ModuleName" {
if moduleName == "" {
return "", errors.New("failed to find moduleName")
}
return fmt.Sprintf(errorInfoTemplate, err.errorName, moduleName, err.code, err.description), nil
}
return fmt.Sprintf(errorInfoTemplate, err.errorName, err.codeSpace, err.code, err.description), nil
}

func addError(line string, errorDict map[string]string) (errorInfo, error) {
parts := strings.SplitN(line, "=", 2)
errName := strings.TrimSpace(parts[0])
errBody := strings.TrimSpace(parts[1])
// error info is like as sdkerrors.Register(...)
pattern := regexp.MustCompile(`sdkerrors\.Register\((.*)\)`)
match := pattern.FindStringSubmatch(errBody)

if len(match) == 2 {
parts := strings.SplitN(match[1], ",", 3)

if len(parts) == 3 {
codeSpace := strings.TrimSpace(parts[0])
code := strings.TrimSpace(parts[1])
description := strings.Trim(strings.TrimSpace(parts[2]), `"`)

if constValue, found := errorDict[codeSpace]; found {
codeSpace = constValue
}

return errorInfo{
errorName: errName,
codeSpace: codeSpace,
code: code,
description: description,
}, nil
}
return errorInfo{}, errors.New("failed to get error info in: " + line)
}
return errorInfo{}, errors.New("failed to parse error info in: " + line)
}

func getErrors(p string) ([]errorInfo, error) {
var errorDict []errorInfo
constDict := make(map[string]string)

file, err := os.Open(p)
if err != nil {
return nil, err
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
// get const
if strings.Contains(line, "=") {
if !strings.Contains(line, "sdkerrors.Register") {
identifier, value, err := getConst(line)
if err != nil {
return nil, err
}
constDict[identifier] = value
} else {
errInfo, err := addError(line, constDict)
if err != nil {
return nil, err
}
errorDict = append(errorDict, errInfo)
}
}
}
return errorDict, nil
}
144 changes: 144 additions & 0 deletions tools/error_doc/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package main

import (
"errors"
"fmt"
"os"
"path/filepath"
"sort"

"golang.org/x/text/cases"
"golang.org/x/text/language"
)

func findFilesWithName(startPath, fileName string) ([]string, error) {
var foundFiles []string

err := filepath.Walk(startPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && info.Name() == fileName {
foundFiles = append(foundFiles, path)
}
return nil
})
if err != nil {
return nil, err
}

return foundFiles, nil
}

func findModuleWithFiles(targetPath string) (map[string][]string, []string, error) {

// get all errors.go in x folder
errorFile := "errors.go"
filePaths, err := findFilesWithName(targetPath, errorFile)
if len(filePaths) == 0 || err != nil {
return nil, nil, errors.New("not find target files in x folder")
}

// get each module name and bind it to paths (one module may have multiple errors.go)
moduleWithPaths := make(map[string][]string)
170210 marked this conversation as resolved.
Show resolved Hide resolved
for _, filePath := range filePaths {
moduleName := findModuleName(filePath)
if moduleName == "" {
return nil, nil, errors.New("failed to get module name for " + filePath)
}
moduleWithPaths[moduleName] = append(moduleWithPaths[moduleName], filePath)
}

// sort keys and filepaths
n := len(moduleWithPaths)
modules := make([]string, 0, n)

for moduleName := range moduleWithPaths {
modules = append(modules, moduleName)
sort.Strings(moduleWithPaths[moduleName])
}
sort.Strings(modules)

return moduleWithPaths, modules, nil
}

func autoGenerate(targetPath string, moduleWithPaths map[string][]string, modules []string) error {
filePath := targetPath + "/ERRORS.md"
file, err := os.Create(filePath)
if err != nil {
return err
}
defer file.Close()

// generate category
file.WriteString("<!-- TOC -->\n")
file.WriteString("# Category\n")
columnTemplate := " * [%s](#%s)\n"
for _, moduleName := range modules {
file.WriteString(fmt.Sprintf(columnTemplate, cases.Title(language.Und).String(moduleName), moduleName))
}
file.WriteString("<!-- TOC -->\n")

extraInfoTemplate := " * [%s](%s)\n"
// errors in each module
for _, moduleName := range modules {

// table header
file.WriteString("\n")
file.WriteString("## " + cases.Title(language.Und).String(moduleName) + "\n")
file.WriteString("\n")
file.WriteString("|Error Name|Codespace|Code|Description|\n")
file.WriteString("|:-|:-|:-|:-|\n")

filePaths := moduleWithPaths[moduleName]
for _, filePath := range filePaths {
errDict, err := getErrors(filePath)
if err != nil {
return err
}
moduleName, err := getModuleNameValue(filePath)
if err != nil {
return err
}
for _, errInfo := range errDict {
column, err := errInfo.toString(moduleName)
if err != nil {
return err
}
file.WriteString(column)
}
}

file.WriteString("\n>You can also find detailed information in the following Errors.go files:\n")
for _, filePath := range filePaths {
relPath, err := filepath.Rel(targetPath, filePath)
if err != nil {
return err
}
file.WriteString(fmt.Sprintf(extraInfoTemplate, relPath, relPath))
}

}
return nil
}

func main() {
currentPath, err := os.Getwd()
if err != nil {
fmt.Println("Error getting current directory:", err)
os.Exit(1)
}
targetPath := filepath.Join(currentPath, "..", "..", "x")

moduleWithPaths, modules, err := findModuleWithFiles(targetPath)
if err != nil {
fmt.Println(err)
os.Exit(1)
}

if err := autoGenerate(targetPath, moduleWithPaths, modules); err != nil {
fmt.Println(err)
os.Exit(1)
}

}
52 changes: 52 additions & 0 deletions tools/error_doc/module_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

import (
"bufio"
"errors"
"os"
"strings"
)

func findModuleName(s string) string {
startIndex := strings.Index(s, "/x/") + len("/x/")
endIndex := strings.Index(s[startIndex:], "/")

if startIndex != -1 && endIndex != -1 {
return s[startIndex : startIndex+endIndex]
}
return ""
}

func getModuleNameValue(filePath string) (string, error) {
possibleFileNames := []string{"keys.go", "key.go"}
var keyFilePath string
for _, fileName := range possibleFileNames {
paramPath := strings.Replace(filePath, "errors.go", fileName, 1)
if _, err := os.Stat(paramPath); err == nil {
keyFilePath = paramPath
break
}
}

if keyFilePath != "" {
file, err := os.Open(keyFilePath)
if err != nil {
return "", errors.New(keyFilePath + " cannot be opened")
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
// get module name
if strings.Contains(line, "ModuleName = ") {
_, val, err := getConst(line)
if err != nil {
return "", err
}
return val, nil
}
}
}

return "", nil
}
Loading
Loading