get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/server/errors_gen.go (about) 1 //go:build ignore 2 // +build ignore 3 4 package main 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "log" 10 "os" 11 "os/exec" 12 "regexp" 13 "sort" 14 "strings" 15 "text/template" 16 17 "get.pme.sh/pnats/server" 18 ) 19 20 var tagRe = regexp.MustCompile("\\{(.+?)}") 21 22 var templ = ` 23 // Generated code, do not edit. See errors.json and run go generate to update 24 25 package server 26 27 import "strings" 28 29 const ( 30 {{- range $i, $error := . }} 31 {{- if .Comment }} 32 // {{ .Constant }} {{ .Comment }} ({{ .Description | print }}) 33 {{- else }} 34 // {{ .Constant }} {{ .Description | print }} 35 {{- end }} 36 {{ .Constant }} ErrorIdentifier = {{ .ErrCode }} 37 {{ end }} 38 ) 39 40 var ( 41 ApiErrors = map[ErrorIdentifier]*ApiError{ 42 {{- range $i, $error := . }} 43 {{ .Constant }}: {Code: {{ .Code }},ErrCode: {{ .ErrCode }},Description: {{ .Description | printf "%q" }}},{{- end }} 44 } 45 46 {{- range $i, $error := . }} 47 {{- if .Deprecates }} 48 // {{ .Deprecates }} Deprecated by {{ .Constant }} ApiError, use IsNatsError() for comparisons 49 {{ .Deprecates }} = ApiErrors[{{ .Constant }}] 50 {{- end }} 51 {{- end }} 52 ) 53 54 {{- range $i, $error := . }} 55 // {{ .Constant | funcNameForConstant }} creates a new {{ .Constant }} error: {{ .Description | printf "%q" }} 56 func {{ .Constant | funcNameForConstant }}({{ .Description | funcArgsForTags }}) *ApiError { 57 eopts := parseOpts(opts) 58 if ae, ok := eopts.err.(*ApiError); ok { 59 return ae 60 } 61 {{ if .Description | hasTags }} 62 e:=ApiErrors[{{.Constant}}] 63 args:=e.toReplacerArgs([]interface{}{ {{.Description | replacerArgsForTags }} }) 64 return &ApiError{ 65 Code: e.Code, 66 ErrCode: e.ErrCode, 67 Description: strings.NewReplacer(args...).Replace(e.Description), 68 } 69 {{- else }} 70 return ApiErrors[{{.Constant}}] 71 {{- end }} 72 } 73 74 {{- end }} 75 ` 76 77 func panicIfErr(err error) { 78 if err == nil { 79 return 80 } 81 panic(err) 82 } 83 84 func goFmt(file string) error { 85 c := exec.Command("go", "fmt", file) 86 out, err := c.CombinedOutput() 87 if err != nil { 88 log.Printf("go fmt failed: %s", string(out)) 89 } 90 91 return err 92 } 93 94 func checkIncrements(errs []server.ErrorsData) error { 95 sort.Slice(errs, func(i, j int) bool { 96 return errs[i].ErrCode < errs[j].ErrCode 97 }) 98 99 last := errs[0].ErrCode 100 gaps := []uint16{} 101 102 for i := 1; i < len(errs); i++ { 103 if errs[i].ErrCode != last+1 { 104 gaps = append(gaps, last) 105 } 106 last = errs[i].ErrCode 107 } 108 109 if len(gaps) > 0 { 110 return fmt.Errorf("gaps found in sequences: %v", gaps) 111 } 112 113 return nil 114 } 115 116 func checkDupes(errs []server.ErrorsData) error { 117 codes := []uint16{} 118 highest := uint16(0) 119 for _, err := range errs { 120 codes = append(codes, err.ErrCode) 121 if highest < err.ErrCode { 122 highest = err.ErrCode 123 } 124 } 125 126 codeKeys := make(map[uint16]bool) 127 constKeys := make(map[string]bool) 128 129 for _, entry := range errs { 130 if _, found := codeKeys[entry.ErrCode]; found { 131 return fmt.Errorf("duplicate error code %+v, highest code is %d", entry, highest) 132 } 133 134 if _, found := constKeys[entry.Constant]; found { 135 return fmt.Errorf("duplicate error constant %+v", entry) 136 } 137 138 codeKeys[entry.ErrCode] = true 139 constKeys[entry.Constant] = true 140 } 141 142 return nil 143 } 144 145 func findTags(d string) []string { 146 tags := []string{} 147 for _, tag := range tagRe.FindAllStringSubmatch(d, -1) { 148 if len(tag) != 2 { 149 continue 150 } 151 152 tags = append(tags, tag[1]) 153 } 154 155 sort.Strings(tags) 156 157 return tags 158 } 159 160 func main() { 161 ej, err := os.ReadFile("server/errors.json") 162 panicIfErr(err) 163 164 errs := []server.ErrorsData{} 165 panicIfErr(json.Unmarshal(ej, &errs)) 166 167 panicIfErr(checkDupes(errs)) 168 panicIfErr(checkIncrements(errs)) 169 170 sort.Slice(errs, func(i, j int) bool { 171 return errs[i].Constant < errs[j].Constant 172 }) 173 174 t := template.New("errors").Funcs( 175 template.FuncMap{ 176 "inc": func(i int) int { return i + 1 }, 177 "hasTags": func(d string) bool { 178 return strings.Contains(d, "{") && strings.Contains(d, "}") 179 }, 180 "replacerArgsForTags": func(d string) string { 181 res := []string{} 182 for _, tag := range findTags(d) { 183 res = append(res, `"{`+tag+`}"`) 184 res = append(res, tag) 185 } 186 187 return strings.Join(res, ", ") 188 }, 189 "funcArgsForTags": func(d string) string { 190 res := []string{} 191 for _, tag := range findTags(d) { 192 if tag == "err" { 193 res = append(res, "err error") 194 } else if tag == "seq" { 195 res = append(res, "seq uint64") 196 } else { 197 res = append(res, fmt.Sprintf("%s interface{}", tag)) 198 } 199 } 200 201 res = append(res, "opts ...ErrorOption") 202 203 return strings.Join(res, ", ") 204 }, 205 "funcNameForConstant": func(c string) string { 206 res := "" 207 208 switch { 209 case strings.HasSuffix(c, "ErrF"): 210 res = fmt.Sprintf("New%sError", strings.TrimSuffix(c, "ErrF")) 211 case strings.HasSuffix(c, "Err"): 212 res = fmt.Sprintf("New%sError", strings.TrimSuffix(c, "Err")) 213 case strings.HasSuffix(c, "ErrorF"): 214 res = fmt.Sprintf("New%s", strings.TrimSuffix(c, "F")) 215 case strings.HasSuffix(c, "F"): 216 res = fmt.Sprintf("New%sError", strings.TrimSuffix(c, "F")) 217 default: 218 res = fmt.Sprintf("New%s", c) 219 } 220 221 if !strings.HasSuffix(res, "Error") { 222 res = fmt.Sprintf("%sError", res) 223 } 224 225 return res 226 }, 227 }) 228 p, err := t.Parse(templ) 229 panicIfErr(err) 230 231 tf, err := os.CreateTemp("", "") 232 panicIfErr(err) 233 defer tf.Close() 234 235 panicIfErr(p.Execute(tf, errs)) 236 237 panicIfErr(os.Rename(tf.Name(), "server/jetstream_errors_generated.go")) 238 panicIfErr(goFmt("server/jetstream_errors_generated.go")) 239 }