mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 11:48:43 -07:00
Previously we had a few confusing functions like NewT and similar that were quite fragile to use due to minimal validation and a panic in go stdlib string Replacer. Now we generate helper methods for every string, these are used to access errors, fill in templates and conditional returns of error type using the new Unless() option We now get compile time errors for some common mistakes and have better IDE helpers for arguments etc Signed-off-by: R.I.Pienaar <rip@devco.net>
216 lines
4.8 KiB
Go
216 lines
4.8 KiB
Go
// +build ignore
|
|
|
|
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"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 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))
|
|
|
|
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 := ioutil.TempFile("", "")
|
|
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"))
|
|
}
|