Files
nats-server/server/errors_gen.go
2022-08-05 09:55:37 -06:00

240 lines
5.2 KiB
Go

//go:build ignore
// +build ignore
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
"regexp"
"sort"
"strings"
"text/template"
"github.com/nats-io/nats-server/v2/server"
)
var tagRe = regexp.MustCompile("\\{(.+?)}")
var templ = `
// Generated code, do not edit. See errors.json and run go generate to update
package server
import "strings"
const (
{{- range $i, $error := . }}
{{- if .Comment }}
// {{ .Constant }} {{ .Comment }} ({{ .Description | print }})
{{- else }}
// {{ .Constant }} {{ .Description | print }}
{{- end }}
{{ .Constant }} ErrorIdentifier = {{ .ErrCode }}
{{ end }}
)
var (
ApiErrors = map[ErrorIdentifier]*ApiError{
{{- range $i, $error := . }}
{{ .Constant }}: {Code: {{ .Code }},ErrCode: {{ .ErrCode }},Description: {{ .Description | printf "%q" }}},{{- end }}
}
{{- range $i, $error := . }}
{{- if .Deprecates }}
// {{ .Deprecates }} Deprecated by {{ .Constant }} ApiError, use IsNatsError() for comparisons
{{ .Deprecates }} = ApiErrors[{{ .Constant }}]
{{- end }}
{{- end }}
)
{{- range $i, $error := . }}
// {{ .Constant | funcNameForConstant }} creates a new {{ .Constant }} error: {{ .Description | printf "%q" }}
func {{ .Constant | funcNameForConstant }}({{ .Description | funcArgsForTags }}) *ApiError {
eopts := parseOpts(opts)
if ae, ok := eopts.err.(*ApiError); ok {
return ae
}
{{ if .Description | hasTags }}
e:=ApiErrors[{{.Constant}}]
args:=e.toReplacerArgs([]interface{}{ {{.Description | replacerArgsForTags }} })
return &ApiError{
Code: e.Code,
ErrCode: e.ErrCode,
Description: strings.NewReplacer(args...).Replace(e.Description),
}
{{- else }}
return ApiErrors[{{.Constant}}]
{{- end }}
}
{{- end }}
`
func panicIfErr(err error) {
if err == nil {
return
}
panic(err)
}
func goFmt(file string) error {
c := exec.Command("go", "fmt", file)
out, err := c.CombinedOutput()
if err != nil {
log.Printf("go fmt failed: %s", string(out))
}
return err
}
func checkIncrements(errs []server.ErrorsData) error {
sort.Slice(errs, func(i, j int) bool {
return errs[i].ErrCode < errs[j].ErrCode
})
last := errs[0].ErrCode
gaps := []uint16{}
for i := 1; i < len(errs); i++ {
if errs[i].ErrCode != last+1 {
gaps = append(gaps, last)
}
last = errs[i].ErrCode
}
if len(gaps) > 0 {
return fmt.Errorf("gaps found in sequences: %v", gaps)
}
return nil
}
func checkDupes(errs []server.ErrorsData) error {
codes := []uint16{}
highest := uint16(0)
for _, err := range errs {
codes = append(codes, err.ErrCode)
if highest < err.ErrCode {
highest = err.ErrCode
}
}
codeKeys := make(map[uint16]bool)
constKeys := make(map[string]bool)
for _, entry := range errs {
if _, found := codeKeys[entry.ErrCode]; found {
return fmt.Errorf("duplicate error code %+v, highest code is %d", entry, highest)
}
if _, found := constKeys[entry.Constant]; found {
return fmt.Errorf("duplicate error constant %+v", entry)
}
codeKeys[entry.ErrCode] = true
constKeys[entry.Constant] = true
}
return nil
}
func findTags(d string) []string {
tags := []string{}
for _, tag := range tagRe.FindAllStringSubmatch(d, -1) {
if len(tag) != 2 {
continue
}
tags = append(tags, tag[1])
}
sort.Strings(tags)
return tags
}
func main() {
ej, err := os.ReadFile("server/errors.json")
panicIfErr(err)
errs := []server.ErrorsData{}
panicIfErr(json.Unmarshal(ej, &errs))
panicIfErr(checkDupes(errs))
panicIfErr(checkIncrements(errs))
sort.Slice(errs, func(i, j int) bool {
return errs[i].Constant < errs[j].Constant
})
t := template.New("errors").Funcs(
template.FuncMap{
"inc": func(i int) int { return i + 1 },
"hasTags": func(d string) bool {
return strings.Contains(d, "{") && strings.Contains(d, "}")
},
"replacerArgsForTags": func(d string) string {
res := []string{}
for _, tag := range findTags(d) {
res = append(res, `"{`+tag+`}"`)
res = append(res, tag)
}
return strings.Join(res, ", ")
},
"funcArgsForTags": func(d string) string {
res := []string{}
for _, tag := range findTags(d) {
if tag == "err" {
res = append(res, "err error")
} else if tag == "seq" {
res = append(res, "seq uint64")
} else {
res = append(res, fmt.Sprintf("%s interface{}", tag))
}
}
res = append(res, "opts ...ErrorOption")
return strings.Join(res, ", ")
},
"funcNameForConstant": func(c string) string {
res := ""
switch {
case strings.HasSuffix(c, "ErrF"):
res = fmt.Sprintf("New%sError", strings.TrimSuffix(c, "ErrF"))
case strings.HasSuffix(c, "Err"):
res = fmt.Sprintf("New%sError", strings.TrimSuffix(c, "Err"))
case strings.HasSuffix(c, "ErrorF"):
res = fmt.Sprintf("New%s", strings.TrimSuffix(c, "F"))
case strings.HasSuffix(c, "F"):
res = fmt.Sprintf("New%sError", strings.TrimSuffix(c, "F"))
default:
res = fmt.Sprintf("New%s", c)
}
if !strings.HasSuffix(res, "Error") {
res = fmt.Sprintf("%sError", res)
}
return res
},
})
p, err := t.Parse(templ)
panicIfErr(err)
tf, err := os.CreateTemp("", "")
panicIfErr(err)
defer tf.Close()
panicIfErr(p.Execute(tf, errs))
panicIfErr(os.Rename(tf.Name(), "server/jetstream_errors_generated.go"))
panicIfErr(goFmt("server/jetstream_errors_generated.go"))
}