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
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/check-generated.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# 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: |
make error-doc
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 'make 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
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,10 @@ libsodium:
fi
.PHONY: libsodium

error-doc:
170210 marked this conversation as resolved.
Show resolved Hide resolved
cd ./tools/error_doc && go run ./
.PHONY: error-doc
170210 marked this conversation as resolved.
Show resolved Hide resolved

###############################################################################
### release ###
###############################################################################
Expand Down
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
}
5 changes: 5 additions & 0 deletions tools/error_doc/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/Finschia/finschia-sdk/tools/error_doc

go 1.20

require golang.org/x/text v0.11.0
2 changes: 2 additions & 0 deletions tools/error_doc/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
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